From ab6e4c2a56fdb40516091a87ac5ff6524d8b138f Mon Sep 17 00:00:00 2001 From: mpuckett Date: Thu, 11 Jun 2026 01:41:46 -0500 Subject: [PATCH] Form 4562 Report --- server/depreciation.js | 2 + server/services/reportEngine.js | 150 +++++++++++++++++++++++++++----- server/smoke-test.js | 10 +++ src/components/UserManual.vue | 14 ++- 4 files changed, 153 insertions(+), 23 deletions(-) diff --git a/server/depreciation.js b/server/depreciation.js index b64bb86..3412f91 100644 --- a/server/depreciation.js +++ b/server/depreciation.js @@ -605,7 +605,9 @@ function money(value) { // Export the functions for use in other modules. This allows the depreciation logic to be organized in a single module and reused across different parts of the application, such as in API endpoints or report generation. module.exports = { annualSchedule, + bonusAmount, depreciableBasis, + section179Amount, totalDepreciableBasis, getMethodRule, isMidQuarterEligible, diff --git a/server/services/reportEngine.js b/server/services/reportEngine.js index fe43187..0411d83 100644 --- a/server/services/reportEngine.js +++ b/server/services/reportEngine.js @@ -1,5 +1,5 @@ const { all, now, one, parseJson } = require('../db'); -const { annualSchedule, money } = require('../depreciation'); +const { annualSchedule, bonusAmount, depreciableBasis, getMethodRule, money, section179Amount } = require('../depreciation'); const { ruleSetForBook } = require('./ruleSets'); const { assetFromRow } = require('./assets'); @@ -453,32 +453,137 @@ function journalDisposals(options) { }; } +// MACRS property class for Form 4562, Part III line 19 (and ADS line 20), by recovery period. +function form4562Class(months) { + const y = Number(months) / 12; + if (months >= 468) return { label: '(i) Nonresidential real property', sort: 9, recovery: '39 yrs' }; + if (months >= 330) return { label: '(h) Residential rental property', sort: 8, recovery: '27.5 yrs' }; + if (y >= 24) return { label: '(g) 25-year property', sort: 7, recovery: '25 yrs' }; + if (y >= 18) return { label: '(f) 20-year property', sort: 6, recovery: '20 yrs' }; + if (y >= 13) return { label: '(e) 15-year property', sort: 5, recovery: '15 yrs' }; + if (y >= 9) return { label: '(d) 10-year property', sort: 4, recovery: '10 yrs' }; + if (y >= 6.5) return { label: '(c) 7-year property', sort: 3, recovery: '7 yrs' }; + if (y >= 4.5) return { label: '(b) 5-year property', sort: 2, recovery: '5 yrs' }; + if (y >= 2.5) return { label: '(a) 3-year property', sort: 1, recovery: '3 yrs' }; + return { label: 'Other', sort: 10, recovery: `${Math.round(y * 10) / 10} yrs` }; +} + +function form4562Method(methodRule) { + if (!methodRule) return 'S/L'; + const f = methodRule.formula; + if (f === 'macrs_declining_balance' || f === 'macrs_table' || f === 'declining_balance') { + return Number(methodRule.rateMultiplier) === 1.5 ? '150 DB' : '200 DB'; + } + if (f === 'sum_of_years_digits') return 'SYD'; + return 'S/L'; +} + +function form4562Convention(bk, methodRule, year, companyId) { + if (!methodRule || methodRule.family !== 'MACRS') return '—'; + const conv = methodRule.defaultConvention || bk.convention || 'half_year'; + if (conv === 'mid_month' || Number(bk.useful_life_months) >= 330) return 'MM'; + if (conv === 'half_year' && require('./midQuarter').applies(companyId, 'FEDERAL', year)) return 'MQ'; + return conv === 'half_year' ? 'HY' : conv; +} + +// IRS Form 4562 (Depreciation and Amortization) filing worksheet for the Federal book and tax year: +// Part I (§179 with the dollar limit, phase-out, income limitation and carryover), Part II (special/bonus +// allowance), Part III (MACRS — prior-year line 17 and current-year line 19 by property class), Part IV +// (summary total), and Part V (listed property). Computed from the asset register; assist only. function form4562(options) { const year = Number(options.year) || currentYear(); + const companyId = options.entity_id; const rules = ruleSetForBook('FEDERAL'); - const items = assetBookRows('FEDERAL', options).map(toAssetBook) - .filter(({ asset }) => yearOf(asset.in_service_date || asset.acquired_date) === year); - let section179 = 0; - let bonus = 0; - let macrs = 0; - const rows = items.map(({ asset, book: bk }) => { + const limitation = require('./section179Limitation').summary(companyId, 'FEDERAL', year, options.taxable_income); + + let line14Bonus = 0; // special (bonus) allowance, non-listed + let line16Other = 0; // other depreciation incl. ACRS / non-MACRS + let line17Prior = 0; // MACRS placed in service before this year + let line21Listed = 0; // listed property depreciation deduction + const classMap = new Map(); + const listedRows = []; + + for (const { asset, book: bk } of assetBookRows('FEDERAL', { entity_id: companyId }).map(toAssetBook)) { + const methodRule = getMethodRule(rules, bk.depreciation_method || 'straight_line'); const c = computeYear(asset, bk, rules, year); - const s179 = Math.min(Number(bk.cost ?? asset.acquisition_cost ?? 0), Number(bk.section_179_amount || 0)); - section179 += s179; - macrs += c.depreciation; - return { - asset_id: asset.asset_id, description: asset.description, method: c.methodLabel, - cost: c.cost, section_179: money(s179), depreciation: c.depreciation - }; - }); + if (!c.depreciation) continue; + const placedThisYear = yearOf(asset.in_service_date || asset.acquired_date) === year; + const s179a = placedThisYear ? section179Amount(asset, bk) : 0; + const bonusa = placedThisYear ? bonusAmount(asset, bk) : 0; + const regular = money(c.depreciation - s179a - bonusa); // MACRS/other portion (excludes §179 & bonus) + + if (asset.listed_property || asset.passenger_auto) { + line21Listed += money(regular + bonusa); + listedRows.push({ + type: asset.description, in_service: asset.in_service_date || asset.acquired_date, + business_use: `${Number(asset.business_use_percent || 100)}%`, + cost: money(c.cost), basis: money(depreciableBasis(asset, bk, methodRule)), + recovery: form4562Class(bk.useful_life_months || asset.useful_life_months).recovery, + method: `${form4562Method(methodRule)} / ${form4562Convention(bk, methodRule, year, companyId)}`, + deduction: money(regular + bonusa), section_179: money(s179a) + }); + continue; + } + + line14Bonus += bonusa; + if (methodRule.family === 'MACRS') { + if (placedThisYear) { + const cls = form4562Class(bk.useful_life_months || asset.useful_life_months); + const e = classMap.get(cls.label) || { class: cls.label, sort: cls.sort, basis: 0, recovery: cls.recovery, convention: form4562Convention(bk, methodRule, year, companyId), method: form4562Method(methodRule), deduction: 0 }; + e.basis = money(e.basis + depreciableBasis(asset, bk, methodRule)); + e.deduction = money(e.deduction + regular); + classMap.set(cls.label, e); + } else { + line17Prior += money(c.depreciation); + } + } else { + line16Other += money(c.depreciation); + } + } + + const line19Rows = [...classMap.values()].sort((a, b) => a.sort - b.sort); + const line19Total = money(line19Rows.reduce((s, r) => s + r.deduction, 0)); + const line12 = limitation.allowed_deduction; + const total = money(line12 + line14Bonus + line16Other + line17Prior + line19Total + line21Listed); + const L = (part, line, description, amount) => ({ part, line, description, amount: money(amount) }); + return { - columns: [ - col('asset_id', 'Asset ID'), col('description', 'Description'), col('method', 'Method'), - col('cost', 'Cost', 'currency'), col('section_179', '§179', 'currency'), col('depreciation', 'Depreciation', 'currency') + title: `IRS Form 4562 worksheet — Federal, ${year}`, + columns: [col('part', 'Part'), col('line', 'Line'), col('description', 'Description'), col('amount', 'Amount', 'currency')], + rows: [ + L('I', '1', 'Maximum §179 deduction', limitation.base_limit || 0), + L('I', '2', 'Total cost of §179 property placed in service', limitation.property_cost), + L('I', '3', 'Threshold cost of §179 property before reduction', limitation.phaseout_threshold || 0), + L('I', '4', 'Reduction in limitation (line 2 − line 3)', limitation.phaseout_reduction), + L('I', '5', 'Dollar limitation for the year (line 1 − line 4)', limitation.dollar_limit || 0), + L('I', '8', 'Total elected cost of §179 property', limitation.elected), + L('I', '9', 'Tentative deduction (smaller of line 5 or 8)', Math.min(limitation.dollar_limit || Infinity, limitation.elected)), + L('I', '10', 'Carryover of disallowed deduction from last year', limitation.prior_carryover), + L('I', '11', 'Business income limitation', limitation.taxable_income || 0), + L('I', '12', '§179 deduction (smaller of line 9+10 or 11)', limitation.allowed_deduction), + L('I', '13', 'Carryover of disallowed deduction to next year', limitation.carryover_to_next), + L('II', '14', 'Special depreciation allowance (bonus) — non-listed', line14Bonus), + L('II', '16', 'Other depreciation (including ACRS)', line16Other), + L('III', '17', 'MACRS for assets placed in service before this year', line17Prior), + L('III', '19', 'MACRS for assets placed in service this year (detail below)', line19Total), + L('IV', '21', 'Listed property (from Part V, detail below)', line21Listed), + L('IV', '22', 'TOTAL — add lines 12, 14, 16, 17, 19 and 21', total) ], - rows, - totals: { section_179: money(section179), bonus: money(bonus), depreciation: money(macrs), cost: money(rows.reduce((s, r) => s + r.cost, 0)) }, - meta: { note: 'Summary for IRS Form 4562 (Federal book, current-year placements). Validate against current tax law before filing.' } + meta: { + note: `IRS Form 4562 filing worksheet — Federal book, ${year}. The §179 income limitation uses the figure saved on Financial → Section 179 (override with the report parameter). Assist only; validate against current tax law before filing.`, + sections: [ + { + title: 'Part III, line 19 — MACRS property placed in service this tax year', + columns: [col('class', 'Classification'), col('basis', 'Basis for depreciation', 'currency'), col('recovery', 'Recovery'), col('convention', 'Conv.'), col('method', 'Method'), col('deduction', 'Deduction', 'currency')], + rows: line19Rows + }, + { + title: 'Part V — Listed property (autos & other listed assets)', + columns: [col('type', 'Property'), col('in_service', 'In service', 'date'), col('business_use', 'Business use'), col('cost', 'Cost', 'currency'), col('basis', 'Basis', 'currency'), col('recovery', 'Recovery'), col('method', 'Method/Conv.'), col('deduction', 'Deduction', 'currency'), col('section_179', '§179', 'currency')], + rows: listedRows + } + ] + } }; } @@ -1104,7 +1209,7 @@ const REPORTS = { cip: { title: 'Construction-in-progress', group: 'Activity', params: ['entity_id'], build: (o) => constructionInProgress(o) }, journal_depreciation: { title: 'Journal entry: depreciation', group: 'Journal entries', params: ['book', 'year', 'entity_id'], build: (o) => journalDepreciation(o) }, journal_disposals: { title: 'Journal entry: disposals', group: 'Journal entries', params: ['year', 'entity_id'], build: (o) => journalDisposals(o) }, - form_4562: { title: 'Federal Form 4562 (depreciation)', group: 'Tax', params: ['year', 'entity_id'], build: (o) => form4562(o) }, + form_4562: { title: 'Federal Form 4562 (depreciation)', group: 'Tax', params: ['year', 'taxable_income', 'entity_id'], build: (o) => form4562(o) }, form_4797: { title: 'Federal Form 4797 (sales)', group: 'Tax', params: ['year', 'entity_id'], build: (o) => form4797(o) }, amt: { title: 'AMT depreciation', group: 'Tax', params: ['year', 'entity_id'], build: (o) => depreciationExpense(o, 'AMT') }, ace: { title: 'ACE depreciation', group: 'Tax', params: ['year', 'entity_id'], build: (o) => depreciationExpense(o, 'ACE') }, @@ -1167,6 +1272,7 @@ function paramSpecs() { return { book: { label: 'Book', type: 'select', options: bookCodes, default: bookCodes[0] || 'GAAP' }, year: { label: 'Year', type: 'number', default: year }, + taxable_income: { label: 'Business taxable income (§179)', type: 'number', clearable: true, default: null }, month: { label: 'Through month', type: 'select', options: Array.from({ length: 12 }, (_, i) => i + 1), default: 12 }, txn_month: { label: 'Month (optional)', type: 'select', options: Array.from({ length: 12 }, (_, i) => i + 1), clearable: true, default: null }, startYear: { label: 'Start year', type: 'number', default: year }, diff --git a/server/smoke-test.js b/server/smoke-test.js index e5ab325..94873e7 100644 --- a/server/smoke-test.js +++ b/server/smoke-test.js @@ -2053,6 +2053,16 @@ async function main() { if (farmDisp.section_1255_recapture !== 10000) throw new Error(`§1255 recapture wrong: expected 10,000, got ${farmDisp.section_1255_recapture}`); if (farmDisp.section_1231_gain !== 28000) throw new Error(`Farm §1231 gain wrong: expected 28,000, got ${farmDisp.section_1231_gain}`); + // ---- Form 4562 filing worksheet (self-contained on a clean year) ----------- + await request('/api/assets', { method: 'POST', headers: aHeaders, body: JSON.stringify({ asset_id: `F62M-${suffix}`, description: 'Machine', category: 'Computer Equipment', acquisition_cost: 10000, acquired_date: '2035-04-01', in_service_date: '2035-04-01', useful_life_months: 60, books: [{ book_type: 'FEDERAL', active: true, depreciation_method: 'macrs_gds_200db_5', convention: 'half_year', useful_life_months: 60, bonus_percent: 0, section_179_amount: 0, cost: 10000 }] }) }); + await request('/api/assets', { method: 'POST', headers: aHeaders, body: JSON.stringify({ asset_id: `F62A-${suffix}`, description: 'Auto', category: 'Computer Equipment', acquisition_cost: 90000, acquired_date: '2035-06-01', in_service_date: '2035-06-01', useful_life_months: 60, passenger_auto: true, business_use_percent: 100, books: [{ book_type: 'FEDERAL', active: true, depreciation_method: 'macrs_gds_200db_5', convention: 'half_year', useful_life_months: 60, bonus_percent: 0, section_179_amount: 0, cost: 90000 }] }) }); + const f4562 = (await request('/api/reports/run', { method: 'POST', headers: aHeaders, body: JSON.stringify({ type: 'form_4562', options: { year: 2035 } }) })).report; + if (!f4562.rows.some((r) => r.line === '12') || !f4562.rows.some((r) => r.line === '22')) throw new Error('Form 4562 worksheet missing §179 (line 12) / total (line 22)'); + if (!f4562.meta || !Array.isArray(f4562.meta.sections) || f4562.meta.sections.length !== 2) throw new Error('Form 4562 missing Part III / Part V sections'); + const f4562Line19 = f4562.rows.find((r) => r.line === '19'); + if (!f4562Line19 || Math.abs(f4562Line19.amount - 2000) > 1) throw new Error(`Form 4562 line 19 (current-year MACRS) wrong: expected 2,000, got ${f4562Line19 && f4562Line19.amount}`); + if (!f4562.meta.sections[1].rows.some((r) => r.type === 'Auto')) throw new Error('Form 4562 Part V should list the passenger auto'); + // ---- Literal MACRS tables (MT200), true ADS recovery period, short tax year --- const deprRow = async (assetId, year) => (await request('/api/reports/run', { method: 'POST', headers: aHeaders, body: JSON.stringify({ type: 'depreciation_expense', options: { book: 'FEDERAL', year } }) })).report.rows.find((r) => r.asset_id === assetId); // #1 MT200 uses the literal Pub 946 Appendix-A percentages (5-year property year-1 = 20% = 2,000). diff --git a/src/components/UserManual.vue b/src/components/UserManual.vue index 286a485..0259608 100644 --- a/src/components/UserManual.vue +++ b/src/components/UserManual.vue @@ -449,7 +449,19 @@ disposal, revaluation, and write-off summaries.
  • DetailAsset history, a chronological timeline of every event for one asset (acquisition, edits, maintenance, notes, warranties, adjustments, and disposal).
  • +
  • Tax — AMT/ACE depreciation, personal-property tax, UBIA (§199A), and the + Form 4562 and Form 4797 filing worksheets (below).
  • + + Form 4562 worksheet. The Reports → Tax → Federal Form 4562 report builds a + line-by-line worksheet for the federal book and a chosen tax year: Part I §179 (the dollar limit, + investment phase-out, business-income limit, and carryover — enter taxable income as a report parameter or save it + on the Section 179 screen), Part II the special (bonus) allowance, Part III MACRS + split into prior-year (line 17) and current-year placements grouped by property class (line 19, with basis, recovery + period, convention and method), Part IV the total, and Part V listed property + (autos with the §280F caps). Export it to PDF/Excel/CSV. Form 4797 likewise summarizes dispositions with the + §1245/§1250/§1252/§1255 recapture. Both are filing aids — confirm against current tax law. +

    Reporting conventions: a “… journal” is a double-entry GL posting (debits = credits); a “… transaction” lists the underlying records. Revaluation reports cover @@ -1270,7 +1282,7 @@ export default { { id: 'assets', title: 'Assets — creating & managing', icon: 'mdi-archive-search-outline', keywords: 'asset register create edit import export mass edit workbench files notes tasks warranty' }, { id: 'lifecycle', title: 'Asset lifecycle', icon: 'mdi-cash-remove', keywords: 'dispose disposal sale retire gain loss lease amortization impairment adjustment write down' }, { id: 'barcodes', title: 'Barcodes & scanning', icon: 'mdi-barcode-scan', keywords: 'barcode label print scan camera tag' }, - { id: 'books', title: 'Books, depreciation & tax', icon: 'mdi-book-open-variant', keywords: 'book gaap federal depreciation general ledger gl method convention 179 bonus reports journal entry entries import export csv excel conflict merge history zone section 179 280f listed property passenger automobile luxury auto caps ads straight line mid-quarter limitation carryover taxable income macrs recapture 1245 1250 1231 form 4797 ordinary gain disposal mt200 tables appendix a short tax year proration ads recovery period farm pub 225 agriculture livestock 1252 1255 soil water conservation cost sharing 150 declining balance' }, + { id: 'books', title: 'Books, depreciation & tax', icon: 'mdi-book-open-variant', keywords: 'book gaap federal depreciation general ledger gl method convention 179 bonus reports journal entry entries import export csv excel conflict merge history zone section 179 280f listed property passenger automobile luxury auto caps ads straight line mid-quarter limitation carryover taxable income macrs recapture 1245 1250 1231 form 4797 4562 worksheet filing ordinary gain disposal mt200 tables appendix a short tax year proration ads recovery period farm pub 225 agriculture livestock 1252 1255 soil water conservation cost sharing 150 declining balance' }, { id: 'close', title: 'Period close (month & year-end)', icon: 'mdi-calendar-lock', keywords: 'close month end year end period lock reconcile subledger gl roll forward retained earnings depreciation post reopen finalize wip capitalize disposal impairment audit' }, { id: 'taxrules', title: 'Tax rule sets', icon: 'mdi-scale-balance', keywords: 'tax rules jurisdiction method limits activate version' }, { id: 'templates', title: 'Templates', icon: 'mdi-form-select', keywords: 'template defaults custom fields data entry' },