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
@@ -0,0 +1,265 @@
|
||||
{
|
||||
"assets": [],
|
||||
"bills": [
|
||||
{
|
||||
"amount_paid": "0",
|
||||
"date": "2026-02-05",
|
||||
"due_date": "2026-03-07",
|
||||
"entry_id": 4,
|
||||
"id": 1,
|
||||
"lines": [
|
||||
{
|
||||
"amount": "80.00",
|
||||
"description": "Paper"
|
||||
}
|
||||
],
|
||||
"status": "Open",
|
||||
"vendor_id": 1
|
||||
}
|
||||
],
|
||||
"entries": [
|
||||
{
|
||||
"date": "2026-01-01",
|
||||
"description": "Opening capital",
|
||||
"id": 1,
|
||||
"lines": [
|
||||
{
|
||||
"account": 1,
|
||||
"amount": {
|
||||
"amount": "1000.00",
|
||||
"currency": [
|
||||
85,
|
||||
83,
|
||||
68
|
||||
]
|
||||
},
|
||||
"side": "Debit"
|
||||
},
|
||||
{
|
||||
"account": 2,
|
||||
"amount": {
|
||||
"amount": "1000.00",
|
||||
"currency": [
|
||||
85,
|
||||
83,
|
||||
68
|
||||
]
|
||||
},
|
||||
"side": "Credit"
|
||||
}
|
||||
],
|
||||
"reversal_of": null,
|
||||
"status": "Posted"
|
||||
},
|
||||
{
|
||||
"date": "2026-02-01",
|
||||
"description": "Invoice 1",
|
||||
"id": 2,
|
||||
"lines": [
|
||||
{
|
||||
"account": 3,
|
||||
"amount": {
|
||||
"amount": "100.00",
|
||||
"currency": [
|
||||
85,
|
||||
83,
|
||||
68
|
||||
]
|
||||
},
|
||||
"side": "Debit"
|
||||
},
|
||||
{
|
||||
"account": 4,
|
||||
"amount": {
|
||||
"amount": "100.00",
|
||||
"currency": [
|
||||
85,
|
||||
83,
|
||||
68
|
||||
]
|
||||
},
|
||||
"side": "Credit"
|
||||
}
|
||||
],
|
||||
"reversal_of": null,
|
||||
"status": "Posted"
|
||||
},
|
||||
{
|
||||
"date": "2026-02-20",
|
||||
"description": "Payment on invoice 1",
|
||||
"id": 3,
|
||||
"lines": [
|
||||
{
|
||||
"account": 1,
|
||||
"amount": {
|
||||
"amount": "40.00",
|
||||
"currency": [
|
||||
85,
|
||||
83,
|
||||
68
|
||||
]
|
||||
},
|
||||
"side": "Debit"
|
||||
},
|
||||
{
|
||||
"account": 3,
|
||||
"amount": {
|
||||
"amount": "40.00",
|
||||
"currency": [
|
||||
85,
|
||||
83,
|
||||
68
|
||||
]
|
||||
},
|
||||
"side": "Credit"
|
||||
}
|
||||
],
|
||||
"reversal_of": null,
|
||||
"status": "Posted"
|
||||
},
|
||||
{
|
||||
"date": "2026-02-05",
|
||||
"description": "Bill 1",
|
||||
"id": 4,
|
||||
"lines": [
|
||||
{
|
||||
"account": 5,
|
||||
"amount": {
|
||||
"amount": "80.00",
|
||||
"currency": [
|
||||
85,
|
||||
83,
|
||||
68
|
||||
]
|
||||
},
|
||||
"side": "Debit"
|
||||
},
|
||||
{
|
||||
"account": 6,
|
||||
"amount": {
|
||||
"amount": "80.00",
|
||||
"currency": [
|
||||
85,
|
||||
83,
|
||||
68
|
||||
]
|
||||
},
|
||||
"side": "Credit"
|
||||
}
|
||||
],
|
||||
"reversal_of": null,
|
||||
"status": "Posted"
|
||||
}
|
||||
],
|
||||
"entry_parties": [
|
||||
[
|
||||
2,
|
||||
[
|
||||
{
|
||||
"party_id": 1,
|
||||
"share": "derived"
|
||||
}
|
||||
]
|
||||
],
|
||||
[
|
||||
3,
|
||||
[
|
||||
{
|
||||
"party_id": 1,
|
||||
"share": "derived"
|
||||
}
|
||||
]
|
||||
],
|
||||
[
|
||||
4,
|
||||
[
|
||||
{
|
||||
"party_id": 2,
|
||||
"share": "derived"
|
||||
}
|
||||
]
|
||||
]
|
||||
],
|
||||
"invoices": [
|
||||
{
|
||||
"amount_paid": "40.00",
|
||||
"customer_id": 1,
|
||||
"date": "2026-02-01",
|
||||
"due_date": "2026-03-03",
|
||||
"entry_id": 2,
|
||||
"id": 1,
|
||||
"lines": [
|
||||
{
|
||||
"amount": "100.00",
|
||||
"description": "Design"
|
||||
}
|
||||
],
|
||||
"status": "Partial"
|
||||
}
|
||||
],
|
||||
"payroll_runs": [],
|
||||
"periods": [],
|
||||
"recurring": [],
|
||||
"schedules": [],
|
||||
"snapshot": {
|
||||
"Ok": {
|
||||
"balance_sheet": {
|
||||
"assets": "1100",
|
||||
"balanced": true,
|
||||
"current_earnings": "20",
|
||||
"equity": "1020",
|
||||
"liabilities": "80"
|
||||
},
|
||||
"profit_and_loss": {
|
||||
"expenses": "80",
|
||||
"net_income": "20",
|
||||
"revenue": "100"
|
||||
},
|
||||
"trial_balance": {
|
||||
"lines": [
|
||||
{
|
||||
"code": "1000",
|
||||
"credit": "0",
|
||||
"debit": "1040",
|
||||
"name": "Bank"
|
||||
},
|
||||
{
|
||||
"code": "1200",
|
||||
"credit": "0",
|
||||
"debit": "60",
|
||||
"name": "Accounts Receivable"
|
||||
},
|
||||
{
|
||||
"code": "2000",
|
||||
"credit": "80",
|
||||
"debit": "0",
|
||||
"name": "Accounts Payable"
|
||||
},
|
||||
{
|
||||
"code": "3000",
|
||||
"credit": "1000",
|
||||
"debit": "0",
|
||||
"name": "Capital"
|
||||
},
|
||||
{
|
||||
"code": "4000",
|
||||
"credit": "100",
|
||||
"debit": "0",
|
||||
"name": "Revenue"
|
||||
},
|
||||
{
|
||||
"code": "6999",
|
||||
"credit": "0",
|
||||
"debit": "80",
|
||||
"name": "General Expenses"
|
||||
}
|
||||
],
|
||||
"total_credits": "1180",
|
||||
"total_debits": "1180"
|
||||
}
|
||||
}
|
||||
},
|
||||
"verify": {
|
||||
"Ok": null
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
{"payload":{"base_currency":"USD"},"prev":"0000000000000000000000000000000000000000000000000000000000000000","seq":1,"ts":"2026-10-27T10:05:35Z","type":"store.created"}
|
||||
{"payload":{"features":["account_renamed","asset_basis","depreciation_catch_up","disposal_before_service","document_line_projects","document_standing","documents_v2","members_cumulative","month_reposting","party_creditors","payment_instruments","projects","require_tax_category","schedule_convention","tax_categories_gain_exempt","vendor_1099_decisions"],"format":2},"prev":"a5bda5ebc50445e043401e7d4eca41f5a952ce0eca31e96ab219cd0c97a2d78e","seq":2,"ts":"2026-10-27T10:05:35Z","type":"journal.format_set"}
|
||||
{"payload":{"account_type":"Asset","code":"1000","currency":"USD","id":1,"name":"Bank","parent":null,"placeholder":false},"prev":"1fcfbf8c42fcb55f9b993f982272af69186f6f74b392bc07f6f381850737a7ef","seq":3,"ts":"2026-10-27T10:05:35Z","type":"account.added"}
|
||||
{"payload":{"account_type":"Equity","code":"3000","currency":"USD","id":2,"name":"Capital","parent":null,"placeholder":false},"prev":"a1df6eddd876e9a0ad6bc16bb1313eba210b50c99eb69404da78384af6501a0d","seq":4,"ts":"2026-10-27T10:05:35Z","type":"account.added"}
|
||||
{"payload":{"date":"2026-01-01","description":"Opening capital","id":1},"prev":"ee64fc19e7bc187997ff388f5ed450272012f3212db8bf31bec5284cb482a309","seq":5,"ts":"2026-10-27T10:05:35Z","type":"entry.created"}
|
||||
{"payload":{"account":1,"amount":"1000.00","currency":"USD","id":1,"side":"debit"},"prev":"a94ba3cf73b371457fd2df9eede6881be9f604b52572574a5218af18ba0524e5","seq":6,"ts":"2026-10-27T10:05:35Z","type":"entry.line_added"}
|
||||
{"payload":{"account":2,"amount":"1000.00","currency":"USD","id":1,"side":"credit"},"prev":"9f181cadbc27d4c621614b913e55d3caa823d9608c7dc4cc0240b04c2320ce42","seq":7,"ts":"2026-10-27T10:05:35Z","type":"entry.line_added"}
|
||||
{"payload":{"date":"2026-01-01","description":"Opening capital","id":1,"lines":[{"account":1,"amount":"1000.00","currency":"USD","side":"debit"},{"account":2,"amount":"1000.00","currency":"USD","side":"credit"}],"reversal_of":null},"prev":"01d4037388ff0a14b015fa7a8e63308f2dcde48933850f74815f23baf01d4fb1","seq":8,"ts":"2026-10-27T10:05:35Z","type":"entry.posted"}
|
||||
{"payload":{"id":1,"name":"Fernhill Labs"},"prev":"a97e58dc7d09b974b9fba27e61482a72ff1b7634d1eacff1ce86324c049150be","seq":9,"ts":"2026-10-27T10:05:35Z","type":"customer.added"}
|
||||
{"payload":{"id":1,"name":"Tobin Harlow Supply"},"prev":"36adb1c1e5e6e91c62b71b2aa02d03b89106b796236e0dac0c2ef2f4dbda98cf","seq":10,"ts":"2026-10-27T10:05:35Z","type":"vendor.added"}
|
||||
{"payload":{"account_type":"Asset","code":"1200","currency":"USD","id":3,"name":"Accounts Receivable","parent":null,"placeholder":false},"prev":"41668aee0e8c90a460c0255b76ecc7116d4e0884096c28f0d44ec58300b1c08a","seq":11,"ts":"2026-10-27T10:05:35Z","type":"account.added"}
|
||||
{"payload":{"account_type":"Revenue","code":"4000","currency":"USD","id":4,"name":"Revenue","parent":null,"placeholder":false},"prev":"95ec779a5c6e75702e0220fd63fe77b1fac4f8225c84fb288b48fa80a46eb87b","seq":12,"ts":"2026-10-27T10:05:35Z","type":"account.added"}
|
||||
{"payload":{"date":"2026-02-01","description":"Invoice 1","id":2},"prev":"008dafc140ceaced1176b0c0b304b8e8f6f03ebc32b7bf886af05087b385d239","seq":13,"ts":"2026-10-27T10:05:35Z","type":"entry.created"}
|
||||
{"payload":{"account":3,"amount":"100.00","currency":"USD","id":2,"side":"debit"},"prev":"b831d4b02d0595b88b17e66f834dcb2f82bf34bd16642ca2b648ba564cd57c76","seq":14,"ts":"2026-10-27T10:05:35Z","type":"entry.line_added"}
|
||||
{"payload":{"account":4,"amount":"100.00","currency":"USD","id":2,"side":"credit"},"prev":"9ac5ebb94ec4f0d0cae2a2d6cf8891ede2d1f88e5681dda0bb08dc825211ed17","seq":15,"ts":"2026-10-27T10:05:35Z","type":"entry.line_added"}
|
||||
{"payload":{"date":"2026-02-01","description":"Invoice 1","id":2,"lines":[{"account":3,"amount":"100.00","currency":"USD","side":"debit"},{"account":4,"amount":"100.00","currency":"USD","side":"credit"}],"reversal_of":null},"prev":"281fefb129d147bd4d8e2c7400f5de8c18c9d353ab5d35d70128e7bda6823af4","seq":16,"ts":"2026-10-27T10:05:35Z","type":"entry.posted"}
|
||||
{"payload":{"customer_id":1,"date":"2026-02-01","due_date":"2026-03-03","entry_id":2,"id":1,"lines":[{"amount":"100.00","description":"Design"}]},"prev":"968344c97883fed97e1f6270da8715ef94776d4a4c99eef1273c55e95f6aba9c","seq":17,"ts":"2026-10-27T10:05:35Z","type":"invoice.created"}
|
||||
{"payload":{"date":"2026-02-20","description":"Payment on invoice 1","id":3},"prev":"31d6018e811feac96b0fd9a171cf1d931aefab064750192bef91441f53791437","seq":18,"ts":"2026-10-27T10:05:35Z","type":"entry.created"}
|
||||
{"payload":{"account":1,"amount":"40.00","currency":"USD","id":3,"side":"debit"},"prev":"fcbb56661d4ddc912ad2ab09badb3c2a8bd95919cfc2a42f17862b74b2e3af93","seq":19,"ts":"2026-10-27T10:05:35Z","type":"entry.line_added"}
|
||||
{"payload":{"account":3,"amount":"40.00","currency":"USD","id":3,"side":"credit"},"prev":"c2635b69e09523474d7a62556df3b3c44bf6732ef7918a1ab988291757d916c9","seq":20,"ts":"2026-10-27T10:05:35Z","type":"entry.line_added"}
|
||||
{"payload":{"date":"2026-02-20","description":"Payment on invoice 1","id":3,"lines":[{"account":1,"amount":"40.00","currency":"USD","side":"debit"},{"account":3,"amount":"40.00","currency":"USD","side":"credit"}],"reversal_of":null},"prev":"bba5c27982f933e44e00b50601f14c754ab8050a5ab602655e1ac46f1f2e76fd","seq":21,"ts":"2026-10-27T10:05:35Z","type":"entry.posted"}
|
||||
{"payload":{"amount":"40.00","cash_account":1,"date":"2026-02-20","entry_id":3,"invoice_id":1},"prev":"2a36c0bba5c9c87cfdf7a4d311ad9c459bbdb758f29b68de966baec8e52ac0af","seq":22,"ts":"2026-10-27T10:05:35Z","type":"invoice.payment_received"}
|
||||
{"payload":{"account_type":"Expense","code":"6999","currency":"USD","id":5,"name":"General Expenses","parent":null,"placeholder":false},"prev":"27a91b52e654fc0095464ebe5ba3ebbdcb6c5ab7fec68c686af7c4ed8c79c052","seq":23,"ts":"2026-10-27T10:05:35Z","type":"account.added"}
|
||||
{"payload":{"account_type":"Liability","code":"2000","currency":"USD","id":6,"name":"Accounts Payable","parent":null,"placeholder":false},"prev":"c6e3cb6713211338e5772699d220e9b9539297f93a861609df9f14cbd0443e6c","seq":24,"ts":"2026-10-27T10:05:35Z","type":"account.added"}
|
||||
{"payload":{"date":"2026-02-05","description":"Bill 1","id":4},"prev":"c353df71bd868c67f7e65c3bea31ab60fd379f4f62a4a4d5ea87770cb2dca2cc","seq":25,"ts":"2026-10-27T10:05:35Z","type":"entry.created"}
|
||||
{"payload":{"account":5,"amount":"80.00","currency":"USD","id":4,"side":"debit"},"prev":"2070852a5425af68a5cc2eac9bc8da4cbc7ded979c4984baf9dbc303aa39a5ca","seq":26,"ts":"2026-10-27T10:05:35Z","type":"entry.line_added"}
|
||||
{"payload":{"account":6,"amount":"80.00","currency":"USD","id":4,"side":"credit"},"prev":"1f437a1e3f7f16fa8b48ff50dbe8a7c0c5baff9de4e69ab47b048d6a0ebe30ba","seq":27,"ts":"2026-10-27T10:05:35Z","type":"entry.line_added"}
|
||||
{"payload":{"date":"2026-02-05","description":"Bill 1","id":4,"lines":[{"account":5,"amount":"80.00","currency":"USD","side":"debit"},{"account":6,"amount":"80.00","currency":"USD","side":"credit"}],"reversal_of":null},"prev":"c9c3750dc017acd6c3f228a7cc6bc1aa756d30b2c00cc1592d904cb0f4de9b5c","seq":28,"ts":"2026-10-27T10:05:35Z","type":"entry.posted"}
|
||||
{"payload":{"date":"2026-02-05","due_date":"2026-03-07","entry_id":4,"id":1,"lines":[{"amount":"80.00","description":"Paper"}],"vendor_id":1},"prev":"773f411c11ff7dd2f22dd8836ec7c619b6301cacb925dfcc81efbe51ffa330ee","seq":29,"ts":"2026-10-27T10:05:35Z","type":"bill.created"}
|
||||
{"payload":{"id":3,"name":"Avery Quill","roles":["member"]},"prev":"80b465670d96f7f7d82f047ac24ccd79dd7f6824b17dea607bef320208f55073","seq":30,"ts":"2026-10-27T10:05:35Z","type":"party.added"}
|
||||
{"payload":{"change":{"field":"email","value":"avery@quill.example"},"id":3},"prev":"ba2139f78a88c2ed7fc90820e17b6a87cf29d368e4c3f99c446eb49233742795","seq":31,"ts":"2026-10-27T10:05:35Z","type":"party.updated"}
|
||||
{"payload":{"change":{"field":"default_terms_as_customer","value":{"kind":{"net":15},"text":"Net 15"}},"id":3},"prev":"9721f78fbb621c5bd1bf3e93cb998bfd4b5fc093a2c8fabc95d604242da1812b","seq":32,"ts":"2026-10-27T10:05:35Z","type":"party.updated"}
|
||||
{"payload":{"changes":[{"key":"invoicing.number_prefix","value":"INV-"},{"key":"invoicing.first_number","value":7},{"key":"invoicing.default_terms","value":"Net 30"}],"source":{"decided_by":"Avery Quill","decided_on":"2026-02-01"}},"prev":"dd3fc95f2e5be0fdddabc795fa8658b06b9e557128218d99c089d52b2db83323","seq":33,"ts":"2026-10-27T10:05:35Z","type":"settings.set"}
|
||||
Reference in new issue
Block a user