ADR-018: Finance reporting
Status: accepted 6 October 2026. The owner chose revenue on delivery, all four areas (sales and material profit, purchases and stock, payments and couriers, operating expenses) and access for managers and above. The other points below are defaults taken while building it.
Context
Section titled “Context”Flowery already records what a finance report needs:
- what each order sold, with the delivery fee and currency;
- when each order changed status (
order_transitions); - the batch cost of every stem an assembly used (
order_allocations); - supplies with their batch prices and supplier delivery charges;
- write-offs and supplier returns at batch cost, in the ledger;
- payments recorded by staff, with reversals;
- what Uklon charged for each courier.
Nothing added these up over a period. The owner asked for a module that calculates revenue and the rest of the shop’s figures. Rent, wages and other running costs were not recorded anywhere.
Decision
Section titled “Decision”-
Revenue is recognised on delivery. An order counts on the moment of its
deliveredtransition, in the workspace timezone (rule B05). The period is whole local days, so an order delivered at 23:30 on 31 October in Kyiv counts in October although it is 31 October in UTC either way. An order delivered before transitions were recorded (BRV-07, 14 September 2026) counts on its delivery date. Pending, confirmed, in-progress and ready orders are shown separately as not yet completed; cancelled orders never count. -
Net sales are items (the order subtotal, packaging included) plus delivery fees, less refunds. A refund is a delivered order whose payment status is
refunded, and its whole amount comes off. Its stems stay in the cost. -
Material cost is the order page’s.
OrderCosting::figures()gives many orders at once the figuresummary()shows for one: the consumed batch cost once assembled, otherwise the revision’s estimate. A test asserts that the two agree. Material margin and markup keep the BRV-10 definitions. When some orders lack a cost, the margin is computed over the orders whose cost is known, and the page says how many of how many. -
A missing cost makes profit unknown, never partial (rule B13 for totals). Every sum that can lack a part says how much is known and names what is missing. Gross and operating profit are shown only when every part is known.
-
The statement:
Line Source Net sales item sales + delivery fees − refunds − Stems used material cost of the delivered orders − Uklon couriers uklon_deliveries.final_cost, elsebooked_cost= Gross profit − Written off write_offmovements in the period, at batch cost− Lost on cancelled orders consumed cost of orders cancelled after assembly (rule B08), on the cancellation date − Supplier delivery charges supplies.delivery_price, on the supply date− Operating expenses recorded expenses dated in the period = Operating profit before tax -
Purchases are not an expense. Money spent on stock is reported per supplier: batches at their price × the received quantity (as on the supply page), delivery charges, and returns on the date they happened. The stems become a cost when an order uses them or when they are written off. Subtracting purchases as well would count them twice. Supplier delivery charges are not spread over batch costs, so they are a cost of the period they arrive in.
-
Operating expenses are a new record (
expenses): category, description, amount, date and note, in the workspace currency, which each row snapshots. Managers and above record, correct and remove them, and a removed expense is soft-deleted. An expense locks the workspace currency like any priced record (B12). The categories are rent, wages, utilities, consumables, marketing, transport, equipment, software, taxes and fees, and other. -
Access. Two permissions:
finance.view, for the pages and the CSV exports;finance.expenses, for recording expenses.
Both go to manager and above, the same people who already see purchase costs and margins (ADR-001).
-
The demo shop. While it is loaded, its orders, batches and expenses count, and the pages say so with a switch to hide them. The demo adds three small expenses this month, which its removal deletes (ADR-016).
-
Read-only. The report never writes. Point-in-time figures (stock on hand, customers’ debt, orders not yet completed) are as of now, whatever the period.
Consequences
Section titled “Consequences”- Managers and above see business-wide totals, not only per-order margin. If an owner wants to keep these figures to themselves, the matrix changes in
WorkspaceUser::hasPermission()and its test. - Packaging has no cost column. A packaged order’s assembled cost covers stems only, as on the order page. Its estimate is unknown until assembly. Shops can record wrapping materials as consumables.
- Labour is in the figures only when wages are recorded as an expense. A shop’s own courier has no recorded cost per order; it belongs under transport.
- A refund is only the
refundedpayment status. Reversing payment entries changes cash received, not revenue. - The sums use the same
Moneylimit as one order (99,999,999.99 in the currency’s major unit). A shop’s year would have to exceed that to reach it. - Stock value is today’s, not historical; the ledger could reconstruct a past date later.