feat(parties): an email and default terms per side (P4b S4.2)

A customer's email is copied onto an invoice's bill-to snapshot at issue,
and a party's default terms (one as our customer, one as our vendor) are
the fallback a document's due date takes when the document itself names
none (A-13, rules.md B-39). Both are journaled as party.updated facts and
guarded by documents_v2: in books that have not declared it the write is
refused with the upgrade remedy and journaling nothing, and replay gives
the same directory back.

An email must be one plain address: one @, no spaces, no full TIN (the
party text rule already refuses nine digits). Terms are typed and already
canonical, so there is no second spelling to check. A party.updated naming
a field this version does not know is now NewerJournal, never corrupt
(B-27): an older version must say update, not damage; a known field with a
malformed value is still corrupt.
This commit is contained in:
swaits committed 2026-10-27 03:44:24 -06:00
1 parent 1fa77cff02
commit 02cb7f0254
8 files changed
+257 -12

No files matched your search

+16
View File
@@ -1603,6 +1603,9 @@ impl Store {
field: ledger_core::PartyField,
) -> Result<(), StoreError> {
self.ledger.check_update_party(id, &field)?;
if field.needs_documents_v2() {
self.ensure_feature(crate::format::DOCUMENTS_V2)?;
}
let line = self.append_journal("party.updated", json!({"id": id, "change": field}))?;
self.ledger.update_party(id, field)?;
apply_line_to_sqlite(&self.conn, &line)?;
@@ -6203,6 +6206,19 @@ fn replay_line(
}
}
"party.updated" => {
// A field this version doesn't know: a newer one wrote it
// (rules.md B-27). A known field with a bad value is damage.
if let Some(field) = p
.get("change")
.and_then(|c| c.get("field"))
.and_then(Value::as_str)
&& !ledger_core::PartyField::known_field(field)
{
return Err(crate::format::newer_content(
line,
format!("the party field {field}"),
));
}
let field: ledger_core::PartyField = serde_json::from_value(p["change"].clone())
.map_err(|_| StoreError::JournalCorrupt("party.updated change".into()))?;
ledger.update_party(get_u64(p, "id")?, field)?;