P3: rename accounts, one set of period defaults, tax categories warn or refuse (defects 15, 16, 8)
check / check (push) Canceled after 0s

books.rename_account; account names trimmed, bounded, refused when a
sibling's or TIN-shaped; books.show refuses what its target ignores. A
report asked for without dates covers this fiscal year so far, everywhere
(one rule for tools, descriptions and the viewer; the IIF and CPA package
keep their own dates). policy.require_tax_category warns or refuses; a
Form 4797 gain is shown apart on every return.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
This commit is contained in:
swaitsandClaude Opus 5.5 committed 2026-10-21 10:41:24 -06:00
1 parent 8255ac2103
commit 9ab003d0d1
69 files changed
+8063 -570

No files matched your search

+35 -35
View File
@@ -12,7 +12,6 @@ use crate::books::{Attention, Books, Severity};
use crate::fmt;
use crate::nav::Route;
use chrono::Datelike;
use ledger_core::reports::profit_and_loss_for_period;
use ledger_core::{AccountType, EntryStatus, PeriodStatus, vendor_1099_report};
use rust_decimal::Decimal;
@@ -45,8 +44,11 @@ fn cant_compute(what: String, why: &ledger_core::LedgerError, target: Option<Rou
/// 2. One item per: draft entry (Todo); posted entry missing a receipt
/// (Problem; four or more are one combined item); recurring entry or
/// schedule due through today (Todo); period ended and still open
/// (Todo); fiscal-year account with activity and no tax category
/// (Info); legal rule known only through a past year (Todo); 1099
/// (Todo); the revenue and expense accounts (not headers) with no tax
/// category, with activity or not (ledger_core::tax::
/// uncategorized_accounts: one item; Info, Todo under
/// policy.require_tax_category = refuse); legal rule known only
/// through a past year (Todo); 1099
/// vendor without a W-9 (Problem); overdue bills, and overdue
/// invoices (Problem, one each, with the total); money owed to
/// owners (Info, per account with a balance); the database differing
@@ -242,38 +244,36 @@ pub fn find(b: &Books) -> Vec<Attention> {
}
}
let (fy_start, _) = b.fiscal_ytd();
if let Ok(pl) = profit_and_loss_for_period(&b.ledger, fy_start, today, None) {
let uncategorized: Vec<String> = pl
.revenue
.iter()
.chain(&pl.expenses)
.filter(|l| {
b.ledger
.chart()
.find_by_code(&l.code)
.is_some_and(|a| a.tax_category.is_none())
})
.map(|l| format!("{} {}", l.code, l.name))
.collect();
if !uncategorized.is_empty() {
out.push(Attention {
severity: Severity::Info,
title: format!(
"{} without a tax category",
fmt::count(uncategorized.len(), "account", "accounts")
),
detail: format!(
"{}. The tax summary lists them as uncategorized.",
uncategorized.join(", ")
),
target: Some(Route::Accounts),
prompt: format!(
"In wanderbooks, propose a tax category for each of these accounts ({}) using books.tax_categories, and set them with books.set_tax_category once I agree.",
uncategorized.join(", ")
),
});
}
// Every revenue and expense account (not a header) without a tax
// category, with activity or not (rules.md B-32): the one list the
// digest gives the agent too.
let uncategorized: Vec<String> = ledger_core::tax::uncategorized_accounts(&b.ledger)
.into_iter()
.map(|a| format!("{} {}", a.code, a.name))
.collect();
if !uncategorized.is_empty() {
let refuse = b.ledger.settings().require_tax_category()
== ledger_core::settings::TaxCategoryPolicy::Refuse;
out.push(Attention {
severity: if refuse {
Severity::Todo
} else {
Severity::Info
},
title: format!(
"{} without a tax category",
fmt::count(uncategorized.len(), "account", "accounts")
),
detail: format!(
"{}. Each revenue and expense account maps to a line of the return (the tax summary and the CPA package list it as uncategorized until then).",
uncategorized.join(", ")
),
target: Some(Route::Accounts),
prompt: format!(
"In wanderbooks, propose a tax category for each of these accounts ({}) using books.tax_categories, and set them with books.set_tax_category once I agree.",
uncategorized.join(", ")
),
});
}
// Rules set by law that wanderbooks may no longer know (rules.md;