Files
wanderbooks/crates/ledger-import/templates/asset_register.typ
T
swaitsandClaude Opus 5.5 2f999c6050
check / check (push) Canceled after 0s
P3: assets' posted and scheduled depreciation, their bases, and the K-1 on the tax basis (defect 13, D-41)
Book value is cost less what the books posted; the schedule's figure is
apart. An asset's acquired date comes from its entry. Contributed
property keeps its section 723 carryover basis apart from its depreciable
basis (the lower of value and adjusted basis), with its 704(c) built-in
gain and method. The K-1 worksheet reports tax-basis capital (Form 1065
Item L) beside the book figures, leaving open what only the CPA knows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01T3gXMVt7XJPi6ens1or9Dj
2026-10-21 14:04:12 -06:00

266 lines
8.5 KiB
Typst

// The fixed asset register: every asset, its cost, depreciation scheduled
// and posted, and book value, then the totals and the reconciliation to
// the general ledger, then each asset's federal tax figures beside its
// book cost, and the notes that cite the rules.
//
// Everything arrives as data in sys.inputs.data (JSON), laid out by
// ledger-import::asset_register, with amounts already split into exact
// parts. The template only typesets: no arithmetic, and no text is ever
// parsed as markup.
//
// Shape: { company, title, period, footer, columns: [12 headings],
// tax_title, tax_columns: [9 headings], empty, fmv_mark,
// lines: [{ tag, description, serial, acquired, in_service, cost,
// salvage, life, accumulated_scheduled,
// accumulated_posted, book_value, status, disposed,
// rule, rule_mark, recorded, fmv_at_contribution|null,
// fmv_recorded, contributor_adjusted_basis|null,
// tax_basis_723|null, depreciable_basis|null,
// built_in_gain|null, method|null, source|null }],
// totals: [cost, scheduled, posted, book_value],
// tax_totals: [tax_basis_723, depreciable_basis],
// missing: text|null, notes: [{ mark, text }],
// reconciliation: [{ label, amount, difference }] }
// where every amount is { digits: "1,234.56", negative }.
#let d = json(bytes(sys.inputs.data))
#let ink = luma(25)
#let quiet = luma(110)
#let hair = 0.5pt + luma(160)
#let firm = 0.6pt + ink
#set document(title: d.company + " — " + d.title, author: d.company)
#set text(font: ("Inter", "Noto Sans CJK SC", "Noto Sans Arabic UI", "Noto Sans Hebrew", "Noto Sans Thai", "Noto Sans Devanagari"), size: 8pt, fill: ink, number-width: "tabular", hyphenate: false)
#set page(
paper: "us-letter",
flipped: true,
margin: (x: 0.6in, top: 0.7in, bottom: 0.7in),
header: context {
if counter(page).get().first() > 1 {
set text(7.5pt, fill: quiet)
[#d.company · #d.title #h(1fr) #d.period]
}
},
footer: context {
set text(7.5pt, fill: quiet)
grid(
columns: (1fr, auto, 1fr),
[],
d.footer,
align(right)[Page #counter(page).display() of #counter(page).final().first()],
)
},
)
#align(center)[
#text(15pt, fill: luma(45), d.company) \
#v(4pt)
#text(10.5pt, weight: "bold", d.title) \
#v(2pt)
#d.period
]
#v(12pt)
// An amount: negatives in parentheses, the closing one's width reserved on
// every row so decimal points line up; "$" on totals.
#let amount(a, currency: false) = {
let open = if a.negative { [(] } else { [] }
let close = if a.negative { [)] } else { hide[)] }
let sign = if currency [\$] else []
[#open#sign#a.digits#close]
}
// Columns: 5-10 are numbers (right-aligned).
#let numeric = (5, 6, 7, 8, 9, 10)
#let ncols = d.columns.len()
#let full(body, ..args) = table.cell(colspan: ncols, ..args, body)
#table(
// Number columns are fixed widths (headings wrap); text takes the rest.
columns: (auto, 1.4fr, auto, auto, auto, 6.2em, 5.6em, 5.6em, 7em, 7em, 6.2em, 1.2fr),
stroke: none,
inset: (x: 3.5pt, y: 3.4pt),
align: (col, row) => (if col in numeric { right } else { left }) + (if row == 0 { bottom } else { horizon }),
table.header(
..d.columns.map(c => table.cell(
stroke: (top: firm, bottom: hair),
text(6.5pt, weight: "bold", tracking: 0.04em, upper(c)),
)),
),
..if d.lines.len() == 0 {
(full(emph(d.empty)),)
} else {
d
.lines
.map(l => {
let t(s) = if l.disposed { text(fill: quiet, s) } else { s }
let a(x) = t(amount(x))
(
t(l.tag),
t(l.description),
t(l.serial),
t(l.acquired),
t(l.in_service),
a(l.cost),
a(l.salvage),
t(l.life),
a(l.accumulated_scheduled),
a(l.accumulated_posted),
a(l.book_value),
t(l.status),
)
})
.flatten()
},
// Totals of the assets held: rule above, double rule beneath.
..{
let s = (top: firm)
let tot(x) = table.cell(stroke: s, text(weight: "bold", amount(x, currency: true)))
let blank = table.cell(stroke: s, [])
(
table.cell(stroke: s, colspan: 5, text(weight: "bold", "Total, assets held")),
tot(d.totals.at(0)),
blank,
blank,
tot(d.totals.at(1)),
tot(d.totals.at(2)),
tot(d.totals.at(3)),
blank,
)
},
full(
inset: (x: 0pt, y: 0pt),
stack(spacing: 1.6pt, line(length: 100%, stroke: firm), line(length: 100%, stroke: firm)),
),
)
// The reconciliation to the books, drawn beside the notes at the end.
#let reconciliation = block(breakable: false, {
text(weight: "semibold", "Reconciliation to the books")
v(2pt)
table(
columns: (auto, 9em),
stroke: none,
inset: (x: 4pt, y: 3.4pt),
align: (left + horizon, right + horizon),
..d
.reconciliation
.map(x => {
if x.difference {
let s = (top: hair)
(
table.cell(stroke: s, text(weight: "bold", x.label)),
table.cell(stroke: s, text(weight: "bold", amount(x.amount, currency: true))),
table.cell(colspan: 2, inset: 2pt, []),
)
} else {
(pad(left: 1.4em, x.label), amount(x.amount))
}
})
.flatten(),
)
})
// Federal tax figures, asset by asset, beside the book cost they may
// differ from; each rule marked with the number of the note that cites
// it, a value read as the book cost marked too; under the description,
// the §704(c) method and who gave the facts.
#let tax_numeric = (3, 4, 5, 6, 7, 8)
#let tcols = d.tax_columns.len()
#let maybe(x) = if x == none { [] } else { amount(x) }
#v(12pt)
#text(weight: "semibold", d.tax_title)
#v(2pt)
#table(
// Each amount column wide enough for its heading's longest word.
columns: (auto, 1fr, auto, 6.2em, 8.6em, 7.6em, 6.4em, 7.2em, 7.6em),
stroke: none,
inset: (x: 3.5pt, y: 3.4pt),
align: (col, row) => (if col in tax_numeric { right } else { left }) + (if row == 0 { bottom } else { horizon }),
table.header(
..d.tax_columns.enumerate().map(((i, c)) => table.cell(
stroke: (top: firm, bottom: hair),
text(6.5pt, weight: "bold", tracking: 0.04em, {
upper(c)
if i == tcols - 1 { super("4") }
}),
)),
),
..if d.lines.len() == 0 {
(table.cell(colspan: tcols, emph(d.empty)),)
} else {
d
.lines
.map(l => {
let t(s) = if l.disposed { text(fill: quiet, s) } else { s }
let rule = if l.recorded {
[#l.rule#super(l.rule_mark)]
} else if l.disposed {
emph(l.rule)
} else {
text(fill: rgb("#9a5b00"), emph(l.rule))
}
let fmv = if l.fmv_at_contribution == none {
[]
} else if l.fmv_recorded {
amount(l.fmv_at_contribution)
} else {
[#super(d.fmv_mark)#amount(l.fmv_at_contribution)]
}
let about = (l.method, l.source).filter(x => x != none)
(
t(l.tag),
t({
l.description
for x in about {
linebreak()
text(6.5pt, fill: quiet, x)
}
}),
t(rule),
t(amount(l.cost)),
t(fmv),
t(maybe(l.contributor_adjusted_basis)),
t(maybe(l.tax_basis_723)),
t(maybe(l.depreciable_basis)),
t(maybe(l.built_in_gain)),
)
})
.flatten()
},
..{
let s = (top: firm)
let blank = table.cell(stroke: s, [])
(
table.cell(stroke: s, colspan: 6, text(weight: "bold", "Total, assets held")),
table.cell(stroke: s, text(weight: "bold", amount(d.tax_totals.at(0), currency: true))),
table.cell(stroke: s, text(weight: "bold", amount(d.tax_totals.at(1), currency: true))),
blank,
)
},
table.cell(
colspan: tcols,
inset: (x: 0pt, y: 0pt),
stack(spacing: 1.6pt, line(length: 100%, stroke: firm), line(length: 100%, stroke: firm)),
),
)
#if d.missing != none {
v(4pt)
text(fill: rgb("#9a5b00"), emph(d.missing))
}
// Notes: book value against tax basis, and each rule's citation.
#let notes = {
set text(7.5pt, fill: luma(70))
set par(leading: 0.5em)
text(weight: "semibold", fill: ink, "Notes")
block(above: 6pt, d.notes.at(0).text)
for n in d.notes.slice(1) {
block(above: 4pt, grid(columns: (1.2em, 1fr), super(n.mark), n.text))
}
}
// The reconciliation and the notes, side by side.
#v(12pt)
#grid(columns: (auto, 1fr), column-gutter: 32pt, reconciliation, pad(top: 1pt, notes))