If the entries behind a receivables card add up to something else, which figure should an owner trust? I did not want the public demonstration to carry a second set of manually adjusted totals.
This article follows the calculations from synthetic transactions to cards and details. It then connects a new independent validator for missing amounts and overcollection. No company ledger or client accounting policy is disclosed.
A manually chosen card creates a second source of truth
The monthly example begins with three sales rows and two purchase rows. Synthetic sales amounts are KRW 24 million, 18 million and 6 million, with receipt entries attached to each row. The first row has two receipts, KRW 10 million and 6.8 million. Instead of writing KRW 48 million into a card and decorating a separate detail list, I derive the summary from those rows.
Receipts total KRW 35.4 million and outstanding balances total KRW 12.6 million. The first two rows contribute KRW 7.2 million and 5.4 million respectively to that balance. These figures demonstrate a relationship; they do not prove real revenue or cash flow. Calling the material synthetic and leaving a traceable row calculation are both necessary parts of the explanation.
Receivables are a view of the same sales rows
The current model derives collected amounts and balances from the same sales entries. Opening sales shows all those entries. Opening receivables shows the entries whose outstanding balance is positive. It does not ask someone to type a separate receivables ledger. Purchases and payables follow the same pattern. That removes an avoidable opportunity for the detail screen to disagree with its summary.
The excerpt below comes from the current public synthetic model. It depends on the syntheticLedger function that supplies the source rows, so it is not a standalone program. It is also not a general validator for arbitrary missing or negative inputs. Distinguishing the original closed-fixture renderer from the independent input validator shared with this article matters when deciding which code to reuse.
Explore the implementation
Current derived-entry function; depends on the fixture provider
export function ledgerRows(key){
if(!['sales','purchases','receivables','payables'].includes(key))throw Error('Unknown example ledger');
const ledger=syntheticLedger(),source=['sales','receivables'].includes(key)?ledger.sales:ledger.purchases;
return source.map(row=>({...row,settled:row.receipts.reduce((n,v)=>n+v,0),balance:row.amount-row.receipts.reduce((n,v)=>n+v,0)})).filter(row=>!['receivables','payables'].includes(key)||row.balance>0);
}Outstanding and overdue answer different questions
The example’s as-of date is 15 April 2030. The first sales row is due on 8 April and still has KRW 7.2 million outstanding. The second is due on 17 April: its KRW 5.4 million balance exists, but its due date has not arrived. Calling all KRW 12.6 million overdue would change the meaning. Titles and supporting sentences need the same date precision as the calculation itself.
The monthly example covers the synthetic period from 1 to 15 April. A real implementation must also define opening balances, advances, refunds, offsets, tax and revenue-recognition policy. I do not present this simple balance calculation as a finished implementation of those policies. First agree which question an owner needs answered, then define the transactions and dates that belong in the answer.
Separate the display unit from the calculation unit
Amounts are stored and calculated as integer Korean won. Korean presentation uses ten-thousand-won units, while English presentation uses the won sign and digit grouping. The model never parses those formatted strings back into a sum. A bare displayed number cannot explain its unit, and a sum of rounded strings can differ from a sum of original amounts.
JavaScript numbers do not represent integers of unlimited size exactly. The new independent module checks that amounts and individual settlements are nonnegative safe integers. It checks the accumulated settlements and ledger totals as well. It does not silently coerce numeric strings or fractional values. If another service needs multiple currencies or fractional units, its input contract must change before its formatting does.
Unknown is not another spelling of zero
Filling a missing amount with zero lets every total look complete. It also treats an amount that has not been checked as if it were a known zero. The new validator returns null totals for missing amounts, missing settlement lists, duplicate transaction identifiers, overcollection or integer overflow. It leaves reviewable row details and issue codes rather than showing an apparently complete ledger.
For the same reason it does not clamp an overcollected balance to zero. In an operational system the excess could mean an advance or an allocation mistake, requiring an explicit policy. This small module does not choose that policy on the reader’s behalf; it returns a review issue. A valid subset must not be labelled as the whole ledger. Once the issue is resolved, the original input can be reconciled again.
Share the checks as well as the result
For the normal synthetic example I checked that each amount equals its receipts plus outstanding balance, with the same relation across the totals. The independent package tests missing values, duplicates, overcollection and safe-integer boundaries. It also compares balances across 861 combinations of small amounts and partial settlements. That validates this integer model, not the accounting suitability of real business records.
The shared module page lets readers switch between valid and invalid examples and download the code, tests and bilingual demos. Applying it requires explicit company rules for identifiers, units, due dates and settlement allocation. I consider being able to unpack and verify a figure as important as being able to read it quickly on an owner dashboard.