Skip to content

Debtors

The Debtors module is Lubb ERP's accounts-receivable and collections workspace: it turns the general ledger into a live ageing report, lets staff send customers their Statement of Account by email/SMS, and tracks promise-to-pay dates on a Commitment Calendar so collections follow-up doesn't get lost. It's used day to day by accounts/collections staff working the Debtors Aging report and Customers grid — admin and sales roles both get the debtors app in the site's role/app-access catalog — while the Commitment Calendar's own API (/qualifier/commitments*) is separately gated behind the debt_collector role, so debt-collection staff and voice-AI collection campaigns can book and manage promises-to-pay without needing the rest of the accounting app.

What you can do

  • View the Debtors Aging report — one row per customer with total outstanding, a 0-10/10-20/20-30/31-60/61-90/90+ day bucket breakdown, days overdue, oldest due date, open charge count, payment terms, and an overall ageing bucket badge (blue/amber/red).
  • Filter the ageing report by bucket and drill into a customer's detail from the row.
  • See stat cards above the ageing report: a customer count per bucket (click to filter), a total customer count, and a grand total outstanding across every customer — the cards honour the Company filter but not the Bucket filter, since they are the bucket breakdown.
  • View a customer's Statement of Account — a public, token-secured web page (no login) showing every GL movement against their receivable account with a running balance and ageing summary, opened from either the Customers grid or the Aging report.
  • Preview and bulk-send Statement of Account emails or SMS texts to every ageing customer at once ("Test Statement"/"Send Statements" and "Test SMS"/"Send SMS" toolbar actions), or email/SMS one customer at a time.
  • Schedule a Commitment Date (promise-to-pay) against a customer, from either the Aging report or the Customers grid — opens a modal, pre-filled with the customer's outstanding balance, and posts to the Commitment Calendar.
  • See and manage a customer's open commitments on the Commitment Calendar (list/create/update/delete promise-to-pay entries with a date, amount and notes).
  • Bulk-disable customers by ageing bucket ("Disable Customers") — suspends real PBX extensions and hosting/FTP accounts for every customer at or worse than a picked bucket, skipping (and reporting) anyone with an open Commitment Date on file.
  • Disable or re-enable a single customer's services directly from the Aging report row.
  • View or email a Letter of Confirmation — a simple account-holder/account-number/contact confirmation letter, rendered inline without needing a public token.
  • View a customer's linked invoices, payments and subscriptions in one click (each opens the relevant grid pre-scoped to that customer), and click-to-call or send a free-form email to them.
  • See "Debtors (owe money)" as a stat card on the Customers grid — customers with a positive receivable balance, clicking it filters the grid down to just them — alongside a computed, always-current Balance column per customer.
  • Log into the customer's own service portal as them (support/preview, superuser-only) directly from their Customers row.

Common tasks

  1. Review the ageing report. Open the Debtors Aging grid. Each row is a customer with a positive receivable balance, sorted by days overdue (worst first); a customer's headline bucket is their single worst-aged charge, even if some of their balance is current. Use the Bucket filter to narrow to a specific ageing band, or toggle on the hidden per-bucket currency columns to see how an outstanding total splits across bands rather than just its worst bucket. The same 0-10 / 10-20 / 20-30 / 31-60 / 61-90 / 90+ day buckets back the bulk "Send Statements", "Send SMS" and "Disable Customers" actions below — picking a bucket there means "this bucket and every worse one," not an exact-match slice.
  2. Send a customer's statement. From a row, click "View Statement" to open the public web-view link, or "Email Statement"/"Send SMS" to text/email that link. A customer more than 10 days overdue with no open Commitment Date is locked from receiving statements ("Statement Locked") — book a Commitment Date first, or escalate to Disable Customer.
  3. Bulk-send statements to everyone overdue. Use the toolbar's "Test Statement" (or "Test SMS") to preview the email/SMS against a reference customer first, then "Send Statements" ("Send SMS") to open the bulk-send modal, which lists every ageing customer and iterates the same per-customer send, skipping locked accounts.
  4. Book a promise-to-pay (Commitment Date). From a row on the Aging report or the Customers grid, click "Commitment Date"/"Create Commitment". The modal pre-fills the customer's name and outstanding balance; submitting creates an entry on the Commitment Calendar and the customer's commitment_date column updates immediately. Duplicate commitments (same customer, same day) are rejected with a warning rather than silently doubled.
  5. Work the Commitment Calendar. List commitments for a date range (GET /qualifier/commitments?frm=...&to=...), reschedule or cancel one as it's kept or broken. Commitments also get created automatically from debt-collection campaign calls (voice AI/qualifier flows), so the calendar mixes agent-booked and manually-booked promises.
  6. Escalate an overdue bucket. Use "Disable Customers" on the toolbar to bulk-suspend every customer at or worse than a chosen bucket — the modal lists affected customers up front (splitting out any with an open Commitment Date, which are held back), then disables PBX extensions and hosting/FTP accounts per customer (the only two service types with a real provider path today; anything else on an affected customer is reported, not silently skipped). Use "Enable Customer" on a suspended row to undo, or override the Commitment Date hold with a forced disable when genuinely necessary.
  7. Send a Letter of Confirmation. From a customer row, use "Letter of Confirmation" to view it inline, or "Email Letter" to send it — useful for account-holder/number verification requests that don't need the full Statement of Account.

Data model

  • acc_customer (AccCustomer) — the debtor record: name, contact details, receivable account, payment terms, status (active/grace_period/suspended/cancelled, auto-synced daily by customer_account_status.sync_all), statement_token (public statement link auth), billing frequency/renewal date. Balance is never stored — it's always computed live from the GL.
  • gl_entry (GLEntry) — the ledger of record. A customer's balance is SUM(debit − credit) over their non-cancelled rows against their receivable account; the same rows drive both the Statement of Account and the ageing report.
  • Ageing computation (app/services/party_balance.py) — not a stored table. Each GL charge row is treated as an open item and consumed FIFO by credit rows (oldest-due-first); due dates come from the invoice's posting date plus the customer's payment-term credit days, falling back to the invoice's own due date. This replaced older per-invoice outstanding_amount bookkeeping, which could drift from reality when on-account payments weren't allocated.
  • panel_debtor_commitments (DebtorCommitment) — a debtor's promise-to-pay: promised_at (date only, stored as SAST midnight), promised_amount, status (scheduled/paid/broken/cancelled), soft links to the customer (customer_id), an optional CRM deal, and an optional originating call session (call_session_id, for commitments booked by a debt-collection voice-AI campaign).
  • acc_payment_terms, acc_price_list, acc_account, acc_customer_group, acc_territory — reference/master data a customer links to (payment terms drive ageing due dates; receivable account is the GL account balance/ageing is computed against).
  • acc_sales_invoice / acc_payment_entry — the source documents whose postings become gl_entry rows; invoices and payments are viewable per-customer from the Customers grid but are documented in the Accounting module.
  • Relationship to CRM/qualifier — a Commitment can originate from a CRM deal or from a debt-collection campaign call (app/api/qualifier.py), linking Debtors to the CRM/voice-AI calling stack rather than living in isolation.

Configuration & integrations

  • Role gating: the Commitment Calendar's REST endpoints (/qualifier/commitments*) require the debt_collector role (or a role with equivalent access); creating a commitment also requires the tenant's plan to include the commitment_calls feature.
  • Statement delivery: reuses the platform's existing email and SMS senders (no separate credentials for Debtors); the public statement link is served unauthenticated via a per-customer statement_token, generated lazily on first use.
  • Auto-suspension: a scheduled job ("Sync Account Status") recomputes each customer's status daily from how long their oldest open invoice has been outstanding (grace period, then suspended, then real PBX/hosting disablement) — the same mechanism the Aging report's manual "Disable Customers" action drives on demand.
  • No third-party collections/dunning provider is integrated — ageing, statements and commitments are computed and sent entirely by Lubb's own backend.

API reference

Debtors Aging is a provider-driven grid (computed on the fly, not a plain DB table), reached through the generic grid engine plus a few Debtors-specific action endpoints:

  • GET /grid/ar-ageing/data — paginated ageing rows (one per customer), same sort/filter/search plumbing as any grid.
  • GET /grid/ar-ageing/stats — summary stat cards (totals per bucket) for the ageing report.
  • POST /grid/ar-ageing/action/view_pdf — renders the ageing report as a PDF using the same computed rows as the grid.
  • POST /grid/ar-ageing/action/email_statement / sms_statement — email/SMS one or more customers' Statement of Account; blocked server-side for accounts >10 days overdue with no Commitment Date.
  • POST /grid/ar-ageing/action/send_statements / send_sms — bulk toolbar send across every ageing customer (or a bucket-filtered subset).
  • POST /grid/ar-ageing/action/test_statement / test_sms — preview the bulk send against a reference customer without sending.
  • POST /grid/ar-ageing/action/disable_by_bucket / enable_customer — bulk-suspend or re-enable customers by ageing bucket.
  • GET /grid/acc-customers/data, POST /grid/acc-customers, PUT /grid/acc-customers/{row_id} — standard CRUD on the customer master (create/edit; delete is disabled — customers are cancelled, not removed).
  • POST /grid/acc-customers/action/view_statement / email_statement / sms_document / create_commitment — same statement/commitment actions, launched from the Customers grid.
  • POST /grid/acc-customers/action/view_letter_of_confirmation / email_letter_of_confirmation — view or email a customer's Letter of Confirmation.
  • POST /grid/acc-customers/action/login_as_customer — superuser-only, opens the customer's own service-portal session for support/preview.
  • GET /doc/statement/{token} and GET /doc/statement/{token}/pdf — the public, unauthenticated Statement of Account web page and PDF download the emailed/texted links point to.
  • GET /qualifier/commitments?frm=&to= — list open/scheduled commitments in a date range (Commitment Calendar data source).
  • POST /qualifier/commitments — book a new commitment (promise-to-pay); rejects an exact duplicate (same customer/date) unless allow_duplicate is set.
  • PATCH /qualifier/commitments/{commitment_id} — reschedule or update a commitment's status/amount/notes.
  • DELETE /qualifier/commitments/{commitment_id} — remove a commitment.
  • POST /qualifier/commitments/send-test-warning — send a one-off test of the "commitment due today" reminder email/SMS content.

Lubb ERP Documentation