painfree 0.7.0
A bank's status report is now read against the payment it answers, instead of being printed as JSON.
/ui/statements/<id> rendered every downloaded document the same way: a key/value block about the file, then the normalised payload in a <pre>. For a pain.002 that is a screen of nulls with one status buried in it, and not one figure saying who was paid or how much.
The figures are not in the report, and that is not a defect in the report. Banks report by exception: the report for a file the bank took whole names no transaction at all, and a partially accepted one lists only what failed. A page rendered from the report is therefore empty in the ordinary case and carries no amounts in any case. The money is in the pain.001 that was sent, one OrgnlMsgId away.
So the rows are the message's and the report annotates them. Beneficiary, amount, execution date and reference on one side; the bank's status and its own reason code and text on the other, with the boundary between the two drawn on the page, because nobody should have to guess which document a number came from.
All three status levels are kept, and none is derived from another. GrpSts, PmtInfSts and TxSts are shown side by side, with the level that decided the order marked and a level the bank left out saying not reported rather than repeating the level above it. Where a transfer's status was inherited from its payment block or from the whole message, the page says so: "the bank accepted this payment" and "the bank accepted the file this payment was in" are different sentences. A listed rejection is never read across to the transfer beside it.
An account statement is drawn as an account statement. Two amount columns rather than one signed one, entries in the order the bank sent them, each opening into its transactions — which for a collected entry is the only place a payroll run's two hundred lines exist.
The running balance is arithmetic the console does, in Decimal, and the page says so: no camt message carries one. Three rules keep it honest. No opening balance means no column, because a column of figures all wrong by the same amount is worse than no column. Only booked entries move it, so a PDNG entry is grouped above the ledger with no balance beside it. And the total is checked against the closing balance the bank stated, with the page saying whether it adds up — not checked and does not add up being different answers.
An entry that names a MsgId this service generated links to the order it settles. That is the last link of the chain the status report starts: the report says the bank took the payment, the booking says the money left the account.
The statements index is split in two, because it holds two kinds of document and used to carry the union of both column sets — a closing balance column empty on every status report, an order column empty on every statement. It still lists no entries, and the responses half lists no amount and no counterparty either.
None of this needs JavaScript: the disclosures are <details> and the filters are links, so every state is a URL and the pages work with scripting off.
No migration, and nothing new is stored. Every value on these pages was already in the normalised payload, on the order, or in the status-code table the reconciler decides with. Reading the payment behind a status report needs payments:read on the connection; without it the page still renders what the report itself says.