Fixed assets, fully featured: tags, serials, acquisition links, label sheets, the fixed asset register, and an Assets section in the viewer; fix a flaky verify
check / check (push) Canceled after 0s
check / check (push) Canceled after 0s
- core: asset tags (generated PREFIX-0001 or the human's own; unique ignoring case; never reused by another asset) and serial numbers (rules.md B-24); a journaled link from an asset to the entry that acquired it (posted, unreversed, debiting the cost account); fixed_asset_register(as_of), reconciled to the GL (unposted depreciation, unregistered cost) - store: asset.tagged, asset.serial_set, asset.acquisition_linked journaled, replayed, projected (schema v9); differs_from now also compares control roles and tag history - config: [books] asset_tag_prefix, default the company's initials without legal-form words - import: Avery 5160/8160 label sheets with QR codes (geometry vendored from Avery, docs/spec/sources/avery-5160.md); the register as XLSX and PDF - mcp: register_asset tags (and links) automatically; books.label_asset, books.asset_labels; asset tools take a tag; books.show asset / assets page / register report; export fixed_asset_register (json, csv, xlsx, pdf) and in the CPA package - viewer: Assets page (register, reconciliation), asset page (identity, schedule posted vs not, entries, documents), Fixed asset register report, global search by tag/serial/description, journal text, needs-attention items - fix: verify compared snapshot amounts as strings; a zero total summed in hash-map order could print "0" or "0.00", so agreeing books failed verify at random. Now by value (regression test) - tests: spec-blind suites for tags, register, prefix, store, MCP; label and register PDF/XLSX tests; viewer tests Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GWVo7x3UHw5kwCUaWxiejX
This commit is contained in:
1 parent
767fd469cd
commit
ee7e205f5c
53 files changed
+7872
-104
No files matched your search
+24
-4
@@ -156,20 +156,40 @@ CPA. Partial first/final months prorated by days; the final month trues up
|
||||
so the lifetime total is exactly (cost − salvage).
|
||||
|
||||
- `books.register_asset { description, purchase_date, cost, salvage_value?,
|
||||
useful_life_months, cost_account, accum_depr_account, expense_account }`
|
||||
→ `{ asset_id }`. Accounts resolved by id or code.
|
||||
- `books.asset_schedule { asset_id }`
|
||||
useful_life_months, cost_account, accum_depr_account, expense_account,
|
||||
tag?, serial? }` → `{ asset_id, tag }`. Accounts resolved by id or code.
|
||||
Without `tag`, the next free `PREFIX-0001` (`[books] asset_tag_prefix`,
|
||||
else the company name's initials without LLC/Inc; "FA" when none).
|
||||
- `books.label_asset { asset, tag?, serial?, clear_serial? }` → `{ asset_id,
|
||||
tag, serial }`: set or change the tag (unique ignoring case, never reused
|
||||
by another asset even after a retag or disposal) or the manufacturer's
|
||||
serial. `asset` is the id or current tag. Journaled as `asset.tagged`,
|
||||
`asset.serial_set`.
|
||||
- `books.asset_labels { assets?, skip? }` → `{ file_name, content_base64,
|
||||
tags }`: an Avery 5160/8160 label sheet (PDF): company, tag, QR code of
|
||||
the tag, description, serial. Default: every active tagged asset; `skip`
|
||||
reuses a partly used sheet.
|
||||
- `books.asset_schedule { asset_id | tag }`
|
||||
→ `{ status, schedule: [{ period, amount, accumulated, book_value }] }`.
|
||||
- `books.list_assets {}` → assets with status and current book values.
|
||||
- `books.run_depreciation { period: "YYYY-MM", asset_id? }`
|
||||
→ per-asset `{ entry_id, amount, already_posted }`, plus `skipped` for
|
||||
assets outside their useful life. Idempotent per asset and period.
|
||||
- `books.dispose_asset { asset_id, date, proceeds, proceeds_account,
|
||||
- `books.dispose_asset { asset_id | tag, date, proceeds, proceeds_account,
|
||||
gain_loss_account, confirmed?, receipt_attachment_ids? }` → without
|
||||
confirmation: `{ status: "preview", ... }` and posts nothing; with
|
||||
`confirmed: true`: auto-posts missing final-period depreciation, then
|
||||
posts the balanced disposal entry. Subject to receipt policy (bill of
|
||||
sale); monthly depreciation is exempt as an allocation.
|
||||
- `books.export { report: "fixed_asset_register", format: json | csv,
|
||||
as_of? }`: every asset registered by the date (tag, serial, cost,
|
||||
depreciation by schedule, book value, disposal), totals, and the
|
||||
general ledger's balances of the assets' cost and accumulated
|
||||
depreciation accounts, with `unposted_depreciation` and
|
||||
`unregistered_cost` (zero when register and books agree;
|
||||
`ledger_core::fixed_asset_register`).
|
||||
- `books.show { asset }` opens an asset in the viewer; `page: "assets"`
|
||||
and `report: "fixed_asset_register"` open the register.
|
||||
|
||||
## Tools — schedules and recurring entries
|
||||
|
||||
|
||||
+1
-1
@@ -14,7 +14,7 @@ layers differ in what they cover and in whether we may copy them:
|
||||
|---|---|---|---|
|
||||
| **Pacioli (1494), Geijsbeek translation (1914)** | The mechanics of double entry: debits equal credits, the trial balance, one money, writing only forward, correcting by opposite entry, closing into capital. No modern standard restates these; they predate every regulator. | Public domain | Vendored verbatim: `sources/pacioli-1494.md` |
|
||||
| **IRS: Internal Revenue Code, Treasury Regulations, publications, form instructions** | Records and supporting documents, retention, accounting periods and methods, information returns (1099), capitalization (de minimis, 12-month rule), partners' capital, payroll taxes, rounding on returns, return line items for tax categories (`sources/irs-tax-lines.md`) | Public domain (17 U.S.C. §105; IRS.gov's content policy) | Vendored verbatim: `sources/irs-*.md`, `sources/us-public-domain-status.md` |
|
||||
| **File formats: Intuit (QuickBooks Online bank CSV, QuickBooks Desktop IIF), ECMA-376 Office Open XML, Microsoft Excel limits** | What `ledger-import` reads and writes: bank/card CSV layouts and signs, IIF journal entries, how XLSX stores numbers and Excel's 15-digit precision | © Intuit Inc.; © Ecma International; © Microsoft | Cited with short quotes, URLs, and hashes: `sources/intuit-bank-csv.md`, `sources/intuit-iif.md`, `sources/xlsx-numbers.md` |
|
||||
| **File formats: Intuit (QuickBooks Online bank CSV, QuickBooks Desktop IIF), ECMA-376 Office Open XML, Microsoft Excel limits, Avery label sheets** | What `ledger-import` reads and writes: bank/card CSV layouts and signs, IIF journal entries, how XLSX stores numbers and Excel's 15-digit precision, asset-label sheet geometry (Avery 5160) | © Intuit Inc.; © Ecma International; © Microsoft; © Avery Products Corporation | Cited with short quotes, URLs, and hashes: `sources/intuit-bank-csv.md`, `sources/intuit-iif.md`, `sources/xlsx-numbers.md`, `sources/avery-5160.md` |
|
||||
| **FASB Accounting Standards Codification (ASC)** | Authoritative US GAAP: cash flows (ASC 230), revenue and principal vs agent (ASC 606), foreign currency (ASC 830), credit losses and aging (ASC 326), property and depreciation (ASC 360) | © Financial Accounting Foundation | Cited by paragraph with short quotes only (in `rules.md`) |
|
||||
| **FASB Concepts Statement No. 8, Chapter 4 (2021)** | Definitions of the five elements (it replaced Concepts Statement 6) | © Financial Accounting Foundation; non-authoritative | Cited with short quotes only |
|
||||
| **Textbooks (OpenStax *Principles of Accounting, Vol. 1*)** | Conventions no standard states: normal balances, closing-entry mechanics, straight-line depreciation arithmetic | CC BY-NC-SA 4.0 (incompatible with this repo's license) | Cited with short quotes and attribution only |
|
||||
|
||||
@@ -446,3 +446,21 @@ postings stay where they are.
|
||||
`check_assign_role`, `Ledger::check_renumber_account` (an assigned role
|
||||
frees its code). **Checked:** `ledger-core` `tests/spec_roles.rs`;
|
||||
`ledger-store` replay of `role.assigned`.
|
||||
|
||||
## B-24 Asset tags: one label per asset, never reused
|
||||
|
||||
**Status:** DECISION (Stephen, 2026-10-15)
|
||||
|
||||
Each fixed asset may carry a tag, the label stuck on the thing itself
|
||||
(generated `PREFIX-0001` or the human's own: 1–32 ASCII letters, digits,
|
||||
`- _ . /`), and the manufacturer's serial number. Tags are unique ignoring
|
||||
case and are never reused by another asset, even after a retag or a
|
||||
disposal, so an old label can never point at the wrong thing. Both are
|
||||
journaled. The fixed asset register lists every asset as of a date and
|
||||
reconciles to the general ledger's cost and accumulated depreciation
|
||||
accounts.
|
||||
|
||||
**Enforced:** `Ledger::set_asset_tag` / `check_asset_tag_for`,
|
||||
`assets::check_asset_tag`, `assets::check_serial`. **Checked:**
|
||||
`ledger-core` `tests/spec_asset_tags.rs`; `ledger-store`
|
||||
`tests/asset_tags.rs`; `ledger-mcp` `tests/spec_assets.rs`.
|
||||
@@ -0,0 +1,49 @@
|
||||
# Avery 5160 label sheet geometry
|
||||
Governs `ledger_import::labels` (`LabelSheet::Avery5160`, printable asset-tag sheets). Avery's pages and templates are © Avery Products Corporation, not public domain, so per the vendoring rules (docs/spec/README.md) they are cited, with short quotes only; everything not inside a `>` blockquote is our description, not Avery's words. Avery's pages are dynamic (Next.js), so the hashes are of the files as fetched at retrieval and a later fetch will differ.
|
||||
|
||||
## Avery Template 5160 (product template page)
|
||||
Source: <https://www.avery.com/templates/5160>
|
||||
Retrieved: 2026-10-15
|
||||
SHA-256 of the retrieved file (`templates_5160.html`, 90,873 bytes): `a274842ad34b3cb83e3129306df8b4f4ff51eb520c96cc263d96eff7b15c0a95`
|
||||
|
||||
> 1" x 2-5/8"
|
||||
|
||||
> 30 per sheet
|
||||
|
||||
The page links Avery's blank downloadable template for 5160 as `U-0087-01` (PDF, Word, Illustrator, InDesign).
|
||||
|
||||
## Blank labels, 1" x 2-5/8" (Presta 94200; lists 5160 as compatible)
|
||||
Source: <https://www.avery.com/blank/labels/94200>
|
||||
Retrieved: 2026-10-15
|
||||
SHA-256 of the retrieved file (`blank_labels_94200.html`, 274,099 bytes): `c4e7c7eb3428654b1244ed04da5f52ef7668c14d6703f971ea7d0be7daa95b17`
|
||||
|
||||
Avery's specification table on this page (key and value, transcribed as given):
|
||||
|
||||
| Key | Value |
|
||||
|---|---|
|
||||
| Labels per Sheet | 30 labels per sheet |
|
||||
| Size | 1" x 2-5/8" |
|
||||
| Print to the edge | No |
|
||||
| Sheet Size | 8-1/2" x 11" |
|
||||
| Margin (Top and Bottom) | 0.5" |
|
||||
| Margin (Left and Right) | 0.1875" |
|
||||
| Corner Shape | Rounded |
|
||||
|
||||
The page's `compatibleSkus` field lists `5160` first.
|
||||
|
||||
## Avery 5160 blank PDF template (`U-0087-01.pdf`)
|
||||
Source: the "PDF" download on <https://www.avery.com/templates/5160> (a signed S3 link: `https://s3.amazonaws.com/avery.dpp.projects.s3uspdownloadables/US_en/Downloadables/pdf/U-0087-01.pdf`, served as `Avery5160AddressLabels.pdf`)
|
||||
Retrieved: 2026-10-15
|
||||
SHA-256 of the retrieved file (`U-0087-01.pdf`, 675,971 bytes): `486b464c0380d41225f752dc834c5d7e6362364c1454d7ed92a77fc4164d0466`
|
||||
|
||||
One US Letter page (612 x 792 pt). We read the cell outlines from its content stream (PDF points, origin bottom-left): 30 rounded rectangles, left edges at x = 13.5, 211.5, 409.5 pt; bottom edges at y = 36, 108, ..., 684 pt (step 72); each 72 pt tall with 7.2 pt (0.1") corner radii. The outline is drawn 189.36 pt wide (2.63"); the stated label width is 2-5/8" (189 pt), and we use the stated size.
|
||||
|
||||
## What wanderbooks uses
|
||||
Derived from the figures above (1 in = 72 pt):
|
||||
|
||||
- Page: 8.5" x 11" (US Letter), portrait.
|
||||
- Label: 2.625" wide x 1" tall; 3 columns x 10 rows = 30 per sheet.
|
||||
- Top margin 0.5" (the template's top row spans y = 756..684 pt, i.e. 36 pt below the top edge); bottom margin 0.5" (11 - 0.5 - 10 x 1 = 0.5).
|
||||
- Side margin 0.1875" (13.5 pt).
|
||||
- Column pitch 2.75" (211.5 - 13.5 = 198 pt), so the horizontal gap is 0.125" (8.5 - 2 x 0.1875 - 3 x 2.625 = 0.25, over two gaps).
|
||||
- Row pitch 1" (72 pt): no vertical gap.
|
||||
Reference in new issue
Block a user