feat(bookspec): the specification text and an example for the ledger's own laws (P13 step 4)
bookspec/spec holds the first edition: the model and the five laws (00-model.md) and one file per area, each B-rule a section with its status, a MUST/MAY statement, sources pinned either to a vendored source with a verbatim quotation or to an exact external paragraph, its symbolic form, its examples and its applicability. B-39, B-40, B-43 and B-45 are reserved sections (allocated, not yet stated in rules.md); B-44 is stated in rules.md and now carries its bookspec id and CONVENTION status. Eight new examples show the laws that had none, each figure worked by hand: an unbalanced entry is refused (Pacioli ch. 36, Pub. 583), a negative or zero line is refused, a fraction of a cent is refused (ISO 4217), a child account takes its parent's type, the trial balance and the accounting equation on one small book (cash 6550.25 = loan 2000 + capital 5000 + (sales 750.50 - rent 1200.25)), a reversal leaves the original standing and a second reversal is refused, a close sweeps revenue and expenses into Retained Earnings (3999) and the closed quarter takes no late entry, and cents add exactly. The refusal vocabulary gains unbalanced, negative_amount, zero_amount, too_few_lines, finer_than_minor_unit, parent_type_mismatch, already_reversed and period_closed; a refused entry may carry a non-positive amount. The checker's spec.rs fails the build on a rule without its section, a statement without MUST/SHOULD/MAY, a pin that is not verbatim in its vendored source, examples that differ from the catalogue, or more than 60 pages. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HS8WZziDnyxunC23MUxrLp
This commit is contained in:
1 parent
055decf7e2
commit
1fbc1383cb
26 files changed
+1595
-36
No files matched your search
@@ -192,6 +192,14 @@ fn refusal(e: &LedgerError) -> String {
|
||||
LedgerError::InvalidDepreciationPeriod(_) => "invalid_depreciation_period",
|
||||
LedgerError::InvalidSchedule(_) => "invalid_schedule",
|
||||
LedgerError::UntaggedControlLine { .. } => "untagged_control_line",
|
||||
LedgerError::UnbalancedEntry { .. } => "unbalanced",
|
||||
LedgerError::TooFewLines => "too_few_lines",
|
||||
LedgerError::NegativeAmountLine => "negative_amount",
|
||||
LedgerError::ZeroAmountLine => "zero_amount",
|
||||
LedgerError::FinerThanMinorUnit { .. } => "finer_than_minor_unit",
|
||||
LedgerError::ParentTypeMismatch { .. } => "parent_type_mismatch",
|
||||
LedgerError::EntryAlreadyReversed(..) => "already_reversed",
|
||||
LedgerError::ClosedPeriod(..) => "period_closed",
|
||||
other => return format!("another refusal: {other}"),
|
||||
}
|
||||
.to_string()
|
||||
@@ -1029,7 +1037,12 @@ impl<'a> Books<'a> {
|
||||
) -> Result<(), StoreError> {
|
||||
let money = |s: &str| {
|
||||
let amount = exact(s, what);
|
||||
assert!(amount > Decimal::ZERO, "{what}: amounts are positive");
|
||||
// Only a refused entry carries an amount the books refuse
|
||||
// (the schema checks it).
|
||||
assert!(
|
||||
amount > Decimal::ZERO || e.refused.is_some(),
|
||||
"{what}: amounts are positive"
|
||||
);
|
||||
amount
|
||||
};
|
||||
let day = || when.expect("checked: dated");
|
||||
|
||||
Reference in new issue
Block a user