feat(store): draft states and discards are journaled, posted_seq is kept (P4)

Accounting reason: a draft that waits on its receipt, and one thrown
away, are facts about the books' working papers that must survive a
restart and replay identically, or a discarded draft would come back and
hold its period open. entry.draft_state_set and entry.draft_discarded are
drafts_v2's (an older version stops at the declaration instead of
misreading them); no figure moves. Schema 19 (taken at merge as the next
after the current last): entries.posted_seq, and a draft_states table
(drafts are not rows of entries, so the plan's entries.draft_state column
has nowhere to live). posted_seq is the journal line that posted each
entry (entry.posted, entry.reversed, the line that closed a period or
posted a schedule month), identical live and on replay.

8.0.6: a hand-written line of each new shape in
books_written_under_older_rules_still_open; fixture old-books/p4_before
written by 25b52580 (hand entries posted late, a reversal, a document, a
schedule month, a recurring draft, open drafts, a close) opens to the
same figures (serialized entries omit a building state, so no golden
moved); drafts_v2_is_its_own_feature; the fuzzer and GUI journal know the
types.

Tests: draft_states_replay_and_project, drafts_v2_waits_for_the_upgrade,
posted_seq_is_the_posting_lines_seq, an_older_version_stops_at_the_draft_events.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp
This commit is contained in:
swaitsandClaude Fable 5.1 committed 2026-10-29 13:55:17 -06:00
1 parent be8bcf340e
commit 28d46332e4
18 files changed
+1455 -24

No files matched your search

@@ -11,6 +11,9 @@
# the revision before P4b S5)
# cargo run --bin p2_1099 -- OUT (p2_vendor_1099: P2's 1099 marks, run
# against 9c99a0cf, the revision before P3)
# cargo run --bin p4_before -- OUT (p4_before: run against 25b52580, the
# revision before P4: hand entries posted
# late, a reversal, open drafts, a close)
# cargo run --bin expect -- OUT (expected.json beside each journal)
# and copy each journal.jsonl, blobs/ and expected.json into old-books/.
# x_member_closes was written later, the same way, by main 00226a4c
@@ -6,7 +6,7 @@ fn main() {
names.sort();
for dir in names {
let n = dir.file_name().unwrap().to_string_lossy().to_string();
let _ = std::fs::remove_file(dir.join(ledger_store::DB_FILE));
let _ = std::fs::remove_file(dir.join(ledger_store::db_file()));
let s = match ledger_store::Store::open(&dir) { Ok(s) => s, Err(e) => { println!("FAILS {n}: {e}"); continue } };
let l = s.ledger();
let entries: Vec<_> = (1..l.next_entry_id()).filter_map(|id| l.get_entry(id)).collect();
@@ -0,0 +1,106 @@
//! Writes `p4_before`: books the version before P4 (main 25b52580) wrote
//! through its Store API: hand entries (one posted late with an early
//! date), a reversal, an invoice and its payment, a schedule month, a
//! recurring entry with a generated draft, two open drafts (one with
//! lines, one empty), and a closed January. No draft state, discard or
//! posting sequence exists in that version; this version opens the same
//! journal and must report the same figures.
use chrono::NaiveDate;
use ledger_core::schedules::{Frequency, NewRecurring, NewSchedule, ScheduleConvention, ScheduleKind};
use ledger_core::{AccountType, Currency, DocumentLine, Money, Side};
use ledger_store::Store;
use rust_decimal::Decimal;
use std::path::PathBuf;
fn d(y: i32, m: u32, day: u32) -> NaiveDate {
NaiveDate::from_ymd_opt(y, m, day).unwrap()
}
fn cents(c: i64) -> Decimal {
Decimal::new(c, 2)
}
fn usd(c: i64) -> Money {
Money::new(cents(c), Currency::USD)
}
fn main() {
let root = PathBuf::from(std::env::args().nth(1).unwrap());
let dir = root.join("p4_before");
let _ = std::fs::remove_dir_all(&dir);
std::fs::create_dir_all(&dir).unwrap();
let mut s = Store::create(&dir, Currency::USD).unwrap();
let bank = s.add_account("1000", "Bank", AccountType::Asset, None, false).unwrap();
let prepaid = s.add_account("1300", "Prepaid expenses", AccountType::Asset, None, false).unwrap();
let capital = s.add_account("3000", "Capital", AccountType::Equity, None, false).unwrap();
let sales = s.add_account("4000", "Sales", AccountType::Revenue, None, false).unwrap();
let rent = s.add_account("6000", "Rent", AccountType::Expense, None, false).unwrap();
let software = s.add_account("6100", "Software", AccountType::Expense, None, false).unwrap();
let post = |s: &mut Store, date: NaiveDate, what: &str, dr: u64, cr: u64, c: i64| {
let e = s.create_entry(date, what).unwrap();
s.add_line(e, dr, Side::Debit, usd(c)).unwrap();
s.add_line(e, cr, Side::Credit, usd(c)).unwrap();
e
};
let seed = post(&mut s, d(2026, 1, 1), "Opening capital", bank, capital, 100_000);
s.post_entry(seed).unwrap();
// Drafted first, posted last, dated first: posting order is not date order.
let late = post(&mut s, d(2026, 1, 3), "Domain renewal", software, bank, 1_800);
let rent_jan = post(&mut s, d(2026, 1, 5), "January rent", rent, bank, 50_000);
s.post_entry(rent_jan).unwrap();
s.post_entry(late).unwrap();
let wrong = post(&mut s, d(2026, 1, 12), "Rent paid twice", rent, bank, 50_000);
s.post_entry(wrong).unwrap();
s.reverse_entry(wrong, d(2026, 1, 14), "Entered twice").unwrap();
// A document and its payment.
let customer = s.add_customer("Fernhill Labs").unwrap();
let inv = s
.create_invoice(
customer,
d(2026, 1, 10),
d(2026, 2, 9),
vec![DocumentLine {
description: "Design".into(),
amount: cents(120_000),
class_id: None,
account: None,
quantity: None,
unit_price: None,
}],
)
.unwrap();
s.receive_payment(inv, d(2026, 1, 20), cents(40_000), bank).unwrap();
// A schedule month.
let sched = s
.register_schedule(NewSchedule {
description: "Annual insurance".into(),
kind: ScheduleKind::PrepaidExpense,
total: usd(120_000),
start: d(2026, 1, 1),
months: 12,
balance_account: prepaid,
pl_account: rent,
convention: ScheduleConvention::MonthlyEven,
})
.unwrap();
let paid = post(&mut s, d(2026, 1, 2), "Insurance premium", prepaid, bank, 120_000);
s.post_entry(paid).unwrap();
s.post_schedule_month(sched, d(2026, 1, 31)).unwrap();
// A recurring entry with one generated draft (February).
s.register_recurring(NewRecurring {
description: "Figma".into(),
lines: vec![(software, Side::Debit, usd(1_500)), (bank, Side::Credit, usd(1_500))],
frequency: Frequency::Monthly,
start: d(2026, 2, 10),
end: None,
})
.unwrap();
s.run_recurring(d(2026, 2, 28)).unwrap();
// Two open drafts dated in February; January closes around them.
let open = post(&mut s, d(2026, 2, 12), "Coworking day pass", rent, bank, 3_500);
let _ = open;
let _empty = s.create_entry(d(2026, 2, 14), "Placeholder").unwrap();
let jan = s.create_period("2026-01", d(2026, 1, 1), d(2026, 1, 31)).unwrap();
s.close_period(jan).unwrap();
let _ = sales;
s.verify().unwrap();
println!("WROTE {:?}", dir);
}
@@ -0,0 +1,599 @@
{
"assets": [],
"bills": [],
"entries": [
{
"date": "2026-01-01",
"description": "Opening capital",
"id": 1,
"lines": [
{
"account": 1,
"amount": {
"amount": "1000.00",
"currency": [
85,
83,
68
]
},
"side": "Debit"
},
{
"account": 3,
"amount": {
"amount": "1000.00",
"currency": [
85,
83,
68
]
},
"side": "Credit"
}
],
"reversal_of": null,
"status": "Posted"
},
{
"date": "2026-01-03",
"description": "Domain renewal",
"id": 2,
"lines": [
{
"account": 6,
"amount": {
"amount": "18.00",
"currency": [
85,
83,
68
]
},
"side": "Debit"
},
{
"account": 1,
"amount": {
"amount": "18.00",
"currency": [
85,
83,
68
]
},
"side": "Credit"
}
],
"reversal_of": null,
"status": "Posted"
},
{
"date": "2026-01-05",
"description": "January rent",
"id": 3,
"lines": [
{
"account": 5,
"amount": {
"amount": "500.00",
"currency": [
85,
83,
68
]
},
"side": "Debit"
},
{
"account": 1,
"amount": {
"amount": "500.00",
"currency": [
85,
83,
68
]
},
"side": "Credit"
}
],
"reversal_of": null,
"status": "Posted"
},
{
"date": "2026-01-12",
"description": "Rent paid twice",
"id": 4,
"lines": [
{
"account": 5,
"amount": {
"amount": "500.00",
"currency": [
85,
83,
68
]
},
"side": "Debit"
},
{
"account": 1,
"amount": {
"amount": "500.00",
"currency": [
85,
83,
68
]
},
"side": "Credit"
}
],
"reversal_of": null,
"status": "Posted"
},
{
"date": "2026-01-14",
"description": "Entered twice",
"id": 5,
"lines": [
{
"account": 5,
"amount": {
"amount": "500.00",
"currency": [
85,
83,
68
]
},
"side": "Credit"
},
{
"account": 1,
"amount": {
"amount": "500.00",
"currency": [
85,
83,
68
]
},
"side": "Debit"
}
],
"reversal_of": 4,
"status": "Posted"
},
{
"date": "2026-01-10",
"description": "Invoice 1",
"id": 6,
"lines": [
{
"account": 7,
"amount": {
"amount": "1200.00",
"currency": [
85,
83,
68
]
},
"side": "Debit"
},
{
"account": 4,
"amount": {
"amount": "1200.00",
"currency": [
85,
83,
68
]
},
"side": "Credit"
}
],
"reversal_of": null,
"status": "Posted"
},
{
"date": "2026-01-20",
"description": "Payment on invoice 1",
"id": 7,
"lines": [
{
"account": 7,
"amount": {
"amount": "400.00",
"currency": [
85,
83,
68
]
},
"side": "Credit"
},
{
"account": 1,
"amount": {
"amount": "400.00",
"currency": [
85,
83,
68
]
},
"side": "Debit"
}
],
"reversal_of": null,
"status": "Posted"
},
{
"date": "2026-01-02",
"description": "Insurance premium",
"id": 8,
"lines": [
{
"account": 2,
"amount": {
"amount": "1200.00",
"currency": [
85,
83,
68
]
},
"side": "Debit"
},
{
"account": 1,
"amount": {
"amount": "1200.00",
"currency": [
85,
83,
68
]
},
"side": "Credit"
}
],
"reversal_of": null,
"status": "Posted"
},
{
"date": "2026-01-31",
"description": "Prepaid expense: Annual insurance (2026-01)",
"id": 9,
"lines": [
{
"account": 5,
"amount": {
"amount": "100.00",
"currency": [
85,
83,
68
]
},
"side": "Debit"
},
{
"account": 2,
"amount": {
"amount": "100.00",
"currency": [
85,
83,
68
]
},
"side": "Credit"
}
],
"reversal_of": null,
"status": "Posted"
},
{
"date": "2026-02-10",
"description": "Figma",
"id": 10,
"lines": [
{
"account": 6,
"amount": {
"amount": "15.00",
"currency": [
85,
83,
68
]
},
"side": "Debit"
},
{
"account": 1,
"amount": {
"amount": "15.00",
"currency": [
85,
83,
68
]
},
"side": "Credit"
}
],
"reversal_of": null,
"status": "Draft"
},
{
"date": "2026-02-12",
"description": "Coworking day pass",
"id": 11,
"lines": [
{
"account": 5,
"amount": {
"amount": "35.00",
"currency": [
85,
83,
68
]
},
"side": "Debit"
},
{
"account": 1,
"amount": {
"amount": "35.00",
"currency": [
85,
83,
68
]
},
"side": "Credit"
}
],
"reversal_of": null,
"status": "Draft"
},
{
"date": "2026-02-14",
"description": "Placeholder",
"id": 12,
"lines": [],
"reversal_of": null,
"status": "Draft"
},
{
"date": "2026-01-31",
"description": "Close period 2026-01",
"id": 13,
"lines": [
{
"account": 4,
"amount": {
"amount": "1200.00",
"currency": [
85,
83,
68
]
},
"side": "Debit"
},
{
"account": 5,
"amount": {
"amount": "600.00",
"currency": [
85,
83,
68
]
},
"side": "Credit"
},
{
"account": 6,
"amount": {
"amount": "18.00",
"currency": [
85,
83,
68
]
},
"side": "Credit"
},
{
"account": 8,
"amount": {
"amount": "582.00",
"currency": [
85,
83,
68
]
},
"side": "Credit"
}
],
"reversal_of": null,
"status": "Posted"
}
],
"entry_parties": [
[
6,
[
{
"party_id": 1,
"share": "derived"
}
]
],
[
7,
[
{
"party_id": 1,
"share": "derived"
}
]
]
],
"invoices": [
{
"amount_paid": "400.00",
"customer_id": 1,
"date": "2026-01-10",
"due_date": "2026-02-09",
"entry_id": 6,
"id": 1,
"lines": [
{
"amount": "1200.00",
"description": "Design"
}
],
"status": "Partial"
}
],
"payroll_runs": [],
"periods": [
{
"close_seq": 1,
"closing_entry": 13,
"end": "2026-01-31",
"id": 1,
"member_close": null,
"name": "2026-01",
"start": "2026-01-01",
"status": "Closed"
}
],
"recurring": [
{
"description": "Figma",
"end": null,
"frequency": "monthly",
"generated": [
"2026-02-10"
],
"id": 1,
"lines": [
[
6,
"Debit",
{
"amount": "15.00",
"currency": [
85,
83,
68
]
}
],
[
1,
"Credit",
{
"amount": "15.00",
"currency": [
85,
83,
68
]
}
]
],
"start": "2026-02-10"
}
],
"schedules": [
{
"balance_account": 2,
"description": "Annual insurance",
"id": 1,
"kind": "prepaid_expense",
"months": 12,
"pl_account": 5,
"posted": [
"2026-01-31"
],
"start": "2026-01-01",
"total": {
"amount": "1200.00",
"currency": [
85,
83,
68
]
}
}
],
"snapshot": {
"Ok": {
"balance_sheet": {
"assets": "1582",
"balanced": true,
"current_earnings": "0",
"equity": "1582",
"liabilities": "0"
},
"profit_and_loss": {
"expenses": "0",
"net_income": "0",
"revenue": "0"
},
"trial_balance": {
"lines": [
{
"code": "1000",
"credit": "318",
"debit": "0",
"name": "Bank"
},
{
"code": "1200",
"credit": "0",
"debit": "800",
"name": "Accounts Receivable"
},
{
"code": "1300",
"credit": "0",
"debit": "1100",
"name": "Prepaid expenses"
},
{
"code": "3000",
"credit": "1000",
"debit": "0",
"name": "Capital"
},
{
"code": "3999",
"credit": "582",
"debit": "0",
"name": "Retained Earnings"
}
],
"total_credits": "1900",
"total_debits": "1900"
}
}
},
"verify": {
"Ok": null
}
}
@@ -0,0 +1,52 @@
{"payload":{"base_currency":"USD"},"prev":"0000000000000000000000000000000000000000000000000000000000000000","seq":1,"ts":"2026-10-29T18:52:25Z","type":"store.created"}
{"payload":{"features":["account_renamed","asset_basis","attachment_text","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":"ca1a2545b353552615320a4e00c3d9a844be226aafef8c150f36286aa9054c3a","seq":2,"ts":"2026-10-29T18:52:25Z","type":"journal.format_set"}
{"payload":{"account_type":"Asset","code":"1000","currency":"USD","id":1,"name":"Bank","parent":null,"placeholder":false},"prev":"0f90ef6831ad98b59370bc8b6bdcf0518a79bf492955a7201e9a2953c20a5230","seq":3,"ts":"2026-10-29T18:52:25Z","type":"account.added"}
{"payload":{"account_type":"Asset","code":"1300","currency":"USD","id":2,"name":"Prepaid expenses","parent":null,"placeholder":false},"prev":"3d399d1f1a9d8880c935918e93599b4c22a9e826069ca2bc2e61c58e5dec7a33","seq":4,"ts":"2026-10-29T18:52:25Z","type":"account.added"}
{"payload":{"account_type":"Equity","code":"3000","currency":"USD","id":3,"name":"Capital","parent":null,"placeholder":false},"prev":"51014db1e5939a6052c38955c1f5f882cfc95a61d5a600af4323756cac6fe90d","seq":5,"ts":"2026-10-29T18:52:25Z","type":"account.added"}
{"payload":{"account_type":"Revenue","code":"4000","currency":"USD","id":4,"name":"Sales","parent":null,"placeholder":false},"prev":"088bc0a97fff726fd094c1429d1add5f10f9bc2fd34acc90f0b531ff6d2ce4a2","seq":6,"ts":"2026-10-29T18:52:25Z","type":"account.added"}
{"payload":{"account_type":"Expense","code":"6000","currency":"USD","id":5,"name":"Rent","parent":null,"placeholder":false},"prev":"804aa97749bca03f0d365aaf3a3f2685103b77b270f5e80716d33a7a2165e8be","seq":7,"ts":"2026-10-29T18:52:25Z","type":"account.added"}
{"payload":{"account_type":"Expense","code":"6100","currency":"USD","id":6,"name":"Software","parent":null,"placeholder":false},"prev":"3a685e245cf48a4093e15e796c2af633001dbef7262180b67f1c8d831d48f16a","seq":8,"ts":"2026-10-29T18:52:25Z","type":"account.added"}
{"payload":{"date":"2026-01-01","description":"Opening capital","id":1},"prev":"9449aedbe86967fd9133b8452dc73eb11cb4a4bc518a812727bd504695b00d03","seq":9,"ts":"2026-10-29T18:52:25Z","type":"entry.created"}
{"payload":{"account":1,"amount":"1000.00","currency":"USD","id":1,"side":"debit"},"prev":"fc27bd6075e1cc3b4ae49640c594ce05d05a7e6a579d7636d9ec40dd6ba27ac2","seq":10,"ts":"2026-10-29T18:52:25Z","type":"entry.line_added"}
{"payload":{"account":3,"amount":"1000.00","currency":"USD","id":1,"side":"credit"},"prev":"7364b7b2f0218b81181f5943dc1122bc0e7eb40ce793adf09ed801803f20fd39","seq":11,"ts":"2026-10-29T18:52:25Z","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":3,"amount":"1000.00","currency":"USD","side":"credit"}],"reversal_of":null},"prev":"9a9fdb4c1ad9eb87bb2cff9c006853468ac3e2c400d66d0cb0e4b6ef32f306f0","seq":12,"ts":"2026-10-29T18:52:25Z","type":"entry.posted"}
{"payload":{"date":"2026-01-03","description":"Domain renewal","id":2},"prev":"e31d3eb4d2e500f75126e447198b693ab74faeb8fe95375eb7bd7da3a15c5835","seq":13,"ts":"2026-10-29T18:52:25Z","type":"entry.created"}
{"payload":{"account":6,"amount":"18.00","currency":"USD","id":2,"side":"debit"},"prev":"ff0c990a5c0d76f4feba5daffa4fca11ad50e61b5937cec085295b10e1a318c2","seq":14,"ts":"2026-10-29T18:52:25Z","type":"entry.line_added"}
{"payload":{"account":1,"amount":"18.00","currency":"USD","id":2,"side":"credit"},"prev":"4e0e1ae37eed9ff1f5d1ed2cb2048d7bbd36e18125750db7389fa4e251d264c0","seq":15,"ts":"2026-10-29T18:52:25Z","type":"entry.line_added"}
{"payload":{"date":"2026-01-05","description":"January rent","id":3},"prev":"1c7051ba6b15d2c3d4cd042ac4c2f14b0b9b3d6bec69b130cf4c6fcdbba41f6f","seq":16,"ts":"2026-10-29T18:52:25Z","type":"entry.created"}
{"payload":{"account":5,"amount":"500.00","currency":"USD","id":3,"side":"debit"},"prev":"841b1ff16c452c73f44e51f4bdb19feb988c6efc69bb16b63f5451f61d6d3aea","seq":17,"ts":"2026-10-29T18:52:25Z","type":"entry.line_added"}
{"payload":{"account":1,"amount":"500.00","currency":"USD","id":3,"side":"credit"},"prev":"1162148134dd5415014e15081850edf1f4eb5563197b9a035e1d5b756e8621fe","seq":18,"ts":"2026-10-29T18:52:25Z","type":"entry.line_added"}
{"payload":{"date":"2026-01-05","description":"January rent","id":3,"lines":[{"account":5,"amount":"500.00","currency":"USD","side":"debit"},{"account":1,"amount":"500.00","currency":"USD","side":"credit"}],"reversal_of":null},"prev":"53e63c46da026e64bc071fc15bf8d56acdd6e784570d593830e50be20ccc6638","seq":19,"ts":"2026-10-29T18:52:25Z","type":"entry.posted"}
{"payload":{"date":"2026-01-03","description":"Domain renewal","id":2,"lines":[{"account":6,"amount":"18.00","currency":"USD","side":"debit"},{"account":1,"amount":"18.00","currency":"USD","side":"credit"}],"reversal_of":null},"prev":"fc8da8cc2bda1cbb6849618c16e7deecf0e8b6bde4495a8631157dc56763cf97","seq":20,"ts":"2026-10-29T18:52:25Z","type":"entry.posted"}
{"payload":{"date":"2026-01-12","description":"Rent paid twice","id":4},"prev":"f1f032ad841660ce1e440e35410909057b74e3706f6cdde35c155b1451ca276c","seq":21,"ts":"2026-10-29T18:52:25Z","type":"entry.created"}
{"payload":{"account":5,"amount":"500.00","currency":"USD","id":4,"side":"debit"},"prev":"8a55f6097f5899c708602100788624d2b2848b8ee3460eee4e70c385c7c381ac","seq":22,"ts":"2026-10-29T18:52:25Z","type":"entry.line_added"}
{"payload":{"account":1,"amount":"500.00","currency":"USD","id":4,"side":"credit"},"prev":"ad7264185501160ed54e29791a79d034bf3c3bd18d6b03fbdc46077943467509","seq":23,"ts":"2026-10-29T18:52:25Z","type":"entry.line_added"}
{"payload":{"date":"2026-01-12","description":"Rent paid twice","id":4,"lines":[{"account":5,"amount":"500.00","currency":"USD","side":"debit"},{"account":1,"amount":"500.00","currency":"USD","side":"credit"}],"reversal_of":null},"prev":"06d31261c4a8b67e758c32db49962248ef69bac732b8a2fcd8398c40af4014eb","seq":24,"ts":"2026-10-29T18:52:25Z","type":"entry.posted"}
{"payload":{"date":"2026-01-14","description":"Entered twice","id":5,"lines":[{"account":5,"amount":"500.00","currency":"USD","side":"credit"},{"account":1,"amount":"500.00","currency":"USD","side":"debit"}],"reverses":4},"prev":"134b60dee70f4bb0b495b0473454a4226cbd8dc226822f35edea80b29a56404b","seq":25,"ts":"2026-10-29T18:52:25Z","type":"entry.reversed"}
{"payload":{"id":1,"name":"Fernhill Labs"},"prev":"0ff7c902f8b02337a5657120afb9cc8a014f6ecb9d1ef5e25491008f5b866cf8","seq":26,"ts":"2026-10-29T18:52:25Z","type":"customer.added"}
{"payload":{"account_type":"Asset","code":"1200","currency":"USD","id":7,"name":"Accounts Receivable","parent":null,"placeholder":false},"prev":"0813351327020a32e7868f6ded661cea4054b1667127de7cc2546c377eef9552","seq":27,"ts":"2026-10-29T18:52:25Z","type":"account.added"}
{"payload":{"date":"2026-01-10","description":"Invoice 1","id":6},"prev":"bd6a41e1e84aed64069dba7e2352a8a4c76879049c17b6db3f45d11299f8bcba","seq":28,"ts":"2026-10-29T18:52:25Z","type":"entry.created"}
{"payload":{"account":7,"amount":"1200.00","currency":"USD","id":6,"side":"debit"},"prev":"0036f8a11b1ade230af57558fa60816a68720f43c12c5402b29a7c3fcce2bab8","seq":29,"ts":"2026-10-29T18:52:25Z","type":"entry.line_added"}
{"payload":{"account":4,"amount":"1200.00","currency":"USD","id":6,"side":"credit"},"prev":"269758ab57aa48d8b0118899e6fb7ff9a9d9ef8475b6c6b0b5c4361a03ca8f79","seq":30,"ts":"2026-10-29T18:52:25Z","type":"entry.line_added"}
{"payload":{"date":"2026-01-10","description":"Invoice 1","id":6,"lines":[{"account":7,"amount":"1200.00","currency":"USD","side":"debit"},{"account":4,"amount":"1200.00","currency":"USD","side":"credit"}],"reversal_of":null},"prev":"0b8e1803981bfca51fadc8fb42c63af4b4ba6340e24379c93322f2068381e704","seq":31,"ts":"2026-10-29T18:52:25Z","type":"entry.posted"}
{"payload":{"customer_id":1,"date":"2026-01-10","due_date":"2026-02-09","entry_id":6,"id":1,"lines":[{"amount":"1200.00","description":"Design"}]},"prev":"5de5d98ab8e3d39248dafca54f1dc9aaf5c800e0dff6d489717331249d8868e3","seq":32,"ts":"2026-10-29T18:52:25Z","type":"invoice.created"}
{"payload":{"date":"2026-01-20","description":"Payment on invoice 1","id":7},"prev":"6accfe7a188b03c2c8dfb528be8ea904c023f9bafc7d42b29b9e4a667a364063","seq":33,"ts":"2026-10-29T18:52:25Z","type":"entry.created"}
{"payload":{"account":7,"amount":"400.00","currency":"USD","id":7,"side":"credit"},"prev":"fcddbb8c0076d41480c5e26c211bc8094afe87db10935b6cec8e080a8a8b1c29","seq":34,"ts":"2026-10-29T18:52:25Z","type":"entry.line_added"}
{"payload":{"account":1,"amount":"400.00","currency":"USD","id":7,"side":"debit"},"prev":"9ae9b7df3f932d9ccda1d5365ea6527905236a8a86be04152f6966f0afc88197","seq":35,"ts":"2026-10-29T18:52:26Z","type":"entry.line_added"}
{"payload":{"date":"2026-01-20","description":"Payment on invoice 1","id":7,"lines":[{"account":7,"amount":"400.00","currency":"USD","side":"credit"},{"account":1,"amount":"400.00","currency":"USD","side":"debit"}],"reversal_of":null},"prev":"2b726496d9227cd3d465b80ef849b0b86dc5126474db5b53fc867d71d7f20912","seq":36,"ts":"2026-10-29T18:52:26Z","type":"entry.posted"}
{"payload":{"amount":"400.00","cash_account":1,"date":"2026-01-20","entry_id":7,"invoice_id":1},"prev":"3411b5a4f24686054bb04169c49acc3cd84e0751184c652a19a10504f84de963","seq":37,"ts":"2026-10-29T18:52:26Z","type":"invoice.payment_received"}
{"payload":{"balance_account":2,"convention":"monthly_even","description":"Annual insurance","id":1,"kind":"prepaid_expense","months":12,"pl_account":5,"start":"2026-01-01","total":{"amount":"1200.00","currency":"USD"}},"prev":"ab496ad04d3ee39826f86fdbc8dec256d55d3371940dd8d6e55fb0a6da7104d7","seq":38,"ts":"2026-10-29T18:52:26Z","type":"schedule.registered"}
{"payload":{"date":"2026-01-02","description":"Insurance premium","id":8},"prev":"251e429ea0265c712a6c52763f434ff7f3cd3899c67b5121c7a080d92026c8bf","seq":39,"ts":"2026-10-29T18:52:26Z","type":"entry.created"}
{"payload":{"account":2,"amount":"1200.00","currency":"USD","id":8,"side":"debit"},"prev":"1cc2e261fa3f5958d710d969cb303b3f819de4595a56b66c870844a87537b342","seq":40,"ts":"2026-10-29T18:52:26Z","type":"entry.line_added"}
{"payload":{"account":1,"amount":"1200.00","currency":"USD","id":8,"side":"credit"},"prev":"a564e19c9d4461cb6ec44a305bf24cd55f8b11e5a31361ae04efbb04d1e4d6af","seq":41,"ts":"2026-10-29T18:52:26Z","type":"entry.line_added"}
{"payload":{"date":"2026-01-02","description":"Insurance premium","id":8,"lines":[{"account":2,"amount":"1200.00","currency":"USD","side":"debit"},{"account":1,"amount":"1200.00","currency":"USD","side":"credit"}],"reversal_of":null},"prev":"0ac80761ca87f13347a692e008cb6c6ea162fd497cbd6a2ac3ac9bebc65c9cc7","seq":42,"ts":"2026-10-29T18:52:26Z","type":"entry.posted"}
{"payload":{"entry":{"date":"2026-01-31","description":"Prepaid expense: Annual insurance (2026-01)","id":9,"lines":[{"account":5,"amount":"100.00","currency":"USD","side":"debit"},{"account":2,"amount":"100.00","currency":"USD","side":"credit"}]},"month":"2026-01-31","schedule_id":1},"prev":"a16218a587e7c65ba7cbcff021682970aaa74615aa7d9a63816e9fb8302aeb12","seq":43,"ts":"2026-10-29T18:52:26Z","type":"schedule.posted"}
{"payload":{"description":"Figma","end":null,"frequency":"monthly","id":1,"lines":[{"account":6,"amount":"15.00","currency":"USD","side":"debit"},{"account":1,"amount":"15.00","currency":"USD","side":"credit"}],"start":"2026-02-10"},"prev":"d8e74296193043da842aaf460322c77a53948c282783a890534391d8e5f75353","seq":44,"ts":"2026-10-29T18:52:26Z","type":"recurring.registered"}
{"payload":{"date":"2026-02-10","entry_id":10,"recurring_id":1},"prev":"b093624d33665766fcc7f5b092505defea3480bf5252382ff411c8bd61f6e5a7","seq":45,"ts":"2026-10-29T18:52:26Z","type":"recurring.generated"}
{"payload":{"date":"2026-02-12","description":"Coworking day pass","id":11},"prev":"53f8ad448d05727f9a24938fb9782bfc452d42e55de9987d883dadde86f99f38","seq":46,"ts":"2026-10-29T18:52:26Z","type":"entry.created"}
{"payload":{"account":5,"amount":"35.00","currency":"USD","id":11,"side":"debit"},"prev":"f7bc43ea837a316981b97be04f61b7901cf34105905e0cb5a78132d65b794b70","seq":47,"ts":"2026-10-29T18:52:26Z","type":"entry.line_added"}
{"payload":{"account":1,"amount":"35.00","currency":"USD","id":11,"side":"credit"},"prev":"cde7cfa40a66d4ff68e1b61dc6e1cdc5dee175c96c6ef02e3a6b0cfe86d05a4e","seq":48,"ts":"2026-10-29T18:52:26Z","type":"entry.line_added"}
{"payload":{"date":"2026-02-14","description":"Placeholder","id":12},"prev":"1f4e4348a61307f98b998ba1ff5ceca9b1e2173607acd1196c7769d45f9b0a23","seq":49,"ts":"2026-10-29T18:52:26Z","type":"entry.created"}
{"payload":{"end":"2026-01-31","id":1,"name":"2026-01","start":"2026-01-01"},"prev":"3961bcafda6401e136f4f18f2b98f0910a075660789a0a8b452f20efae76e86d","seq":50,"ts":"2026-10-29T18:52:26Z","type":"period.created"}
{"payload":{"account_type":"Equity","code":"3999","currency":"USD","id":8,"name":"Retained Earnings","parent":null,"placeholder":false},"prev":"e9fbe28b14c3c388d82405c0e5f0f12ef2f01c5a15ab4d8fc5a0650272f9784e","seq":51,"ts":"2026-10-29T18:52:26Z","type":"account.added"}
{"payload":{"close_to":"retained_earnings","closing_entry":13,"entry":{"date":"2026-01-31","description":"Close period 2026-01","id":13,"lines":[{"account":4,"amount":"1200.00","currency":"USD","side":"debit"},{"account":5,"amount":"600.00","currency":"USD","side":"credit"},{"account":6,"amount":"18.00","currency":"USD","side":"credit"},{"account":8,"amount":"582.00","currency":"USD","side":"credit"}]},"id":1},"prev":"e6619d0b6a4f98cffae21be9da2326b0ed5bb6fde0691710c9ae28cb6863220d","seq":52,"ts":"2026-10-29T18:52:26Z","type":"period.closed"}