WhoDB PlatformDocs

Prioritize overdue receivables for collections

Give finance a ranked collections list with overdue balance and aging bucket. Keep disputed items visible for a separate resolution path instead of automatically chasing every overdue balance.

Before you begin#

You need a project where you can create transforms and datasets, plus an available Python script runtime. The sample contains only synthetic records. Start with the complete CSV, not a limited preview of a larger business dataset.

Follow Run a use-case recipe for the shared input, destination, and app steps.

Sample input#

Download the CSV, or copy it into Transforms → Library → Seed → Paste CSV.

Csv

invoice_id,customer,due_date,balance,disputed
AR-1,Acme Retail,2026-11-01,1200.00,false
AR-2,Cedar Studio,2026-09-01,300.00,true
AR-3,Northwind Lab,2026-11-20,500.00,false
AR-4,Orchid Works,2026-08-01,0.00,false

Build it in WhoDB#

  1. Create Collections worklist with one row per open invoice. Use a reconciled outstanding balance rather than the original invoice total.
  2. Load the fixture in Seed, add Script → Python, and use the fixed reporting date below for the first preview.
  3. Filter collection_action != none. Keep dispute_review rows in a separate branch or view from contact_customer rows.
  4. Sort customer-contact rows by balance descending, then days_overdue descending, and write to collections_review.
  5. Present owner, invoice, customer, balance, aging bucket, and next action in an app. Record contact outcomes separately; this recipe does not send collection messages.

Script and output columns#

In Script → Output columns, add these names exactly, in this order:

Text

invoice_id, customer, due_date, balance, disputed, days_overdue, aging_bucket, collection_action

Paste this code into Script. Select Python and leave Dependencies empty; it uses the Python standard library.

Python

from datetime import date
from decimal import Decimal

AS_OF = date(2026, 11, 10)

def main(input):
    output = []
    for row in input:
        days = max(0, (AS_OF - date.fromisoformat(row["due_date"])).days)
        balance = Decimal(str(row["balance"]))
        bucket = "current" if days == 0 else "1-30" if days <= 30 else "31-60" if days <= 60 else "61-90" if days <= 90 else "90+"
        disputed = str(row["disputed"]).lower() == "true"
        action = "none" if balance <= 0 or days == 0 else "dispute_review" if disputed else "contact_customer"
        output.append(dict(row, days_overdue=days, aging_bucket=bucket, collection_action=action))
    return output

Download the script · Expected fixture output. The expected output is computed from this CSV and script; it is not a screenshot of a hosted run.

Check the result#

As of 2026-11-10, AR-1 is nine days overdue in 1-30, action contact_customer. AR-2 is seventy days overdue in 61-90, action dispute_review. AR-3 is not due, and AR-4 has no balance; both have action none.

Find the controls#

These screenshots show the current pipeline configuration controls used in this recipe. They come from the manual’s synthetic demonstration workspace; they do not show this use case already executed.

Script: select Python, declare output columns, and paste the calculation.Script: select Python, declare output columns, and paste the calculation.

Script: select Python, declare output columns, and paste the calculation.Script: select Python, declare output columns, and paste the calculation.

Put it to work#

Define treatment for credits, payment plans, write-offs, local collection rules, and currency before automating outreach. Use a named finance owner and a last-contact field to avoid repeated contacts. Refresh allocations first so a newly paid invoice does not remain in the worklist.

For output mapping and rerun behavior, follow Write pipeline outputs. For a team-facing screen, follow Create an app and Connect state and data.

Was this page helpful?