Assets can be corrected before depreciation: books.correct_asset (cost, salvage, life, in-service date, with a reason), journaled as asset.corrected with the old figures; refused once depreciation is posted
check / check (push) Canceled after 0s

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GWVo7x3UHw5kwCUaWxiejX
This commit is contained in:
swaitsandClaude Opus 5.5 committed 2026-10-15 13:41:15 -06:00
1 parent 6e6f9d65b2
commit 27e78a0ed5
11 files changed
+289 -4

No files matched your search

+13
View File
@@ -87,6 +87,19 @@ pub struct Asset {
pub tax: Option<TaxBasis>,
}
/// Corrected economics for a registered asset ([`crate::Ledger::correct_asset`]).
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct AssetCorrection {
/// Capitalized cost.
pub cost: Money,
/// Residual value.
pub salvage_value: Money,
/// Useful life in months.
pub useful_life_months: u32,
/// In-service date (depreciation starts here).
pub purchase_date: NaiveDate,
}
/// Which rule sets an asset's federal tax basis.
///
/// ```text
+52
View File
@@ -1698,6 +1698,58 @@ impl Ledger {
tax.check().map_err(LedgerError::InvalidTaxBasis)
}
/// Correct a registered asset's economics (cost, salvage, useful life,
/// in-service date) when it was registered wrong. Its accounts, tag,
/// serial, links, and tax basis record stay.
///
/// ```text
/// GOAL: fix a registration mistake without re-registering (which would
/// burn its tag, since tags are never reused).
/// SOURCE: DECISION (Stephen, 2026-10-15).
/// INVARIANTS:
/// 1. The corrected figures pass every check a new registration does
/// (validate_new_asset).
/// 2. Only an active asset is corrected; a store also refuses once any
/// depreciation for it is posted (the posted entries would disagree
/// with the schedule; correct with entries instead).
/// 3. A refusal MUST leave the books unchanged.
/// ```
pub fn correct_asset(
&mut self,
id: AssetId,
fix: assets::AssetCorrection,
) -> Result<(), LedgerError> {
self.check_correct_asset(id, &fix)?;
let a = self.assets.get_mut(&id).expect("checked");
a.cost = fix.cost;
a.salvage_value = fix.salvage_value;
a.useful_life_months = fix.useful_life_months;
a.purchase_date = fix.purchase_date;
Ok(())
}
/// The validation behind [`Self::correct_asset`].
pub fn check_correct_asset(
&self,
id: AssetId,
fix: &assets::AssetCorrection,
) -> Result<(), LedgerError> {
let a = self.assets.get(&id).ok_or(LedgerError::UnknownAsset(id))?;
if a.status != AssetStatus::Active {
return Err(LedgerError::AssetAlreadyDisposed(id));
}
self.validate_new_asset(&NewAsset {
description: a.description.clone(),
purchase_date: fix.purchase_date,
cost: fix.cost,
salvage_value: fix.salvage_value,
useful_life_months: fix.useful_life_months,
cost_account: a.cost_account,
accum_depr_account: a.accum_depr_account,
expense_account: a.expense_account,
})
}
/// The asset whose current tag is `tag`, ignoring case.
pub fn asset_by_tag(&self, tag: &str) -> Option<&Asset> {
let id = self.asset_tags.get(&assets::tag_key(tag))?;
+3 -3
View File
@@ -43,9 +43,9 @@ pub use arap::{
Vendor, VendorId,
};
pub use assets::{
Asset, AssetId, AssetStatus, DepreciationMethod, DisposalAmounts, NewAsset, ScheduleLine,
TaxBasis, TaxBasisRule, accumulated_depreciation, book_value, depreciation_schedule,
disposal_amounts,
Asset, AssetCorrection, AssetId, AssetStatus, DepreciationMethod, DisposalAmounts, NewAsset,
ScheduleLine, TaxBasis, TaxBasisRule, accumulated_depreciation, book_value,
depreciation_schedule, disposal_amounts,
};
pub use classes::{Class, ClassId};
pub use error::LedgerError;