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#
- Create Collections worklist with one row per open invoice. Use a reconciled outstanding balance rather than the original invoice total.
- Load the fixture in Seed, add Script → Python, and use the fixed reporting date below for the first preview.
- Filter
collection_action != none. Keepdispute_reviewrows in a separate branch or view fromcontact_customerrows. - Sort customer-contact rows by
balancedescending, thendays_overduedescending, and write tocollections_review. - 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.
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?