feat(arap): record an invoice's sends as invoice.sent (P4b S5)
The books never send invoices (A-4), but they must remember that one
went out: who sent it, how, to whom, when, whether it was the original
or a reminder, and the channel's own reference. That record is a fact
of the document's life, not a figure: a send moves no money, so it is
one journal line (`invoice.sent`) and no entry, and every derived
figure (outstanding, aging, DocState) is exactly as it was.
`Ledger::mark_sent` refuses a send on a void invoice (it should never
have reached the customer), a send dated before the invoice, no
recipients, and any recipient, reference or note that would put a full
TIN or control characters into the journal. Open, partial, paid and
written-off invoices can be sent (a copy for the customer's records, a
reminder).
The event is `documents_v2`'s (D-52): an older version refuses the
type rather than dropping the record. Replay restores it and the
projection keeps it in schema 16's `invoice_sends` table, so live,
replay and the viewer agree. The new old-books fixture `p4_s5_before`
was written by eeee9658 and opens to the same figures; a hand-written
send in `books_written_under_older_rules_still_open` proves an older
journal with the new line still opens.
Tests: mark_sent_waits_for_documents_v2, sends_replay_and_project,
a_send_changes_no_figure, mark_sent_on_void_refused, Op::MarkSent in
status_is_a_function_of_journal_and_date,
an_older_version_stops_at_invoice_sent.
This commit is contained in:
1 parent
ed637f0e27
commit
d1fb512e4b
16 files changed
+979
-5
No files matched your search
@@ -64,6 +64,9 @@ enum Op {
|
||||
Reverse(usize, u8),
|
||||
/// Void document `k` on day `n`.
|
||||
Void(usize, u8),
|
||||
/// Record a send of document `k` (an invoice; a bill is skipped) on
|
||||
/// day `n`, a reminder after its first send. A send moves no figure.
|
||||
MarkSent(usize, u8),
|
||||
/// Close month `m` (January to March), in order.
|
||||
Close(u8),
|
||||
/// A hand line on receivables (true) or payables: always refused.
|
||||
@@ -77,6 +80,7 @@ fn arb_op() -> impl Strategy<Value = Op> {
|
||||
.prop_map(|(k, n, c, card, r)| Op::Pay(k, n, c, card, r)),
|
||||
3 => (0usize..40, 0u8..90).prop_map(|(k, n)| Op::Reverse(k, n)),
|
||||
1 => (0usize..8, 0u8..90).prop_map(|(k, n)| Op::Void(k, n)),
|
||||
2 => (0usize..8, 0u8..90).prop_map(|(k, n)| Op::MarkSent(k, n)),
|
||||
1 => (1u8..4).prop_map(Op::Close),
|
||||
1 => (any::<bool>(), 1i64..10_000).prop_map(|(r, c)| Op::Hand(r, c)),
|
||||
]
|
||||
@@ -173,6 +177,35 @@ fn run(s: &mut Store, k: &Setup, ops: &[Op], docs: &mut Vec<DocRef>) -> Result<(
|
||||
Op::Void(i, n) => s
|
||||
.void_document(docs[i % docs.len()], day(n), "duplicate")
|
||||
.map(drop),
|
||||
Op::MarkSent(..) if docs.is_empty() => Ok(()),
|
||||
Op::MarkSent(i, n) => match docs[i % docs.len()] {
|
||||
// Bills get no sends: we receive them (plan P4b S5).
|
||||
DocRef::Bill(_) => Ok(()),
|
||||
DocRef::Invoice(id) => {
|
||||
let kind = if s
|
||||
.ledger()
|
||||
.get_invoice(id)
|
||||
.is_some_and(|i| !i.sends.is_empty())
|
||||
{
|
||||
ledger_core::arap::SendKind::Reminder
|
||||
} else {
|
||||
ledger_core::arap::SendKind::Original
|
||||
};
|
||||
s.mark_sent(
|
||||
id,
|
||||
ledger_core::arap::Sent {
|
||||
on: day(n),
|
||||
by: "agent".into(),
|
||||
via: ledger_core::arap::SentVia::Email,
|
||||
to: vec!["billing@fernhill.example".into()],
|
||||
kind,
|
||||
reference: None,
|
||||
note: None,
|
||||
},
|
||||
)
|
||||
.map(drop)
|
||||
}
|
||||
},
|
||||
Op::Close(m) => {
|
||||
let start = NaiveDate::from_ymd_opt(2026, u32::from(m), 1).unwrap();
|
||||
let end = start + chrono::Months::new(1) - chrono::Days::new(1);
|
||||
|
||||
Reference in new issue
Block a user