P3: depreciation straight-line by days through the final month, schedules by day (defects 22, 9, D-37)
The final month takes what the months before leave, spread over its days; a disposed asset stops on its disposal day. A skipped month is caught up in the month the run is made; a reversed run un-posts its month. New schedules spread by day; equal-months schedules must start on the 1st. Amounts round to the currency's own minor unit. Kani and Lean proofs; an exhaustive search over runs, reversals and disposals. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
This commit is contained in:
69 files changed
+10529
-841
No files matched your search
+111
-44
@@ -2679,6 +2679,10 @@ fn schedule_out(ledger: &Ledger, s: &ledger_core::AmortizationSchedule) -> Sched
|
||||
balance_account: code(s.balance_account),
|
||||
pl_account: code(s.pl_account),
|
||||
total: money(s.total.amount),
|
||||
convention: s.convention.key().to_string(),
|
||||
start: s.first_day().to_string(),
|
||||
end: s.last_day().map(|d| d.to_string()),
|
||||
next: s.next().ok().flatten().map(|d| d.to_string()),
|
||||
remaining: plan.as_ref().ok().and(s.remaining().ok()).map(money),
|
||||
months: plan
|
||||
.as_ref()
|
||||
@@ -3386,6 +3390,11 @@ impl BooksServer {
|
||||
return Err(format!("entry {} is not posted", p.entry_id));
|
||||
}
|
||||
let date = parse_date(&p.date)?;
|
||||
// Refused before the preview, as the post would be (an asset's
|
||||
// disposal, a run its disposal took as final, a month posted twice).
|
||||
store
|
||||
.check_reverse_entry(p.entry_id, date)
|
||||
.map_err(|e| e.to_string())?;
|
||||
// Build the preview from the flipped lines without mutating.
|
||||
let flipped: Vec<(AccountId, Side, Money)> = orig
|
||||
.lines()
|
||||
@@ -4556,7 +4565,7 @@ impl BooksServer {
|
||||
#[tool(
|
||||
name = "books.create_schedule",
|
||||
title = "Create an amortization schedule",
|
||||
description = "Spread a prepaid expense (asset expensed monthly, e.g. a year of insurance paid up front) or deferred revenue (liability earned monthly, e.g. an annual subscription collected up front) evenly over the months it covers, to the cent (the last month absorbs rounding). Record the original payment first (Dr prepaid / Cr cash, with its receipt; or Dr cash / Cr deferred revenue). Returns the monthly plan; post it with books.run_schedules.",
|
||||
description = "Spread a prepaid expense (an asset used up as time passes, e.g. a year of insurance paid up front) or deferred revenue (a liability earned as time passes, e.g. an annual subscription collected up front) over the time it covers, to the cent. convention: daily (default, by the day from start: a mid-month start posts a small first and last month, one month end more than its months) or monthly_even (equal months; starts on the 1st). Record the original payment first (Dr prepaid / Cr cash, with its receipt; or Dr cash / Cr deferred revenue). Returns the plan; post it with books.run_schedules.",
|
||||
annotations(
|
||||
read_only_hint = false,
|
||||
destructive_hint = false,
|
||||
@@ -4578,6 +4587,12 @@ impl BooksServer {
|
||||
));
|
||||
}
|
||||
};
|
||||
// The books' setting, else daily (D-11, D-37).
|
||||
let convention = match p.convention.as_deref() {
|
||||
None => store.ledger().settings().schedule_convention(),
|
||||
Some(c) => ledger_core::ScheduleConvention::parse(c)
|
||||
.ok_or_else(|| format!("convention {c:?}: expected daily or monthly_even"))?,
|
||||
};
|
||||
let currency = store.ledger().base_currency();
|
||||
let balance_account = resolve_account(&store, &p.balance_account)?;
|
||||
let pl_account = resolve_account(&store, &p.pl_account)?;
|
||||
@@ -4590,6 +4605,7 @@ impl BooksServer {
|
||||
months: p.months,
|
||||
balance_account,
|
||||
pl_account,
|
||||
convention,
|
||||
})
|
||||
.map_err(|e| e.to_string())?;
|
||||
Ok(Json(schedule_out(
|
||||
@@ -4601,7 +4617,7 @@ impl BooksServer {
|
||||
#[tool(
|
||||
name = "books.list_schedules",
|
||||
title = "List amortization schedules",
|
||||
description = "Amortization schedules (prepaid expenses, deferred revenue): each month's amount, what has posted, and what remains.",
|
||||
description = "Amortization schedules (prepaid expenses, deferred revenue): each one's convention (daily or monthly_even), the first and last day it covers, each month's amount, what has posted, the next month to post, and what remains.",
|
||||
annotations(read_only_hint = true, open_world_hint = false)
|
||||
)]
|
||||
async fn list_schedules(&self) -> Result<Json<ListSchedulesOut>, String> {
|
||||
@@ -4618,7 +4634,7 @@ impl BooksServer {
|
||||
#[tool(
|
||||
name = "books.run_schedules",
|
||||
title = "Post scheduled amortization",
|
||||
description = "Post every amortization schedule's months ending on or before `through` (default today) that haven't posted. Idempotent: a month posts once. Without confirmed=true, returns the postings it would make. Exempt from the receipt policy: the schedule is the evidence (the original payment carries the receipt).",
|
||||
description = "Post every amortization schedule's months ending on or before `through` (default today) that haven't posted. Idempotent: a month posts once; a month reversed in its own month is due again. A month in a closed period, or reversed after it ended, is listed under skipped, not posted. Without confirmed=true, returns the postings it would make. Exempt from the receipt policy: the schedule is the evidence (the original payment carries the receipt).",
|
||||
annotations(
|
||||
read_only_hint = false,
|
||||
destructive_hint = true,
|
||||
@@ -4646,6 +4662,23 @@ impl BooksServer {
|
||||
due.extend(months.into_iter().map(|(d, a)| (d, s.id, a)));
|
||||
}
|
||||
due.sort();
|
||||
// A month dated in a closed period can't post, nor one whose
|
||||
// posting was reversed after the month ended (it would count twice
|
||||
// until then): left for a human, and said, never a failed run.
|
||||
let (held, due): (Vec<_>, Vec<_>) = due
|
||||
.into_iter()
|
||||
.partition(|(d, id, _)| store.ledger().schedule_month_hold(*id, *d).is_some());
|
||||
let skipped: Vec<ScheduleSkippedOut> = held
|
||||
.into_iter()
|
||||
.map(|(d, id, _)| ScheduleSkippedOut {
|
||||
schedule_id: id,
|
||||
date: d.to_string(),
|
||||
reason: store
|
||||
.ledger()
|
||||
.schedule_month_hold(id, d)
|
||||
.unwrap_or_default(),
|
||||
})
|
||||
.collect();
|
||||
let describe = |store: &Store, id: u64, date: NaiveDate| -> Result<String, String> {
|
||||
Ok(store
|
||||
.ledger()
|
||||
@@ -4669,6 +4702,7 @@ impl BooksServer {
|
||||
return Ok(Json(RunSchedulesOut {
|
||||
status: "preview".to_string(),
|
||||
postings,
|
||||
skipped,
|
||||
}));
|
||||
}
|
||||
let mut postings = Vec::new();
|
||||
@@ -4688,6 +4722,7 @@ impl BooksServer {
|
||||
Ok(Json(RunSchedulesOut {
|
||||
status: "posted".to_string(),
|
||||
postings,
|
||||
skipped,
|
||||
}))
|
||||
}
|
||||
|
||||
@@ -6112,7 +6147,7 @@ impl BooksServer {
|
||||
#[tool(
|
||||
name = "books.asset_schedule",
|
||||
title = "Depreciation schedule",
|
||||
description = "Month-by-month straight-line depreciation schedule for an asset: period, amount, accumulated, book value. Partial first and last months are prorated by days; the lifetime total is exactly cost minus salvage.",
|
||||
description = "Month-by-month straight-line depreciation schedule for an asset: period, amount, accumulated, book value. Depreciation accrues by the day: a partial first month, the life's final month, and a month the schedule stops in (through a date, or a disposed asset's disposal day, which is held) are prorated by days; the lifetime total is exactly cost minus salvage.",
|
||||
annotations(read_only_hint = true, open_world_hint = false)
|
||||
)]
|
||||
async fn asset_schedule(
|
||||
@@ -6134,6 +6169,7 @@ impl BooksServer {
|
||||
None => NaiveDate::MAX,
|
||||
};
|
||||
let schedule = depreciation_schedule(&asset, through)
|
||||
.map_err(|e| format!("asset {asset_id}'s schedule: {e}"))?
|
||||
.into_iter()
|
||||
.map(|l| ScheduleLineOut {
|
||||
period: l.period,
|
||||
@@ -6159,7 +6195,7 @@ impl BooksServer {
|
||||
#[tool(
|
||||
name = "books.list_assets",
|
||||
title = "List fixed assets",
|
||||
description = "List all registered fixed assets with accumulated depreciation and book value as of today.",
|
||||
description = "List all registered fixed assets with accumulated depreciation by schedule and book value as of today (a disposed asset's through its disposal day). An asset whose schedule can't be computed lists with null figures and schedule_error.",
|
||||
annotations(read_only_hint = true, open_world_hint = false)
|
||||
)]
|
||||
async fn list_assets(&self) -> Result<Json<ListAssetsOut>, String> {
|
||||
@@ -6168,22 +6204,33 @@ impl BooksServer {
|
||||
let assets = store
|
||||
.ledger()
|
||||
.registered_assets()
|
||||
.map(|a| AssetSummary {
|
||||
asset_id: a.id,
|
||||
tag: a.tag.clone(),
|
||||
serial: a.serial.clone(),
|
||||
acquisition_entry: a.acquired_by,
|
||||
tax: tax_out(a),
|
||||
description: a.description.clone(),
|
||||
purchase_date: a.purchase_date.to_string(),
|
||||
cost: money(a.cost.amount),
|
||||
useful_life_months: a.useful_life_months,
|
||||
status: match a.status {
|
||||
AssetStatus::Active => "active".to_string(),
|
||||
AssetStatus::Disposed { .. } => "disposed".to_string(),
|
||||
},
|
||||
accumulated: money(accumulated_depreciation(a, today)),
|
||||
book_value: money(book_value(a, today)),
|
||||
.map(|a| {
|
||||
// One asset whose schedule can't be computed (an earlier
|
||||
// version's) lists with why, never failing the whole list.
|
||||
let figures = accumulated_depreciation(a, today)
|
||||
.and_then(|acc| Ok((acc, book_value(a, today)?)));
|
||||
let (accumulated, book_value, schedule_error) = match figures {
|
||||
Ok((acc, bv)) => (Some(money(acc)), Some(money(bv)), None),
|
||||
Err(e) => (None, None, Some(e.to_string())),
|
||||
};
|
||||
AssetSummary {
|
||||
asset_id: a.id,
|
||||
tag: a.tag.clone(),
|
||||
serial: a.serial.clone(),
|
||||
acquisition_entry: a.acquired_by,
|
||||
tax: tax_out(a),
|
||||
description: a.description.clone(),
|
||||
purchase_date: a.purchase_date.to_string(),
|
||||
cost: money(a.cost.amount),
|
||||
useful_life_months: a.useful_life_months,
|
||||
status: match a.status {
|
||||
AssetStatus::Active => "active".to_string(),
|
||||
AssetStatus::Disposed { .. } => "disposed".to_string(),
|
||||
},
|
||||
accumulated,
|
||||
book_value,
|
||||
schedule_error,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
Ok(Json(ListAssetsOut { assets }))
|
||||
@@ -6192,7 +6239,7 @@ impl BooksServer {
|
||||
#[tool(
|
||||
name = "books.run_depreciation",
|
||||
title = "Post depreciation",
|
||||
description = "Post one month of depreciation (Dr Depreciation Expense / Cr Accumulated Depreciation) for one asset, or for every active asset when asset_id is omitted. Idempotent: re-running a posted period returns the original entry instead of double-posting. Depreciation is an allocation, not money movement, so it is exempt from the receipt policy; the registered asset and its schedule are the evidence.",
|
||||
description = "Post one month of depreciation (Dr Depreciation Expense / Cr Accumulated Depreciation) for one asset, or for every active asset when asset_id is omitted: what the schedule says is due through the month's end, so a month skipped (or reversed) is caught up in the month run (caught_up says how much; a material miss is a restatement for the CPA, ASC 250). Idempotent: re-running a posted period returns the original entry. Nothing due (rounds to nothing, already caught up, life over) is listed under skipped. Exempt from the receipt policy: the asset and its schedule are the evidence.",
|
||||
annotations(
|
||||
read_only_hint = false,
|
||||
destructive_hint = true,
|
||||
@@ -6226,35 +6273,49 @@ impl BooksServer {
|
||||
let mut runs = Vec::with_capacity(targets.len());
|
||||
let mut skipped = Vec::new();
|
||||
for (id, description) in targets {
|
||||
// Run-all skips assets whose depreciable life doesn't touch the
|
||||
// period instead of failing the whole batch.
|
||||
let skip = |reason: String| SkippedDepreciation {
|
||||
asset_id: id,
|
||||
description: description.clone(),
|
||||
reason,
|
||||
};
|
||||
// Run-all skips assets not yet in service, and any whose
|
||||
// schedule can't be computed (an earlier version's), instead of
|
||||
// failing the whole batch.
|
||||
if p.asset_id.is_none()
|
||||
&& let Some(asset) = store.ledger().get_asset(id)
|
||||
&& asset.purchase_date > period_end
|
||||
{
|
||||
let touches = depreciation_schedule(asset, period_end)
|
||||
.iter()
|
||||
.any(|l| l.period == p.period);
|
||||
if !touches {
|
||||
skipped.push(SkippedDepreciation {
|
||||
asset_id: id,
|
||||
description,
|
||||
reason: format!(
|
||||
"period {} is outside the asset's depreciable life",
|
||||
p.period
|
||||
),
|
||||
});
|
||||
skipped.push(skip(format!(
|
||||
"placed in service {}, after {}",
|
||||
asset.purchase_date, p.period
|
||||
)));
|
||||
continue;
|
||||
}
|
||||
let r = match store.run_depreciation(id, &p.period) {
|
||||
Ok(r) => r,
|
||||
Err(ledger_store::StoreError::Ledger(
|
||||
e @ (ledger_core::LedgerError::TooLargeToCompute(_)
|
||||
| ledger_core::LedgerError::DateOutOfRange(_)),
|
||||
)) if p.asset_id.is_none() => {
|
||||
skipped.push(skip(format!("its schedule can't be computed: {e}")));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let r = store
|
||||
.run_depreciation(id, &p.period)
|
||||
.map_err(|e| e.to_string())?;
|
||||
Err(e) => return Err(e.to_string()),
|
||||
};
|
||||
let Some(entry_id) = r.entry_id else {
|
||||
skipped
|
||||
.push(skip(r.nothing_due.unwrap_or_else(|| {
|
||||
format!("nothing is due for {}", p.period)
|
||||
})));
|
||||
continue;
|
||||
};
|
||||
runs.push(DepreciationRunOut {
|
||||
asset_id: r.asset_id,
|
||||
description,
|
||||
period: r.period,
|
||||
amount: money(r.amount),
|
||||
entry_id: r.entry_id,
|
||||
caught_up: money((r.amount - r.scheduled).max(Decimal::ZERO)),
|
||||
entry_id,
|
||||
already_posted: r.already_posted,
|
||||
});
|
||||
}
|
||||
@@ -6264,7 +6325,7 @@ impl BooksServer {
|
||||
#[tool(
|
||||
name = "books.dispose_asset",
|
||||
title = "Dispose of a fixed asset",
|
||||
description = "Dispose of a fixed asset (sell or retire). Without confirmed=true returns a preview — book value, gain/loss, the auto-posted final depreciation, and the disposal entry's lines — and posts nothing. With confirmed=true posts the disposal entry (plus any missing final-period depreciation) after enforcing the receipt policy: file the bill of sale with books.file_document first and pass its attachment ids as receipt_attachment_ids. Name the buyer (buyer: a party id or name): it is the entry's party, and [books] parties = \"require\" refuses a sale without one; a retirement with no proceeds and no buyer is marked as having no counterparty.",
|
||||
description = "Dispose of a fixed asset (sell or retire). Without confirmed=true returns a preview (book value, gain/loss, the depreciation through the disposal day, which is held, and the disposal entry's lines) and posts nothing. With confirmed=true posts it after the receipt policy: file the bill of sale with books.file_document and pass its ids as receipt_attachment_ids. Depreciation a run posted past the disposal day is credited back (excess_depreciation); a later month's run is refused (reverse it first). Name the buyer (a party id or name): [books] parties = \"require\" refuses a sale without one.",
|
||||
annotations(
|
||||
read_only_hint = false,
|
||||
destructive_hint = true,
|
||||
@@ -6317,18 +6378,22 @@ impl BooksServer {
|
||||
.map(|a| a.code.clone())
|
||||
.ok_or_else(|| format!("unknown account {id}"))
|
||||
};
|
||||
let adjusts =
|
||||
!(preview.excess_depreciation.is_zero() && preview.catch_up_depreciation.is_zero());
|
||||
let codes: Vec<String> = [
|
||||
asset.accum_depr_account,
|
||||
proceeds_account,
|
||||
asset.cost_account,
|
||||
gain_loss_account,
|
||||
]
|
||||
.iter()
|
||||
.map(|id| code_of(*id))
|
||||
.into_iter()
|
||||
.chain(adjusts.then_some(asset.expense_account))
|
||||
.map(code_of)
|
||||
.collect::<Result<_, _>>()?;
|
||||
let code_refs: Vec<&str> = codes.iter().map(|s| s.as_str()).collect();
|
||||
let debits = preview.accumulated
|
||||
+ proceeds.amount
|
||||
+ preview.catch_up_depreciation
|
||||
+ if preview.gain_loss < Decimal::ZERO {
|
||||
-preview.gain_loss
|
||||
} else {
|
||||
@@ -6348,6 +6413,8 @@ impl BooksServer {
|
||||
proceeds: money(proceeds.amount),
|
||||
gain_loss: money(preview.gain_loss),
|
||||
final_depreciation: money(preview.final_depreciation),
|
||||
excess_depreciation: money(preview.excess_depreciation),
|
||||
catch_up_depreciation: money(preview.catch_up_depreciation),
|
||||
lines: preview_lines_for(&store, &raw)?,
|
||||
receipt_required,
|
||||
};
|
||||
|
||||
@@ -115,7 +115,8 @@ pub struct CreateScheduleParams {
|
||||
pub description: String,
|
||||
/// Amount to spread, decimal string.
|
||||
pub total: String,
|
||||
/// Any date in the first month covered, YYYY-MM-DD.
|
||||
/// The first day covered, YYYY-MM-DD: any day for daily; the 1st of a
|
||||
/// month for monthly_even.
|
||||
pub start: String,
|
||||
/// Months covered.
|
||||
pub months: u32,
|
||||
@@ -123,6 +124,13 @@ pub struct CreateScheduleParams {
|
||||
pub balance_account: String,
|
||||
/// Expense or revenue account code.
|
||||
pub pl_account: String,
|
||||
/// "daily" (by the day from start, like depreciation: a mid-month
|
||||
/// start posts a small first and last month, one more month end than
|
||||
/// its months) or "monthly_even" (equal months, starting on the 1st).
|
||||
/// Default: the books' `[books] schedule_convention` setting, else
|
||||
/// daily.
|
||||
#[serde(default)]
|
||||
pub convention: Option<String>,
|
||||
}
|
||||
|
||||
/// One month of a schedule.
|
||||
@@ -151,6 +159,17 @@ pub struct ScheduleOut {
|
||||
pub pl_account: String,
|
||||
/// Total, decimal string.
|
||||
pub total: String,
|
||||
/// "daily" or "monthly_even".
|
||||
pub convention: String,
|
||||
/// The first day covered, YYYY-MM-DD (for a monthly_even schedule an
|
||||
/// earlier version took mid-month, the 1st of its month).
|
||||
pub start: String,
|
||||
/// The last day covered, YYYY-MM-DD; null past the last date the books
|
||||
/// can compute.
|
||||
pub end: Option<String>,
|
||||
/// The next month end to post, YYYY-MM-DD; null once every month has
|
||||
/// posted (or when the plan can't be computed: see `plan_error`).
|
||||
pub next: Option<String>,
|
||||
/// Not yet recognized, decimal string. Null when the plan can't be
|
||||
/// computed (see `plan_error`).
|
||||
pub remaining: Option<String>,
|
||||
@@ -194,6 +213,17 @@ pub struct SchedulePostingOut {
|
||||
pub entry_id: Option<u64>,
|
||||
}
|
||||
|
||||
/// A schedule month a run leaves unposted.
|
||||
#[derive(Debug, Serialize, JsonSchema)]
|
||||
pub struct ScheduleSkippedOut {
|
||||
/// Schedule id.
|
||||
pub schedule_id: u64,
|
||||
/// Month-end date.
|
||||
pub date: String,
|
||||
/// Why it can't post.
|
||||
pub reason: String,
|
||||
}
|
||||
|
||||
/// Result of `books.run_schedules`.
|
||||
#[derive(Debug, Serialize, JsonSchema)]
|
||||
pub struct RunSchedulesOut {
|
||||
@@ -201,6 +231,11 @@ pub struct RunSchedulesOut {
|
||||
pub status: String,
|
||||
/// Postings due (preview) or made.
|
||||
pub postings: Vec<SchedulePostingOut>,
|
||||
/// Months due that a run leaves for a human: dated in a closed period,
|
||||
/// or posted and then reversed after the month ended (posting it again
|
||||
/// would recognize it twice); a human books each in an open period by
|
||||
/// hand, or not at all.
|
||||
pub skipped: Vec<ScheduleSkippedOut>,
|
||||
}
|
||||
|
||||
/// One line of a recurring entry.
|
||||
@@ -2404,10 +2439,17 @@ pub struct AssetSummary {
|
||||
pub useful_life_months: u32,
|
||||
/// "active" or "disposed".
|
||||
pub status: String,
|
||||
/// Accumulated depreciation through today, decimal string.
|
||||
pub accumulated: String,
|
||||
/// Book value today, decimal string.
|
||||
pub book_value: String,
|
||||
/// Accumulated depreciation by its schedule through today (a disposed
|
||||
/// asset's through its disposal day), decimal string. Null when the
|
||||
/// schedule can't be computed (see `schedule_error`).
|
||||
pub accumulated: Option<String>,
|
||||
/// Book value today (cost less `accumulated`), decimal string. Null
|
||||
/// when the schedule can't be computed.
|
||||
pub book_value: Option<String>,
|
||||
/// Why `accumulated` and `book_value` are null, when they are: an
|
||||
/// asset an earlier version took whose schedule the books can't
|
||||
/// compute. It lists, and the rest of the register with it.
|
||||
pub schedule_error: Option<String>,
|
||||
}
|
||||
|
||||
/// Result of `books.list_assets`.
|
||||
@@ -2435,8 +2477,13 @@ pub struct DepreciationRunOut {
|
||||
pub description: String,
|
||||
/// Calendar month posted.
|
||||
pub period: String,
|
||||
/// Amount posted, decimal string.
|
||||
/// Amount posted, decimal string: the month's own depreciation plus
|
||||
/// any earlier month caught up.
|
||||
pub amount: String,
|
||||
/// What the run caught up from earlier months (skipped, or reversed),
|
||||
/// decimal string: the amount less the month's own depreciation, when
|
||||
/// more ("0.00" otherwise).
|
||||
pub caught_up: String,
|
||||
/// The posted entry's id.
|
||||
pub entry_id: u64,
|
||||
/// True when this period was already posted (idempotent no-op).
|
||||
@@ -2448,8 +2495,11 @@ pub struct DepreciationRunOut {
|
||||
pub struct RunDepreciationOut {
|
||||
/// One result per asset run.
|
||||
pub runs: Vec<DepreciationRunOut>,
|
||||
/// Assets skipped by a run-all because the period is outside their
|
||||
/// depreciable life.
|
||||
/// Assets with nothing posted: a run-all's assets not yet in service,
|
||||
/// and any asset with nothing due (the month rounds to nothing, a later
|
||||
/// run caught it up or its run was reversed after it ended, its life is
|
||||
/// over and fully posted, or the books carry more than its schedule),
|
||||
/// each with why.
|
||||
pub skipped: Vec<SkippedDepreciation>,
|
||||
}
|
||||
|
||||
@@ -2511,9 +2561,18 @@ pub struct DisposeAssetOut {
|
||||
pub proceeds: String,
|
||||
/// Gain (positive) or loss (negative), decimal string.
|
||||
pub gain_loss: String,
|
||||
/// Depreciation auto-posted for the final partial period, decimal string
|
||||
/// ("0.00" when the period was already posted).
|
||||
/// Depreciation auto-posted (its own entry) through the disposal day,
|
||||
/// decimal string ("0.00" when the runs already reach it).
|
||||
pub final_depreciation: String,
|
||||
/// Depreciation the runs posted past the disposal day (the disposal
|
||||
/// month's run is dated its end), taken back by a credit to
|
||||
/// depreciation expense in the disposal entry, decimal string.
|
||||
pub excess_depreciation: String,
|
||||
/// Depreciation still due through the disposal day when the disposal
|
||||
/// month already has a run (an earlier run reversed and not run
|
||||
/// again), debited to depreciation expense in the disposal entry,
|
||||
/// decimal string.
|
||||
pub catch_up_depreciation: String,
|
||||
/// The disposal entry's lines with resulting balances.
|
||||
pub lines: Vec<PreviewLine>,
|
||||
/// True when posting requires a receipt (bill of sale) first.
|
||||
|
||||
Reference in new issue
Block a user