Skip to main content

Statement of Account

What it is

The screen that produces a statement of account for one client on one date: what they owe, split into age buckets, with every invoice, payment and credit note that makes up the figure. From here you email it, download it as Excel, or print it to PDF.

It reads the ledger_entries view, which already serves an invoice's balance gross of credits and each credit note as its own full credit line. Nothing on this screen recomputes that.

Who can open it

Needs the finance.ledger feature grant while FEATURE_GRANULAR_RBAC is on. The owner bypasses the grant unconditionally. While that flag is off, any signed-in account can open the screen by typing the address — the sidebar hides it, but hiding is not a gate.

Before you start

The printed statement carries A2Z Ship Stores' letterhead whatever entity you picked

The Print / PDF output has the company header written into the component: the GST number 33AUQPA5216Q1ZC, supply@a2zshipstores.com, purchase@a2zshipstores.com, two Chennai phone numbers, www.a2zshipstores.com, "Chennai - 600001" and /logo.png. SOALedger is not given the entity at all, so a statement for a Marella Marine or Marsea Ship Chandlers client prints another company's name, tax number and bank contacts.

Do not send a printed statement to a client of any entity other than A2Z Ship Stores. The Download Excel and Send via Resend paths are built server-side from the selected client's own entity and are not affected.

Nothing appears until you choose a client. The screen loads with "Awaiting Instruction" and no figures. Choosing a client and pressing Execute Generation reloads the page with ?client_id= and ?date= in the address, so a statement is a link you can share or bookmark.

The statement is in the client's own currency and is never converted. A USD client's statement is in USD, an INR client's in INR. Conversion at the auditor's monthly rate exists for Tally and the accounting exports only — it is deliberately kept off customer-facing statements (owner decision, 2026-08-19). A2Z-F263 "Multi-currency SOA view" is recorded as PARTIALLY IMPLEMENTED because of this: the component still exists in the codebase but is not mounted on the page.

Age buckets are measured from the client's payment terms, not from the invoice date. A client on 60-day terms has an invoice 70 days old sitting in 1-30, not 61-90.

Every control on the page

The button strip, left to right:

ControlWhat it does
+ Issue Credit NoteGoes to New Credit Note
Manage Credit NotesGoes to Credit Notes
Manage Wired PaymentsGoes to Wired Payments
⚡ Automated ImportGoes to /import, carrying ?client_id= when a client is selected
Batch DispatchEmails statements to several clients at once
Bulk ExportDownloads statements for several clients at once

The generator form:

ControlWhat it does
Target EntityThe client, alphabetically. Reads "— SELECT COUNTERPARTY —" until you pick one. Despite the label this is the client, not one of your own companies
Snapshot DateThe date the statement is drawn to. Defaults to today
Execute GenerationSubmits the form as a GET, so the result is addressable

Once a statement is generated:

  • Escalation Protocol Active — a red banner, shown only when something has passed 90 days, naming the amount.
  • Five bucket cards — Current, 1-30, 31-60, 61-90, 90+, each in the client's currency. A bucket with nothing in it is greyed.
  • The ledger — every entry, with Ref # and Date sortable; click the heading, click again to reverse.
  • Four actions at the foot: 📊 Download Excel, ✉️ Send via Resend, 🖨️ Print / PDF, and ✉️ Open in Mail App which appears only as a fallback.

⚠️ Send via Resend is disabled when the client has no email address, with the tooltip "Add client email first in Clients page". It reads ✓ Sent! afterwards and stays disabled, so a genuine resend needs a page reload.

What it writes

Generating a statement writes nothing. It is a read of ledger_entries.

🔴 The ledger deletes invoices, and it is the only thing on this screen that writes.

  • The at the end of an invoice row deletes that invoice — "This will permanently remove the invoice and affect the SOA aging totals."
  • Ticking rows shows Delete Selected (N), which deletes them all.

Both cascade to credit notes. The bulk prompt counts them first and says so: "Delete N invoice(s)? This will ALSO permanently delete N linked credit note(s)." There is no undo, and a deleted invoice is gone from every other screen too — this is not a statement-only action.

⚠️ Only invoice rows carry a ✕. Payments and credit notes on the statement cannot be removed from here.

Send via Resend sends through /api/send-soa; Download Excel builds the file in /api/exports/soa, which recomputes the balances server-side from ledger_entries and takes only ids from the browser.

When it goes wrong

"Clearance Status: Positive" when you expect a balance. The client has no rows in ledger_entries at all. Check you picked the right client — the list is every client across every entity, and two companies can have similar names.

The total does not match the invoice list you were expecting. The statement is drawn to the Snapshot Date. An invoice raised after that date is not on it.

A credit note appears to be applied twice. It should not — invoices come through gross of credits and each credit note as its own line, which is what the aging calculation expects. If you see a double deduction, report it: synthesising credit-note rows separately is a bug this page has had before.

Send via Resend is greyed out. The client has no email address. Add one on Clients.

The printed PDF names the wrong company. Expected — see the warning at the top. Use Excel.