feat(store): journal the document fields and file the issued PDF (P4b S4.3)
S4.1 gave documents a number, terms, line accounts and snapshots, and S4.2
gave parties an email and default terms, but the store journaled none of
them: a document carrying one was refused by a temporary guard, because a
public write that replay loses would break I-3. This slice makes the
journal carry them.
- `invoice.created` and `bill.created` write a number, terms, bill-to and
seller snapshots, references, memo and a duplicate's reason, and each
line its account, quantity and unit price, every key only when set. A
document naming none is journaled byte for byte as every earlier version
wrote it (`no_new_fields_journals_byte_for_byte`), so the owner's live
books keep working.
- All of it is behind `documents_v2` (B-27): a write that names one waits
for the owner's upgrade, refused with `format_upgrade_pending` and
journaling nothing; a write that names none never waits
(`documents_v2_waits_for_the_upgrade`).
- `invoicing.first_number` is settable only while no numbered invoice
exists (A-2, B-40): the series has begun, and a number is never reused or
renumbered. Refused at `set_setting`, before anything is journaled.
- The issued copy is filed in the issuing call (A-7, B-40 item 3):
`create_invoice_issued` journals the entry, the invoice, then
`receipt.attached` on that entry (`INV-0007.pdf`, `application/pdf`,
"Issued invoice") and `attachment.linked` to the invoice. There is no
`issued_pdf` field: the attachment id is allocated after the entry exists
(CD-A6), and the link is how the copy is found and how its SHA-256 is
checked. Every refusal happens before the first journal line; the PDF's
bytes are stored before it, so a refused issue leaves an unreferenced,
deduplicated blob at worst.
- A preview runs the issue on a clone and journals nothing: the posting,
the total, the number it would take and the due date (`preview_invoice`,
`preview_bill`). Books that haven't upgraded keep the legacy shape: the
copy is filed as "Invoice {id}.pdf" and no number is taken (migration
item 5).
- Schema 16 adds the document columns; a line's new fields ride
`lines_json` as its project does. `differs_from` already compares whole
invoices, bills and parties, so it sees every new field.
8.0.6: (1) `books_written_under_older_rules_still_open` hand-writes both
new document shapes through JournalWriter and still opens; (2) a new
fixture `old-books/p4_s43_documents` was written by main d5385a7e (S4.2,
the version before this slice) through the old-books-writer harness, with
its expected.json; this version opens it and reports the same figures;
(3) see the note on R-A step 4 in the PR (the over-the-wire document write
needs S4.4's arguments; S4.2's invoicing-setting entry already covers the
feature's cut-over); (4) `documents_v2_is_its_own_feature` is green; (5) no
figure moves: no golden changed; (6) merge does not need the owner's
go-ahead; the live books refuse the new writes until he gives it.
just check green (no kernel function, Lean theorem or Ledger command, so no
`just prove` change).
This commit is contained in:
1 parent
6be0055927
commit
0e5aebda99
13 files changed
+1721
-224
No files matched your search
+23
-2
@@ -56,12 +56,25 @@ apply is safe — on startup the applier replays every journal line with
|
||||
|
||||
- `seq`: 1-based, gapless, assigned by the writer.
|
||||
- `ts`: UTC, RFC 3339. Recorded, never trusted for ordering (`seq` orders).
|
||||
- `type`: `account.added`, `entry.posted`, `entry.reversed`,
|
||||
`receipt.attached`, `receipt.linked`, `receipt.unlinked`,
|
||||
- `type`: `account.added`, `entry.posted`, `entry.reversed`, `invoice.created`,
|
||||
`bill.created`, `receipt.attached`, `receipt.linked`, `receipt.unlinked`,
|
||||
`document.filed`, `attachment.withdrawn` (plus the A/R, A/P, asset,
|
||||
period, payroll, and class events listed on `JournalLine`).
|
||||
- `payload`: the full domain facts (account fields, entry with lines, blob
|
||||
sha, ...). Everything the applier needs; nothing it must look up elsewhere.
|
||||
An `invoice.created` or `bill.created` written after the `documents_v2`
|
||||
upgrade (plan P4b S4.3) carries only the new fields it names, so a
|
||||
document naming none is byte for byte the payload every earlier version
|
||||
wrote: the invoice's `number`, `terms`, `bill_to` and `seller` snapshots
|
||||
(as they were at issue), `customer_ref` and `memo`; the bill's
|
||||
`vendor_ref`, `received_on`, `terms`, `memo` and the `duplicate_reason`
|
||||
that allowed a duplicate of a standing vendor invoice; and each line's
|
||||
`account`, `quantity` and `unit_price`. The issued copy of an invoice is
|
||||
**not** a field on the event (its attachment id is allocated after the
|
||||
entry exists): it is a `receipt.attached` on the invoice's own entry
|
||||
(`INV-0007.pdf`, `application/pdf`, "Issued invoice"), its SHA-256 the
|
||||
`blob_sha`, linked to the invoice with `attachment.linked` and found
|
||||
through that link (plan 8.3.11).
|
||||
- `prev`: hex SHA-256 of the previous line's canonical JSON (keys sorted, no
|
||||
whitespace). The genesis line uses `"prev": "000...0"` (64 zeros).
|
||||
|
||||
@@ -149,6 +162,14 @@ Suspected corruption is fixed by rebuild, never by forensics.
|
||||
tails. (Poll `WHERE seq > ?`; later SSE over the HTTP transport. This is
|
||||
the correct answer to "watch its files to update live" — watch the event
|
||||
stream, not the files.)
|
||||
- **Document fields.** Since schema v16 (plan P4b S4.3) the documents'
|
||||
new fields are columns: `invoices(number, terms, bill_to, seller,
|
||||
customer_ref, memo)` and `bills(vendor_ref, received_on, terms, memo,
|
||||
duplicate_reason)`, with terms and snapshots as canonical JSON TEXT and a
|
||||
document written before them NULL there. A line's `account`, `quantity`
|
||||
and `unit_price` ride its document's `lines_json`, as its project does.
|
||||
The party's email and default terms are journaled and replayed into the
|
||||
ledger; the projection keeps no party table to hold them.
|
||||
- **Amounts as TEXT, never REAL.** Unchanged and still load-bearing: a REAL
|
||||
column would silently reintroduce binary floats into the money path.
|
||||
- **Append-only, three layers.** (1) The engine's API never mutates posted
|
||||
|
||||
Reference in new issue
Block a user