← all projects

acme-treasury

acme-treasury forecasts the closing balance of ACME's account for eight business days, from what mock-sap says is owed and what mock-bank says it holds, and draws it as a line. Monday's statement from the bank closes 9,300.00 away from the line, in red: the forecast expected to pay an invoice, and the bank refused the payment because the supplier's account is closed, which SAP did not know. Tuesday's forecast replaces the line and lists that invoice as left out, with the bank's reason. The other seven statements close where the forecast then said, one of them at -16,450.00. The payments were made by mock-acme's payment run, and the capture script set up the invoices and moved both clocks.
a cash forecast, held to the bank's statements drawn by acme-treasury from a real run

A cash forecast you can check. It reads what mock-sap says is owed and what mock-bank says the account holds, and says what the account will close at on each of the next business days. Then the clocks are moved, mock-acme's payment run pays what is due each morning, and the bank's statements say whether the forecast was right. Most cash forecasts are never held to anything: by the time the day arrives, nobody looks back.

mock-sap   ──open items──────────────▶
                                        acme-treasury  ──▶  closing balance, day by day
mock-bank  ──camt.052, payments,─────▶
             credits, clock, holidays

                        ... a week of mornings and nights ...

mock-bank  ──camt.053──▶  the same figure, or the test fails
  • Held to the bank's statements. Both mocks keep a clock a test can move, so the tests play a week: a forecast every morning before the payment run, and every night the camt.053 for that day, which has to close where the forecast said, to the cent.
  • A payment at the bank is not owed twice. SAP keeps an item open until a statement clears it, so on the morning after a payment run the same invoice is an open item in SAP and a debit at the bank. They are joined on the EndToEndId and counted once.
  • Money that will not move is listed, not added up. A blocked payable is not money going out, an overdue receivable is not money coming in, and a payment the bank refused will be refused again. Each is listed with its reason. mock-sap 0.20.0 seeds six overdue receivables, 524,619.42 EUR in all, and a forecast that believed them would be comfortable and wrong.
  • The calendar is the bank's. After the cutoff, today's run settles tomorrow, and nothing settles on a weekend or a holiday on the bank's list. A payment the bank will return comes back on its day, and is owed again.
  • It says where it will be wrong. A supplier closed their account last week and SAP does not know, so Monday's forecast expects to pay an invoice the bank then refuses. Monday is out by that invoice, and Tuesday's forecast lists it with the bank's reason. The film above is that week.
  • A payment schedule for when the line dips. plan is a small mixed-integer program, written in JuMP and solved by HiGHS: which invoices to hold, and until which business day, so that no day closes under a floor, holding as little as it can for as short a time as it can. plan --apply makes it happen as a user would, with a payment block in SAP on each held invoice, lifted on the morning it should be paid. It lifts only its own blocks.
  • Customers may be late. A plan that trusts the due dates lifts its block the day a customer's money is due, and if the money comes a day later the account closes under the floor all the same. --customers-late-up-to N holds one plan to the floor for every lateness up to N business days, and --late-customers K allows for at most K customers late at once, each on their own, rather than all of them together. The output says what the caution costs. The last film below is one week played both ways.
acme-treasury forecasts the closing balance of ACME's account for eight business days against a floor of 10,000.00. The forecast is under the floor on Monday the 12th, at -16,450.00, in red: an invoice is due a day before a customer's money arrives. Its payment schedule holds that invoice, INV-E for 61,450.00, until Tuesday the 13th by setting a payment block in mock-sap, and the line is drawn again without the dip. All eight of mock-bank's statements then close where the plan said, none under the floor and the lowest at 26,050.00. On the 13th the block is lifted and the invoice is paid, one day late. The payments were made by mock-acme's payment run, and the capture script set up the invoices, paid in the customer's money on the 13th and moved both clocks.
an overdraft forecast, and one invoice held a day to avoid it drawn by acme-treasury from a real run
One week is played twice against mock-sap and mock-bank, with a floor of 10,000.00 under ACME's account and a customer who pays 42,500.00 on Wednesday the 14th, a day after it is due. In the first, acme-treasury's payment schedule trusts the due date: it holds an invoice, INV-E for 61,450.00, until Tuesday the 13th, lifts the block that morning, and the bank's statement for that day closes at -16,450.00, in red, 42,500.00 off the plan and under the floor. In the second the plan is asked to allow for customers a business day late, and holds the invoice until the 14th. All nine statements close where that plan said, none under the floor, and the invoice is paid two days late. The last line says the caution cost one invoice-day. The payments were made by mock-acme's payment run, and the capture script set up the invoices, paid in the customer's money on the 14th and moved both clocks.
a customer pays a day late: the plan that trusted them, and the one that did not drawn by acme-treasury from a real run
pip install mock-sap mock-bank mock-acme
git clone https://github.com/rseufert/acme-treasury && cd acme-treasury
julia --project=. -e 'using Pkg; Pkg.instantiate()'
bin/demo                                  # a week's invoices, forecast on Monday
bin/acme-treasury plan --days 8 --floor 10000

Written in Julia 1.13, against the mocks as published: mock-sap and mock-bank, with the payment run from mock-acme.