Tally offers two ageing methods and lets the user pick. Bridge silently implements one of them and never says which. This proposes supporting both, and records the live measurements showing it is far cheaper than it looks.
This is an enhancement, not a bug. Bridge is not computing anything wrongly — it implements one of two legitimate methods.
What Tally does
F6: Ageing Method offers Ageing by Bill Date and Ageing by Due Date, and the choice changes the buckets. Measured in Bridge Ageing Lab (synthetic, port 9000, Education) on a bill built for the purpose — CREDIT-30, dated 1-May-26 with a 30-day credit period, due 31-May-26:
| as-of |
method |
days |
bucket |
| 2-Jul-26 |
Ageing by Due Date |
32 |
30 to 60 |
| 2-Jul-26 |
Ageing by Bill Date |
62 |
60 to 90 |
Same bill, same date, different bucket. Both are correct; they answer different questions.
What Bridge does
Ages every bill from BILLDATE, unconditionally. It does not read BILLCREDITPERIOD, and the screen does not state which basis produced the buckets.
On the reference corpus this is invisible, because every bill there has an empty credit period — so bill date equals due date and the two methods coincide. That is why the reconciliation matches Tally exactly, and it is the seventh occasion on which this corpus has been unable to distinguish two candidate behaviours.
Why this is cheap
The data is already in the sealed response. From a VoucherOutstandingsV1 capture of the reference corpus:
BILLCREDITPERIOD occurrences in one partition capture: 19
<BILLDATE TYPE="Date">20240401</BILLDATE> <BILLCREATIONDATE TYPE="Date">20240401</BILLCREATIONDATE>
<NAME>INV-0001</NAME> <BILLCREDITPERIOD/> <BILLTYPE>New Ref</BILLTYPE> <BILLID TYPE="Number">1</BILLID>
The ALLLEDGERENTRIES.* wildcard already returns it on every scan. The parser discards it.
So this needs no request-shape change, no template-hash repin, and no sealed-boundary review. It is a parse-and-compute change plus UI — a different cost class from anything currently in flight.
BILLCREATIONDATE and BILLID are also returned and also unread; worth deciding deliberately whether they matter rather than continuing to drop them by default.
Proposed shape
- A two-state control directly above the ageing buckets, not in a settings page. Use Tally's own vocabulary: Ageing by Bill Date / Ageing by Due Date.
- Default to bill date, so present behaviour is unchanged for everyone.
- Print the basis in the freshness line, so an exported or screenshotted figure is self-describing. A bucket that does not carry its own basis is exactly what made this confusing to diagnose.
Switching costs no Tally traffic. Bridge holds the allocations locally, so it is a client-side recompute — instant, no refetch, no request budget. Tally itself has to re-render the report; Bridge would not.
There is a real analytical benefit beyond parity: showing the difference is informative. A bill 62 days old by date but 32 by terms is precisely the distinction a CA wants visible, and today Bridge picks one silently.
Note for the oracle work (#111)
The native Bills Receivable export returns BILLDATE, BILLDUE and BILLOVERDUE. Measured: BILLOVERDUE is always computed from BILLDUE and does not follow the UI ageing-method setting — verified by toggling the UI to Ageing by Bill Date and re-running the export, which still reported 61 rather than 91 for CREDIT-30.
That makes the export deterministic and safe as an oracle. But the comparison should still be built on amounts and dates, recomputing buckets for whichever method Bridge is presenting, rather than comparing against BILLOVERDUE directly.
Acceptance
- Both methods selectable, defaulting to bill date.
- The active basis is visible wherever buckets are shown.
- A regression with a credit-period bill proving the two methods produce different buckets — the reference corpus cannot test this, so it needs a fixture that carries a non-empty
BILLCREDITPERIOD.
- Documented in
TALLY_PROTOCOL_REFERENCE.md with these measurements.
Tally offers two ageing methods and lets the user pick. Bridge silently implements one of them and never says which. This proposes supporting both, and records the live measurements showing it is far cheaper than it looks.
This is an enhancement, not a bug. Bridge is not computing anything wrongly — it implements one of two legitimate methods.
What Tally does
F6: Ageing Methodoffers Ageing by Bill Date and Ageing by Due Date, and the choice changes the buckets. Measured inBridge Ageing Lab(synthetic, port 9000, Education) on a bill built for the purpose —CREDIT-30, dated 1-May-26 with a 30-day credit period, due 31-May-26:Same bill, same date, different bucket. Both are correct; they answer different questions.
What Bridge does
Ages every bill from
BILLDATE, unconditionally. It does not readBILLCREDITPERIOD, and the screen does not state which basis produced the buckets.On the reference corpus this is invisible, because every bill there has an empty credit period — so bill date equals due date and the two methods coincide. That is why the reconciliation matches Tally exactly, and it is the seventh occasion on which this corpus has been unable to distinguish two candidate behaviours.
Why this is cheap
The data is already in the sealed response. From a
VoucherOutstandingsV1capture of the reference corpus:The
ALLLEDGERENTRIES.*wildcard already returns it on every scan. The parser discards it.So this needs no request-shape change, no template-hash repin, and no sealed-boundary review. It is a parse-and-compute change plus UI — a different cost class from anything currently in flight.
BILLCREATIONDATEandBILLIDare also returned and also unread; worth deciding deliberately whether they matter rather than continuing to drop them by default.Proposed shape
Switching costs no Tally traffic. Bridge holds the allocations locally, so it is a client-side recompute — instant, no refetch, no request budget. Tally itself has to re-render the report; Bridge would not.
There is a real analytical benefit beyond parity: showing the difference is informative. A bill 62 days old by date but 32 by terms is precisely the distinction a CA wants visible, and today Bridge picks one silently.
Note for the oracle work (#111)
The native
Bills Receivableexport returnsBILLDATE,BILLDUEandBILLOVERDUE. Measured:BILLOVERDUEis always computed fromBILLDUEand does not follow the UI ageing-method setting — verified by toggling the UI to Ageing by Bill Date and re-running the export, which still reported 61 rather than 91 forCREDIT-30.That makes the export deterministic and safe as an oracle. But the comparison should still be built on amounts and dates, recomputing buckets for whichever method Bridge is presenting, rather than comparing against
BILLOVERDUEdirectly.Acceptance
BILLCREDITPERIOD.TALLY_PROTOCOL_REFERENCE.mdwith these measurements.