Pub 225 Compliance
This commit is contained in:
@@ -7,6 +7,12 @@
|
|||||||
// IRS Table 1 — Alphabetical Listing of Commonly Used Assets.
|
// IRS Table 1 — Alphabetical Listing of Commonly Used Assets.
|
||||||
const COMMON = [
|
const COMMON = [
|
||||||
{ description: 'Adding Machines', class_number: '00.13', gds: 5, ads: 6 },
|
{ description: 'Adding Machines', class_number: '00.13', gds: 5, ads: 6 },
|
||||||
|
// Farm land improvements & orchards/groves (IRS Pub 225 / Pub 946 farm property).
|
||||||
|
{ description: 'Water Wells (farm land improvement)', class_number: '00.3', gds: 15, ads: 20 },
|
||||||
|
{ description: 'Irrigation Systems (farm land improvement)', class_number: '00.3', gds: 15, ads: 20 },
|
||||||
|
{ description: 'Drainage Facilities (farm land improvement)', class_number: '00.3', gds: 15, ads: 20 },
|
||||||
|
{ description: 'Paved Lots / Farm Roads (land improvement)', class_number: '00.3', gds: 15, ads: 20 },
|
||||||
|
{ description: 'Orchards, Groves & Vineyards (fruit/nut trees & vines)', class_number: '00.0', gds: 10, ads: 20 },
|
||||||
{ description: 'Agricultural Machinery and Equipment', class_number: '01.1', gds: 7, ads: 10 },
|
{ description: 'Agricultural Machinery and Equipment', class_number: '01.1', gds: 7, ads: 10 },
|
||||||
{ description: 'Airplanes, Commercial', class_number: '45.0', gds: 7, ads: 12 },
|
{ description: 'Airplanes, Commercial', class_number: '45.0', gds: 7, ads: 12 },
|
||||||
{ description: 'Airplanes, Noncommercial', class_number: '00.21', gds: 5, ads: 6 },
|
{ description: 'Airplanes, Noncommercial', class_number: '00.21', gds: 5, ads: 6 },
|
||||||
|
|||||||
18
server/db.js
18
server/db.js
@@ -371,6 +371,18 @@ function initialize() {
|
|||||||
updated_at TEXT NOT NULL
|
updated_at TEXT NOT NULL
|
||||||
);
|
);
|
||||||
|
|
||||||
|
-- Short tax years (less than 12 months) prorate that year's MACRS depreciation, per company.
|
||||||
|
CREATE TABLE IF NOT EXISTS short_tax_years (
|
||||||
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
|
entity_id INTEGER REFERENCES entities(id),
|
||||||
|
tax_year INTEGER NOT NULL,
|
||||||
|
months REAL NOT NULL DEFAULT 12,
|
||||||
|
note TEXT,
|
||||||
|
updated_by INTEGER REFERENCES users(id),
|
||||||
|
updated_at TEXT NOT NULL,
|
||||||
|
UNIQUE(entity_id, tax_year)
|
||||||
|
);
|
||||||
|
|
||||||
-- §179 taxable-income limitation: per company+book+year entered income and computed carryover.
|
-- §179 taxable-income limitation: per company+book+year entered income and computed carryover.
|
||||||
CREATE TABLE IF NOT EXISTS section_179_carryover (
|
CREATE TABLE IF NOT EXISTS section_179_carryover (
|
||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
@@ -922,6 +934,12 @@ function migrate() {
|
|||||||
ensureColumn('disposals', 'ordinary_recapture', 'REAL NOT NULL DEFAULT 0');
|
ensureColumn('disposals', 'ordinary_recapture', 'REAL NOT NULL DEFAULT 0');
|
||||||
ensureColumn('disposals', 'section_1231_gain', 'REAL NOT NULL DEFAULT 0');
|
ensureColumn('disposals', 'section_1231_gain', 'REAL NOT NULL DEFAULT 0');
|
||||||
ensureColumn('disposals', 'unrecaptured_1250_gain', 'REAL NOT NULL DEFAULT 0');
|
ensureColumn('disposals', 'unrecaptured_1250_gain', 'REAL NOT NULL DEFAULT 0');
|
||||||
|
// Farm-property recapture inputs (IRS Pub 225): §175 soil/water conservation deductions (§1252) and
|
||||||
|
// §126 excluded cost-sharing payments (§1255), with the computed recapture stored on disposals.
|
||||||
|
ensureColumn('assets', 'soil_water_deductions', 'REAL NOT NULL DEFAULT 0');
|
||||||
|
ensureColumn('assets', 'cost_sharing_excluded', 'REAL NOT NULL DEFAULT 0');
|
||||||
|
ensureColumn('disposals', 'section_1252_recapture', 'REAL NOT NULL DEFAULT 0');
|
||||||
|
ensureColumn('disposals', 'section_1255_recapture', 'REAL NOT NULL DEFAULT 0');
|
||||||
// Refresh stale §179 limits seeded before the 2025 OBBBA increase (only touch the old seed values,
|
// Refresh stale §179 limits seeded before the 2025 OBBBA increase (only touch the old seed values,
|
||||||
// never a figure an administrator has deliberately raised). 2026 per IRS Pub 946 (2025 ed.).
|
// never a figure an administrator has deliberately raised). 2026 per IRS Pub 946 (2025 ed.).
|
||||||
run('UPDATE section_179_limits SET base_limit = 2500000, phaseout_threshold = 4000000 WHERE tax_year = 2025 AND base_limit < 2000000');
|
run('UPDATE section_179_limits SET base_limit = 2500000, phaseout_threshold = 4000000 WHERE tax_year = 2025 AND base_limit < 2000000');
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
salvage value, and conventions is included.
|
salvage value, and conventions is included.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const { parseJson } = require('./db');
|
const { one, parseJson } = require('./db');
|
||||||
const { buildDepreciationMethods } = require('./rule-catalog');
|
const { buildDepreciationMethods } = require('./rule-catalog');
|
||||||
const { getZone } = require('./services/zones');
|
const { getZone } = require('./services/zones');
|
||||||
const { getLimit: getSection179Limit } = require('./services/section179Limits');
|
const { getLimit: getSection179Limit } = require('./services/section179Limits');
|
||||||
@@ -233,6 +233,17 @@ function listedAdsRequired(asset, book, methodRule) {
|
|||||||
return use > 0 && use <= 0.5;
|
return use > 0 && use <= 0.5;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ADS recovery period (in months) for an asset, from its IRS asset class when available; otherwise the
|
||||||
|
// asset's configured life. Used when ADS is forced (listed property ≤50% business use).
|
||||||
|
function adsRecoveryMonths(asset, book) {
|
||||||
|
const classNumber = asset.asset_class_number;
|
||||||
|
if (classNumber) {
|
||||||
|
const row = one('SELECT ads FROM asset_classes WHERE class_number = ? AND ads IS NOT NULL AND ads > 0 ORDER BY id LIMIT 1', [String(classNumber)]);
|
||||||
|
if (row && Number(row.ads) > 0) return Math.round(Number(row.ads) * 12);
|
||||||
|
}
|
||||||
|
return Number(book.useful_life_months || asset.useful_life_months || 60);
|
||||||
|
}
|
||||||
|
|
||||||
// §280F passenger-automobile annual depreciation cap for a given recovery year (0-based index), reduced
|
// §280F passenger-automobile annual depreciation cap for a given recovery year (0-based index), reduced
|
||||||
// by business-use percentage. Returns Infinity when the asset isn't a flagged passenger auto or no cap
|
// by business-use percentage. Returns Infinity when the asset isn't a flagged passenger auto or no cap
|
||||||
// table is configured for the placed-in-service year (leaving depreciation uncapped).
|
// table is configured for the placed-in-service year (leaving depreciation uncapped).
|
||||||
@@ -430,6 +441,18 @@ function decliningBalance(asset, book, index, totalYears, multiplier, methodRule
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Rate table depreciation: (basis * rate). The basis is net of Section 179, bonus, and salvage, since those reduce the amount recoverable through standard depreciation methods. The rate is determined by the method rule's rates array based on the year index, with an optional fallback to straight-line when no rates are provided.
|
// Rate table depreciation: (basis * rate). The basis is net of Section 179, bonus, and salvage, since those reduce the amount recoverable through standard depreciation methods. The rate is determined by the method rule's rates array based on the year index, with an optional fallback to straight-line when no rates are provided.
|
||||||
|
// MACRS optional percentage tables (Pub 946 Appendix A). Applies the published half-year percentage to
|
||||||
|
// the depreciable basis when a literal table exists for the recovery period; otherwise (mid-quarter or an
|
||||||
|
// unsupported period) falls back to the equivalent declining-balance formula.
|
||||||
|
function macrsTable(asset, book, index, methodRule, convention) {
|
||||||
|
const years = Math.max(1, Math.round((methodRule.lifeMonths || book.useful_life_months || asset.useful_life_months || 60) / 12));
|
||||||
|
const table = convention === 'half_year' ? require('./macrs-tables').gdsHalfYear(years) : null;
|
||||||
|
if (!table) {
|
||||||
|
return decliningBalance(asset, book, index, years, Number(methodRule.rateMultiplier || 2), methodRule);
|
||||||
|
}
|
||||||
|
return depreciableBasis(asset, book, methodRule) * (Number(table[index] || 0) / 100);
|
||||||
|
}
|
||||||
|
|
||||||
function rateTable(asset, book, index, methodRule) {
|
function rateTable(asset, book, index, methodRule) {
|
||||||
console.log(`${moment().format()} 🕒 Calculating rate table depreciation for asset ${asset.id}, index ${index}`);
|
console.log(`${moment().format()} 🕒 Calculating rate table depreciation for asset ${asset.id}, index ${index}`);
|
||||||
const basis = depreciableBasis(asset, book, methodRule);
|
const basis = depreciableBasis(asset, book, methodRule);
|
||||||
@@ -461,8 +484,9 @@ function annualSchedule(asset, book, ruleSet, options = {}) {
|
|||||||
// or bonus allowance (the ADS recovery period uses the asset's configured life). `ignoreListedAds`
|
// or bonus allowance (the ADS recovery period uses the asset's configured life). `ignoreListedAds`
|
||||||
// computes the as-if-accelerated path used by the §280F recapture preview.
|
// computes the as-if-accelerated path used by the §280F recapture preview.
|
||||||
if (!options.ignoreListedAds && listedAdsRequired(asset, book, methodRule)) {
|
if (!options.ignoreListedAds && listedAdsRequired(asset, book, methodRule)) {
|
||||||
book = { ...book, section_179_amount: 0, bonus_percent: 0 };
|
const adsMonths = adsRecoveryMonths(asset, book);
|
||||||
methodRule = { ...methodRule, formula: 'straight_line', label: `${methodRule.label || methodRule.code} (ADS — listed ≤50% use)` };
|
book = { ...book, section_179_amount: 0, bonus_percent: 0, useful_life_months: adsMonths };
|
||||||
|
methodRule = { ...methodRule, formula: 'straight_line', lifeMonths: adsMonths, label: `${methodRule.label || methodRule.code} (ADS — listed ≤50% use)` };
|
||||||
}
|
}
|
||||||
// Apply a special-zone §168 allowance when the book has no explicit bonus of its own.
|
// Apply a special-zone §168 allowance when the book has no explicit bonus of its own.
|
||||||
if (!(Number(book.bonus_percent) > 0)) {
|
if (!(Number(book.bonus_percent) > 0)) {
|
||||||
@@ -493,7 +517,10 @@ function annualSchedule(asset, book, ruleSet, options = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
console.log(`${moment().format()} 🕒 Evaluating method rule for asset ${asset.id}, year ${year}`);
|
console.log(`${moment().format()} 🕒 Evaluating method rule for asset ${asset.id}, year ${year}`);
|
||||||
if (methodRule.formula === 'rate_table') {
|
if (methodRule.formula === 'macrs_table') {
|
||||||
|
// MACRS optional percentage tables (literal Pub 946 Appendix A) with formula fallback.
|
||||||
|
depreciation += macrsTable(asset, book, index, methodRule, conventionFor(book, methodRule, asset));
|
||||||
|
} else if (methodRule.formula === 'rate_table') {
|
||||||
// Note: for the rate table formula, we look up the rate for the current year index and apply it to the depreciable basis; if no rates are defined but a fallback to straight-line is specified, we use the straight-line formula instead with the total years determined by the method rule's lifeMonths or the book's useful_life_months. This allows for flexible handling of cases where a rate table is desired but specific rates haven't been configured, while still providing a reasonable default calculation.
|
// Note: for the rate table formula, we look up the rate for the current year index and apply it to the depreciable basis; if no rates are defined but a fallback to straight-line is specified, we use the straight-line formula instead with the total years determined by the method rule's lifeMonths or the book's useful_life_months. This allows for flexible handling of cases where a rate table is desired but specific rates haven't been configured, while still providing a reasonable default calculation.
|
||||||
console.log(`${moment().format()} 🕒 Using rate table formula for asset ${asset.id}, year ${year}`);
|
console.log(`${moment().format()} 🕒 Using rate table formula for asset ${asset.id}, year ${year}`);
|
||||||
depreciation += rateTable(asset, book, index, methodRule);
|
depreciation += rateTable(asset, book, index, methodRule);
|
||||||
@@ -529,6 +556,12 @@ function annualSchedule(asset, book, ruleSet, options = {}) {
|
|||||||
if (allowed <= 0 && remaining > 0) allowed = Math.min(cap280f, remaining);
|
if (allowed <= 0 && remaining > 0) allowed = Math.min(cap280f, remaining);
|
||||||
depreciation = allowed;
|
depreciation = allowed;
|
||||||
}
|
}
|
||||||
|
// Short tax year: prorate the year's MACRS depreciation by (months ÷ 12) for the company (simplified
|
||||||
|
// method; default fraction 1 leaves normal years unchanged). Lazy-required to avoid a load cycle.
|
||||||
|
if (index >= 0 && asset.entity_id) {
|
||||||
|
const f = require('./services/shortTaxYear').fraction(asset.entity_id, year);
|
||||||
|
if (f !== 1) depreciation *= f;
|
||||||
|
}
|
||||||
const cap = recoverableBasis(asset, book, methodRule);
|
const cap = recoverableBasis(asset, book, methodRule);
|
||||||
depreciation = Math.max(0, Math.min(depreciation, Math.max(0, cap - accumulated)));
|
depreciation = Math.max(0, Math.min(depreciation, Math.max(0, cap - accumulated)));
|
||||||
accumulated += depreciation;
|
accumulated += depreciation;
|
||||||
|
|||||||
21
server/macrs-tables.js
Normal file
21
server/macrs-tables.js
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
// IRS Publication 946, Appendix A — GDS optional depreciation tables (percentage of unadjusted basis
|
||||||
|
// deducted each recovery year), half-year convention. 3/5/7/10-year property use 200% declining balance;
|
||||||
|
// 15/20-year property use 150% declining balance. These are the published, rounded percentages used for
|
||||||
|
// the "MACRS Tables" method (MT200); the formula method reproduces them mathematically. Mid-quarter and
|
||||||
|
// other conventions fall back to the formula in the engine.
|
||||||
|
|
||||||
|
const GDS_HALF_YEAR = {
|
||||||
|
3: [33.33, 44.45, 14.81, 7.41],
|
||||||
|
5: [20.00, 32.00, 19.20, 11.52, 11.52, 5.76],
|
||||||
|
7: [14.29, 24.49, 17.49, 12.49, 8.93, 8.92, 8.93, 4.46],
|
||||||
|
10: [10.00, 18.00, 14.40, 11.52, 9.22, 7.37, 6.55, 6.55, 6.56, 6.55, 3.28],
|
||||||
|
15: [5.00, 9.50, 8.55, 7.70, 6.93, 6.23, 5.90, 5.90, 5.91, 5.90, 5.91, 5.90, 5.91, 5.90, 5.91, 2.95],
|
||||||
|
20: [3.750, 7.219, 6.677, 6.177, 5.713, 5.285, 4.888, 4.522, 4.462, 4.461, 4.462, 4.461, 4.462, 4.461, 4.462, 4.461, 4.462, 4.461, 4.462, 4.461, 2.231]
|
||||||
|
};
|
||||||
|
|
||||||
|
// Returns the published half-year percentage array for a recovery period (years), or null if unsupported.
|
||||||
|
function gdsHalfYear(years) {
|
||||||
|
return GDS_HALF_YEAR[years] || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { gdsHalfYear };
|
||||||
@@ -3,6 +3,7 @@ const { requireCapability } = require('../middleware/auth');
|
|||||||
const {
|
const {
|
||||||
deleteAdjustment,
|
deleteAdjustment,
|
||||||
previewDisposal,
|
previewDisposal,
|
||||||
|
previewSection280fRecapture,
|
||||||
recordAdjustment,
|
recordAdjustment,
|
||||||
recordDisposal,
|
recordDisposal,
|
||||||
reverseDisposal
|
reverseDisposal
|
||||||
@@ -16,6 +17,13 @@ router.post('/assets/:id/disposal/preview', (req, res) => {
|
|||||||
return res.json({ preview });
|
return res.json({ preview });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// §280F recapture preview if this listed/passenger asset's business use drops to ≤50% in the given year.
|
||||||
|
router.get('/assets/:id/section-280f-recapture', (req, res) => {
|
||||||
|
const result = previewSection280fRecapture(req.params.id, { book: req.query.book, year: req.query.year });
|
||||||
|
if (!result) return res.status(404).json({ error: 'Asset not found' });
|
||||||
|
return res.json({ recapture: result });
|
||||||
|
});
|
||||||
|
|
||||||
router.post('/assets/:id/disposals', requireCapability('finance.manage'), (req, res) => {
|
router.post('/assets/:id/disposals', requireCapability('finance.manage'), (req, res) => {
|
||||||
const result = recordDisposal(req.params.id, req.body, req.user.id);
|
const result = recordDisposal(req.params.id, req.body, req.user.id);
|
||||||
if (!result) return res.status(404).json({ error: 'Asset not found' });
|
if (!result) return res.status(404).json({ error: 'Asset not found' });
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ const assetClasses = require('../services/assetClasses');
|
|||||||
const zones = require('../services/zones');
|
const zones = require('../services/zones');
|
||||||
const section179Limits = require('../services/section179Limits');
|
const section179Limits = require('../services/section179Limits');
|
||||||
const section280fLimits = require('../services/section280fLimits');
|
const section280fLimits = require('../services/section280fLimits');
|
||||||
|
const shortTaxYear = require('../services/shortTaxYear');
|
||||||
|
|
||||||
const router = express.Router();
|
const router = express.Router();
|
||||||
|
|
||||||
@@ -96,6 +97,24 @@ router.delete('/section-280f-limits/:year', requireCapability('config.manage'),
|
|||||||
return res.status(204).end();
|
return res.status(204).end();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Short tax years (per company) — prorate that year's MACRS depreciation.
|
||||||
|
router.get('/short-tax-years', (req, res) => {
|
||||||
|
res.json({ years: shortTaxYear.list(req.companyId) });
|
||||||
|
});
|
||||||
|
|
||||||
|
router.post('/short-tax-years', requireCapability('config.manage'), (req, res, next) => {
|
||||||
|
try {
|
||||||
|
res.status(201).json({ year: shortTaxYear.save(req.companyId, req.body, req.user.id) });
|
||||||
|
} catch (error) {
|
||||||
|
next(error);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
router.delete('/short-tax-years/:year', requireCapability('config.manage'), (req, res) => {
|
||||||
|
if (!shortTaxYear.remove(req.companyId, req.params.year, req.user.id)) return res.status(404).json({ error: 'Not found' });
|
||||||
|
return res.status(204).end();
|
||||||
|
});
|
||||||
|
|
||||||
// Asset class catalog (seeded from the IRS tables, then user-managed). GET is reference
|
// Asset class catalog (seeded from the IRS tables, then user-managed). GET is reference
|
||||||
// data used by the category picker; mutations require config.manage.
|
// data used by the category picker; mutations require config.manage.
|
||||||
router.get('/asset-classes', (req, res) => {
|
router.get('/asset-classes', (req, res) => {
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ function build200Methods() {
|
|||||||
method('MF200', 'MACRS', 'MF200 — MACRS Formula, 200% DB (GDS)', 'macrs_declining_balance', {
|
method('MF200', 'MACRS', 'MF200 — MACRS Formula, 200% DB (GDS)', 'macrs_declining_balance', {
|
||||||
system: 'GDS', rateMultiplier: 2, switchToStraightLine: true, defaultConvention: 'half_year', ignoreSalvage: true
|
system: 'GDS', rateMultiplier: 2, switchToStraightLine: true, defaultConvention: 'half_year', ignoreSalvage: true
|
||||||
}),
|
}),
|
||||||
method('MT200', 'MACRS', 'MT200 — MACRS Tables, 200% DB (GDS)', 'macrs_declining_balance', {
|
method('MT200', 'MACRS', 'MT200 — MACRS Tables, 200% DB (GDS)', 'macrs_table', {
|
||||||
system: 'GDS', rateMultiplier: 2, switchToStraightLine: true, defaultConvention: 'half_year', ignoreSalvage: true, usesTables: true
|
system: 'GDS', rateMultiplier: 2, switchToStraightLine: true, defaultConvention: 'half_year', ignoreSalvage: true, usesTables: true
|
||||||
}),
|
}),
|
||||||
method('MI200', 'MACRS', 'MI200 — MACRS 200% DB, Indian Reservation', 'macrs_declining_balance', {
|
method('MI200', 'MACRS', 'MI200 — MACRS 200% DB, Indian Reservation', 'macrs_declining_balance', {
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ const assetColumns = [
|
|||||||
'passenger_auto',
|
'passenger_auto',
|
||||||
'business_use_percent',
|
'business_use_percent',
|
||||||
'investment_use_percent',
|
'investment_use_percent',
|
||||||
|
'soil_water_deductions',
|
||||||
|
'cost_sharing_excluded',
|
||||||
'disposal_date',
|
'disposal_date',
|
||||||
'disposal_price',
|
'disposal_price',
|
||||||
'disposal_expense',
|
'disposal_expense',
|
||||||
@@ -64,6 +66,8 @@ function assetFromRow(row) {
|
|||||||
...row,
|
...row,
|
||||||
listed_property: Boolean(row.listed_property),
|
listed_property: Boolean(row.listed_property),
|
||||||
passenger_auto: Boolean(row.passenger_auto),
|
passenger_auto: Boolean(row.passenger_auto),
|
||||||
|
soil_water_deductions: Number(row.soil_water_deductions || 0),
|
||||||
|
cost_sharing_excluded: Number(row.cost_sharing_excluded || 0),
|
||||||
custom_fields: parseJson(row.custom_fields, {}),
|
custom_fields: parseJson(row.custom_fields, {}),
|
||||||
books: row.books ? parseJson(row.books, []) : undefined
|
books: row.books ? parseJson(row.books, []) : undefined
|
||||||
};
|
};
|
||||||
@@ -213,6 +217,8 @@ function normalizeAssetInput(body) {
|
|||||||
input.prior_depreciation = Number(input.prior_depreciation || 0);
|
input.prior_depreciation = Number(input.prior_depreciation || 0);
|
||||||
input.business_use_percent = Number(input.business_use_percent || 100);
|
input.business_use_percent = Number(input.business_use_percent || 100);
|
||||||
input.investment_use_percent = Number(input.investment_use_percent || 0);
|
input.investment_use_percent = Number(input.investment_use_percent || 0);
|
||||||
|
input.soil_water_deductions = Number(input.soil_water_deductions || 0);
|
||||||
|
input.cost_sharing_excluded = Number(input.cost_sharing_excluded || 0);
|
||||||
input.custom_fields = json(input.custom_fields || {});
|
input.custom_fields = json(input.custom_fields || {});
|
||||||
input.listed_property = input.listed_property ? 1 : 0;
|
input.listed_property = input.listed_property ? 1 : 0;
|
||||||
input.passenger_auto = input.passenger_auto ? 1 : 0;
|
input.passenger_auto = input.passenger_auto ? 1 : 0;
|
||||||
|
|||||||
@@ -38,11 +38,34 @@ function isRealProperty(asset, book) {
|
|||||||
return months >= 330 || /real|realty|1250|building/.test(pt);
|
return months >= 330 || /real|realty|1250|building/.test(pt);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Whole years a property was held (for the §1252 / §1255 applicable-percentage schedules).
|
||||||
|
function yearsHeld(asset, disposalDate) {
|
||||||
|
const acq = asset.acquired_date || asset.in_service_date;
|
||||||
|
if (!acq) return 0;
|
||||||
|
return Math.max(0, Math.floor((new Date(`${disposalDate}T00:00:00`) - new Date(`${acq}T00:00:00`)) / (365.25 * 86400000)));
|
||||||
|
}
|
||||||
|
|
||||||
|
// §1252 applicable percentage (IRS Pub 225): 100% if farmland is disposed of within 5 years; then reduced
|
||||||
|
// 20 points per year for years 6–10; 0% after 10 years.
|
||||||
|
function section1252Percent(years) {
|
||||||
|
if (years <= 5) return 1;
|
||||||
|
return Math.max(0, 1 - 0.2 * (years - 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
// §1255 applicable percentage: 100% if held under 10 years; reduced 10 points for each year (or part) the
|
||||||
|
// property was held beyond 10 years; 0% at 20 years.
|
||||||
|
function section1255Percent(years) {
|
||||||
|
if (years < 10) return 1;
|
||||||
|
return Math.max(0, 1 - 0.1 * (years - 9));
|
||||||
|
}
|
||||||
|
|
||||||
// Characterize the gain on disposal into ordinary-income recapture vs. §1231 (capital) gain (Form 4797).
|
// Characterize the gain on disposal into ordinary-income recapture vs. §1231 (capital) gain (Form 4797).
|
||||||
// §1245: ordinary recapture = lesser of gain or total depreciation taken (incl. §179/bonus). §1250:
|
// §1245: ordinary recapture = lesser of gain or total depreciation taken (incl. §179/bonus). §1250:
|
||||||
// ordinary recapture = lesser of gain or "additional depreciation" (accelerated over straight-line); the
|
// ordinary recapture = lesser of gain or "additional depreciation" (accelerated over straight-line); the
|
||||||
// remaining gain up to straight-line taken is unrecaptured §1250 gain (a 25% capital bucket).
|
// remaining gain up to straight-line taken is unrecaptured §1250 gain (a 25% capital bucket). Farm
|
||||||
function characterizeRecapture(asset, book, rules, year, proportion, gainLoss, accumulated) {
|
// property adds §1252 (soil & water conservation, §175) and §1255 (cost-sharing, §126) ordinary recapture,
|
||||||
|
// each by an applicable percentage of the years held and limited to the remaining gain.
|
||||||
|
function characterizeRecapture(asset, book, rules, year, proportion, gainLoss, accumulated, disposalDate) {
|
||||||
const section = isRealProperty(asset, book) ? '1250' : '1245';
|
const section = isRealProperty(asset, book) ? '1250' : '1245';
|
||||||
const gain = Math.max(0, gainLoss);
|
const gain = Math.max(0, gainLoss);
|
||||||
let ordinary = 0;
|
let ordinary = 0;
|
||||||
@@ -51,7 +74,6 @@ function characterizeRecapture(asset, book, rules, year, proportion, gainLoss, a
|
|||||||
if (section === '1245') {
|
if (section === '1245') {
|
||||||
ordinary = Math.min(gain, accumulated);
|
ordinary = Math.min(gain, accumulated);
|
||||||
} else {
|
} else {
|
||||||
// Straight-line accumulated for the same period (additional depreciation = accelerated − SL).
|
|
||||||
const slBook = { ...book, depreciation_method: 'straight_line', section_179_amount: 0, bonus_percent: 0 };
|
const slBook = { ...book, depreciation_method: 'straight_line', section_179_amount: 0, bonus_percent: 0 };
|
||||||
const slRow = annualSchedule(asset, slBook, rules, { startYear: year, endYear: year })[0] || {};
|
const slRow = annualSchedule(asset, slBook, rules, { startYear: year, endYear: year })[0] || {};
|
||||||
const slAccum = money(Number(slRow.accumulated || 0) * proportion);
|
const slAccum = money(Number(slRow.accumulated || 0) * proportion);
|
||||||
@@ -60,11 +82,21 @@ function characterizeRecapture(asset, book, rules, year, proportion, gainLoss, a
|
|||||||
unrecaptured1250 = Math.min(money(gain - ordinary), slAccum);
|
unrecaptured1250 = Math.min(money(gain - ordinary), slAccum);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Farm-property recapture (§1252 / §1255), applied to the gain remaining after depreciation recapture.
|
||||||
|
const years = yearsHeld(asset, disposalDate);
|
||||||
|
let remaining = Math.max(0, gain - ordinary);
|
||||||
|
const r1252 = Math.min(remaining, money(section1252Percent(years) * Number(asset.soil_water_deductions || 0) * proportion));
|
||||||
|
remaining = Math.max(0, remaining - r1252);
|
||||||
|
const r1255 = Math.min(remaining, money(section1255Percent(years) * Number(asset.cost_sharing_excluded || 0) * proportion));
|
||||||
|
const totalOrdinary = money(ordinary + r1252 + r1255);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
recapture_section: section,
|
recapture_section: section,
|
||||||
ordinary_recapture: money(ordinary),
|
ordinary_recapture: money(ordinary),
|
||||||
unrecaptured_1250_gain: money(unrecaptured1250),
|
unrecaptured_1250_gain: money(unrecaptured1250),
|
||||||
section_1231_gain: money(gainLoss - ordinary) // negative for a §1231 loss
|
section_1252_recapture: money(r1252),
|
||||||
|
section_1255_recapture: money(r1255),
|
||||||
|
section_1231_gain: money(gainLoss - totalOrdinary) // negative for a §1231 loss
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,7 +127,7 @@ function computeDisposal(asset, input = {}) {
|
|||||||
|
|
||||||
// Recapture characterization is computed on the disposed book's depreciation (use a tax book — FEDERAL —
|
// Recapture characterization is computed on the disposed book's depreciation (use a tax book — FEDERAL —
|
||||||
// for tax recapture). "Depreciation taken" includes the impairment write-down already reflected in NBV.
|
// for tax recapture). "Depreciation taken" includes the impairment write-down already reflected in NBV.
|
||||||
const recapture = characterizeRecapture(asset, book, rules, year, proportion, gainLoss, money(accumulated + impairment));
|
const recapture = characterizeRecapture(asset, book, rules, year, proportion, gainLoss, money(accumulated + impairment), disposalDate);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
book_type: bookType,
|
book_type: bookType,
|
||||||
@@ -146,14 +178,15 @@ function recordDisposal(assetId, input, userId) {
|
|||||||
asset_id, disposal_date, method, property_type, partial, disposed_cost,
|
asset_id, disposal_date, method, property_type, partial, disposed_cost,
|
||||||
disposal_price, disposal_expense, accumulated_depreciation, net_book_value,
|
disposal_price, disposal_expense, accumulated_depreciation, net_book_value,
|
||||||
gain_loss, book_type, recapture_section, ordinary_recapture, section_1231_gain,
|
gain_loss, book_type, recapture_section, ordinary_recapture, section_1231_gain,
|
||||||
unrecaptured_1250_gain, notes, created_by, created_at
|
unrecaptured_1250_gain, section_1252_recapture, section_1255_recapture, notes, created_by, created_at
|
||||||
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
||||||
[
|
[
|
||||||
assetId, result.disposal_date, result.method, result.property_type, result.partial,
|
assetId, result.disposal_date, result.method, result.property_type, result.partial,
|
||||||
result.disposed_cost, result.disposal_price, result.disposal_expense,
|
result.disposed_cost, result.disposal_price, result.disposal_expense,
|
||||||
result.accumulated_depreciation, result.net_book_value, result.gain_loss,
|
result.accumulated_depreciation, result.net_book_value, result.gain_loss,
|
||||||
result.book_type, result.recapture_section, result.ordinary_recapture, result.section_1231_gain,
|
result.book_type, result.recapture_section, result.ordinary_recapture, result.section_1231_gain,
|
||||||
result.unrecaptured_1250_gain, input.notes || null, userId, timestamp
|
result.unrecaptured_1250_gain, result.section_1252_recapture, result.section_1255_recapture,
|
||||||
|
input.notes || null, userId, timestamp
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -296,16 +296,22 @@ function disposals(options) {
|
|||||||
disposal_date: row.disposal_date,
|
disposal_date: row.disposal_date,
|
||||||
proceeds: money(row.disposal_price - row.disposal_expense),
|
proceeds: money(row.disposal_price - row.disposal_expense),
|
||||||
net_book_value: money(row.net_book_value),
|
net_book_value: money(row.net_book_value),
|
||||||
gain_loss: money(row.gain_loss)
|
gain_loss: money(row.gain_loss),
|
||||||
|
recapture_section: row.recapture_section ? `§${row.recapture_section}` : '',
|
||||||
|
ordinary_recapture: money(row.ordinary_recapture || 0),
|
||||||
|
farm_recapture: money((row.section_1252_recapture || 0) + (row.section_1255_recapture || 0)),
|
||||||
|
section_1231_gain: money(row.section_1231_gain || 0)
|
||||||
}));
|
}));
|
||||||
return {
|
return {
|
||||||
columns: [
|
columns: [
|
||||||
col('asset_id', 'Asset ID'), col('description', 'Description'), col('method', 'Type'),
|
col('asset_id', 'Asset ID'), col('description', 'Description'), col('method', 'Type'),
|
||||||
col('disposal_date', 'Date', 'date'), col('proceeds', 'Net proceeds', 'currency'),
|
col('disposal_date', 'Date', 'date'), col('proceeds', 'Net proceeds', 'currency'),
|
||||||
col('net_book_value', 'Net book value', 'currency'), col('gain_loss', 'Gain / (loss)', 'currency')
|
col('net_book_value', 'Net book value', 'currency'), col('gain_loss', 'Gain / (loss)', 'currency'),
|
||||||
|
col('recapture_section', 'Recapture'), col('ordinary_recapture', 'Ordinary recapture', 'currency'),
|
||||||
|
col('farm_recapture', '§1252/§1255 (farm)', 'currency'), col('section_1231_gain', '§1231 gain / (loss)', 'currency')
|
||||||
],
|
],
|
||||||
rows,
|
rows,
|
||||||
totals: sumBy(rows, ['proceeds', 'net_book_value', 'gain_loss'])
|
totals: sumBy(rows, ['proceeds', 'net_book_value', 'gain_loss', 'ordinary_recapture', 'farm_recapture', 'section_1231_gain'])
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
55
server/services/shortTaxYear.js
Normal file
55
server/services/shortTaxYear.js
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
const { all, audit, now, one, run } = require('../db');
|
||||||
|
|
||||||
|
// Short tax years (IRS Pub 946): when a company's tax year is shorter than 12 months, that year's MACRS
|
||||||
|
// depreciation is prorated. This is the simplified proration — the year's deduction is multiplied by
|
||||||
|
// (months ÷ 12); it is most accurate for the placed-in-service (first) short year. Per company + year,
|
||||||
|
// cached for the engine and invalidated on edits.
|
||||||
|
|
||||||
|
const cache = new Map(); // `${entityId}|${year}` -> fraction
|
||||||
|
|
||||||
|
function clearCache() {
|
||||||
|
cache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proration fraction (0–1) for a company's tax year; 1 when the year is a normal 12-month year.
|
||||||
|
function fraction(entityId, year) {
|
||||||
|
if (!entityId || !year) return 1;
|
||||||
|
const key = `${entityId}|${Number(year)}`;
|
||||||
|
if (cache.has(key)) return cache.get(key);
|
||||||
|
const row = one('SELECT months FROM short_tax_years WHERE entity_id = ? AND tax_year = ?', [entityId, Number(year)]);
|
||||||
|
const months = row ? Math.max(0, Math.min(12, Number(row.months) || 12)) : 12;
|
||||||
|
const f = months / 12;
|
||||||
|
cache.set(key, f);
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
|
||||||
|
function list(companyId) {
|
||||||
|
return all('SELECT * FROM short_tax_years WHERE entity_id = ? ORDER BY tax_year DESC', [companyId]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function save(companyId, body, userId) {
|
||||||
|
const taxYear = Math.trunc(Number(body.tax_year)) || 0;
|
||||||
|
if (!taxYear) throw Object.assign(new Error('A valid tax year is required'), { status: 400 });
|
||||||
|
const months = Math.max(1, Math.min(12, Number(body.months) || 12));
|
||||||
|
run(
|
||||||
|
`INSERT INTO short_tax_years (entity_id, tax_year, months, note, updated_by, updated_at)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?)
|
||||||
|
ON CONFLICT(entity_id, tax_year) DO UPDATE SET months = excluded.months, note = excluded.note,
|
||||||
|
updated_by = excluded.updated_by, updated_at = excluded.updated_at`,
|
||||||
|
[companyId, taxYear, months, body.note || null, userId, now()]
|
||||||
|
);
|
||||||
|
clearCache();
|
||||||
|
audit(userId, 'update', 'short_tax_year', `${companyId}:${taxYear}`, null, { tax_year: taxYear, months });
|
||||||
|
return one('SELECT * FROM short_tax_years WHERE entity_id = ? AND tax_year = ?', [companyId, taxYear]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function remove(companyId, taxYear, userId) {
|
||||||
|
const before = one('SELECT * FROM short_tax_years WHERE entity_id = ? AND tax_year = ?', [companyId, Number(taxYear)]);
|
||||||
|
if (!before) return false;
|
||||||
|
run('DELETE FROM short_tax_years WHERE entity_id = ? AND tax_year = ?', [companyId, Number(taxYear)]);
|
||||||
|
clearCache();
|
||||||
|
audit(userId, 'delete', 'short_tax_year', `${companyId}:${taxYear}`, before, null);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { clearCache, fraction, list, remove, save };
|
||||||
@@ -2030,6 +2030,52 @@ async function main() {
|
|||||||
const lim2032 = (await request('/api/section-179/limitation?book=FEDERAL&year=2032', { headers: aHeaders })).summary;
|
const lim2032 = (await request('/api/section-179/limitation?book=FEDERAL&year=2032', { headers: aHeaders })).summary;
|
||||||
if (lim2032.prior_carryover !== 2000000) throw new Error(`§179 carryover did not chain to next year: ${lim2032.prior_carryover}`);
|
if (lim2032.prior_carryover !== 2000000) throw new Error(`§179 carryover did not chain to next year: ${lim2032.prior_carryover}`);
|
||||||
|
|
||||||
|
// ---- Depreciation recapture: §1245 on disposal + §280F conversion (Form 4797) ---
|
||||||
|
// Fully-depreciated equipment (cost 10k, placed 2018) sold for 12k in 2026 → gain 12k: §1245 ordinary
|
||||||
|
// recapture = depreciation taken (10k), the remaining 2k is §1231 capital gain.
|
||||||
|
const eq = (await request('/api/assets', { method: 'POST', headers: aHeaders, body: JSON.stringify({ asset_id: `EQ-${suffix}`, description: 'Equipment', category: 'Computer Equipment', acquisition_cost: 10000, acquired_date: '2018-01-01', in_service_date: '2018-01-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 }] }) })).asset;
|
||||||
|
const recapDisp = (await request(`/api/assets/${eq.id}/disposal/preview`, { method: 'POST', headers: aHeaders, body: JSON.stringify({ book_type: 'FEDERAL', disposal_date: '2026-08-01', disposal_price: 12000 }) })).preview;
|
||||||
|
if (recapDisp.recapture_section !== '1245') throw new Error(`Recapture section wrong: ${disp.recapture_section}`);
|
||||||
|
if (recapDisp.ordinary_recapture !== 10000) throw new Error(`§1245 ordinary recapture wrong: expected 10,000, got ${recapDisp.ordinary_recapture}`);
|
||||||
|
if (recapDisp.section_1231_gain !== 2000) throw new Error(`§1231 gain wrong: expected 2,000, got ${recapDisp.section_1231_gain}`);
|
||||||
|
|
||||||
|
// §280F conversion recapture: listed PC at 100% use (2024) converting to ≤50% in 2026 → excess of
|
||||||
|
// accelerated-over-ADS through 2025 = 5,200 − 3,000 = 2,200 ordinary income.
|
||||||
|
const pc = (await request('/api/assets', { method: 'POST', headers: aHeaders, body: JSON.stringify({ asset_id: `PC-${suffix}`, description: 'Listed PC', category: 'Computer Equipment', acquisition_cost: 10000, acquired_date: '2024-01-01', in_service_date: '2024-01-01', useful_life_months: 60, business_use_percent: 100, listed_property: true, 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 }] }) })).asset;
|
||||||
|
const rec280 = (await request(`/api/assets/${pc.id}/section-280f-recapture?book=FEDERAL&year=2026`, { headers: aHeaders })).recapture;
|
||||||
|
if (!rec280.applies || rec280.recapture !== 2200) throw new Error(`§280F recapture wrong: ${JSON.stringify(rec280)}`);
|
||||||
|
|
||||||
|
// Farm-property recapture (IRS Pub 225): §1252 soil/water (§175) + §1255 cost-sharing (§126). Farmland
|
||||||
|
// held 7 years with $20k §175 and $10k §126, sold at a $50k gain → §1252 = 60% × 20k = 12k; §1255 = 10k.
|
||||||
|
const farm = (await request('/api/assets', { method: 'POST', headers: aHeaders, body: JSON.stringify({ asset_id: `FARM-${suffix}`, description: 'Farmland', category: 'Computer Equipment', acquisition_cost: 100000, land_value: 100000, in_service_date: '2019-06-01', acquired_date: '2019-06-01', useful_life_months: 0, soil_water_deductions: 20000, cost_sharing_excluded: 10000, books: [{ book_type: 'FEDERAL', active: true, depreciation_method: 'straight_line', useful_life_months: 0, cost: 100000 }] }) })).asset;
|
||||||
|
const farmDisp = (await request(`/api/assets/${farm.id}/disposal/preview`, { method: 'POST', headers: aHeaders, body: JSON.stringify({ book_type: 'FEDERAL', disposal_date: '2026-07-01', disposal_price: 150000 }) })).preview;
|
||||||
|
if (farmDisp.section_1252_recapture !== 12000) throw new Error(`§1252 recapture wrong: expected 12,000, got ${farmDisp.section_1252_recapture}`);
|
||||||
|
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}`);
|
||||||
|
|
||||||
|
// ---- 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).
|
||||||
|
await request('/api/assets', { method: 'POST', headers: aHeaders, body: JSON.stringify({ asset_id: `MT-${suffix}`, description: 'MT200', category: 'Computer Equipment', acquisition_cost: 10000, acquired_date: '2024-05-01', in_service_date: '2024-05-01', useful_life_months: 60, books: [{ book_type: 'FEDERAL', active: true, depreciation_method: 'MT200', convention: 'half_year', useful_life_months: 60, bonus_percent: 0, section_179_amount: 0, cost: 10000 }] }) });
|
||||||
|
const mtRow = await deprRow(`MT-${suffix}`, 2024);
|
||||||
|
if (!mtRow || Math.abs(mtRow.depreciation - 2000) > 1) throw new Error(`MT200 literal table year-1 wrong: expected 2,000, got ${mtRow && mtRow.depreciation}`);
|
||||||
|
|
||||||
|
// #3 Listed property ≤50% business use uses the ADS recovery period from its asset class (GDS 7 / ADS 10):
|
||||||
|
// basis 10k×40% = 4k, ADS 10-year straight-line, half-year → 200 (not the 7-year GDS life).
|
||||||
|
await request('/api/asset-classes', { method: 'POST', headers: auth, body: JSON.stringify({ class_number: `88.${suffix}`, description: 'ADS test', gds: 7, ads: 10 }) });
|
||||||
|
await request('/api/asset-categories', { method: 'POST', headers: aHeaders, body: JSON.stringify({ name: `ADSCat-${suffix}`, asset_class_number: `88.${suffix}` }) });
|
||||||
|
await request('/api/assets', { method: 'POST', headers: aHeaders, body: JSON.stringify({ asset_id: `ADS-${suffix}`, description: 'ADS', category: `ADSCat-${suffix}`, acquisition_cost: 10000, acquired_date: '2024-05-01', in_service_date: '2024-05-01', useful_life_months: 84, business_use_percent: 40, listed_property: true, books: [{ book_type: 'FEDERAL', active: true, depreciation_method: 'macrs_gds_200db_7', convention: 'half_year', useful_life_months: 84, bonus_percent: 0, section_179_amount: 0, cost: 10000 }] }) });
|
||||||
|
const adsRow = await deprRow(`ADS-${suffix}`, 2024);
|
||||||
|
if (!adsRow || Math.abs(adsRow.depreciation - 200) > 1) throw new Error(`ADS recovery period not applied: expected 200 (10-yr ADS), got ${adsRow && adsRow.depreciation}`);
|
||||||
|
|
||||||
|
// #2 Short tax year prorates that year's MACRS depreciation by months ÷ 12 (2,000 → 1,000 for 6 months).
|
||||||
|
await request('/api/assets', { method: 'POST', headers: aHeaders, body: JSON.stringify({ asset_id: `STY-${suffix}`, description: 'STY', category: 'Computer Equipment', acquisition_cost: 10000, acquired_date: '2033-01-01', in_service_date: '2033-01-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 }] }) });
|
||||||
|
const styBaseline = await deprRow(`STY-${suffix}`, 2033);
|
||||||
|
if (!styBaseline || Math.abs(styBaseline.depreciation - 2000) > 1) throw new Error(`Short-year baseline wrong: ${styBaseline && styBaseline.depreciation}`);
|
||||||
|
await request('/api/short-tax-years', { method: 'POST', headers: aHeaders, body: JSON.stringify({ tax_year: 2033, months: 6 }) });
|
||||||
|
const styShort = await deprRow(`STY-${suffix}`, 2033);
|
||||||
|
if (!styShort || Math.abs(styShort.depreciation - 1000) > 1) throw new Error(`Short tax year not prorated: expected 1,000, got ${styShort && styShort.depreciation}`);
|
||||||
|
|
||||||
console.log('Smoke test passed');
|
console.log('Smoke test passed');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -55,6 +55,8 @@
|
|||||||
<v-text-field v-model.number="localAsset.investment_use_percent" type="number" label="Investment use %" />
|
<v-text-field v-model.number="localAsset.investment_use_percent" type="number" label="Investment use %" />
|
||||||
<v-switch v-model="localAsset.listed_property" label="Listed property (≤50% business use → ADS straight-line)" color="primary" density="compact" hide-details class="full" />
|
<v-switch v-model="localAsset.listed_property" label="Listed property (≤50% business use → ADS straight-line)" color="primary" density="compact" hide-details class="full" />
|
||||||
<v-switch v-model="localAsset.passenger_auto" label="Passenger automobile (§280F annual depreciation caps)" color="primary" density="compact" hide-details class="full" />
|
<v-switch v-model="localAsset.passenger_auto" label="Passenger automobile (§280F annual depreciation caps)" color="primary" density="compact" hide-details class="full" />
|
||||||
|
<v-text-field v-model.number="localAsset.soil_water_deductions" type="number" prefix="$" label="Soil & water conservation deductions (§175 — farm §1252)" />
|
||||||
|
<v-text-field v-model.number="localAsset.cost_sharing_excluded" type="number" prefix="$" label="Excluded cost-sharing payments (§126 — farm §1255)" />
|
||||||
|
|
||||||
<v-select
|
<v-select
|
||||||
v-model="localAsset.special_zone"
|
v-model="localAsset.special_zone"
|
||||||
@@ -328,6 +330,14 @@
|
|||||||
Net book value {{ currency(disposalPreview.net_book_value) }} ·
|
Net book value {{ currency(disposalPreview.net_book_value) }} ·
|
||||||
Proceeds {{ currency(disposalPreview.realized) }} →
|
Proceeds {{ currency(disposalPreview.realized) }} →
|
||||||
<strong>{{ disposalPreview.gain_loss >= 0 ? 'Gain' : 'Loss' }} of {{ currency(Math.abs(disposalPreview.gain_loss)) }}</strong>
|
<strong>{{ disposalPreview.gain_loss >= 0 ? 'Gain' : 'Loss' }} of {{ currency(Math.abs(disposalPreview.gain_loss)) }}</strong>
|
||||||
|
<div v-if="disposalPreview.gain_loss > 0" class="text-caption mt-1">
|
||||||
|
Form 4797 §{{ disposalPreview.recapture_section }}:
|
||||||
|
<strong>{{ currency(disposalPreview.ordinary_recapture) }}</strong> ordinary recapture
|
||||||
|
<span v-if="disposalPreview.section_1252_recapture > 0"> · {{ currency(disposalPreview.section_1252_recapture) }} §1252 (soil/water)</span>
|
||||||
|
<span v-if="disposalPreview.section_1255_recapture > 0"> · {{ currency(disposalPreview.section_1255_recapture) }} §1255 (cost-sharing)</span>
|
||||||
|
<span v-if="disposalPreview.section_1231_gain > 0"> · {{ currency(disposalPreview.section_1231_gain) }} §1231 gain</span>
|
||||||
|
<span v-if="disposalPreview.unrecaptured_1250_gain > 0"> · {{ currency(disposalPreview.unrecaptured_1250_gain) }} unrecaptured §1250 (25%)</span>
|
||||||
|
</div>
|
||||||
</v-alert>
|
</v-alert>
|
||||||
|
|
||||||
<v-divider class="my-4" />
|
<v-divider class="my-4" />
|
||||||
|
|||||||
92
src/components/ShortTaxYearSettings.vue
Normal file
92
src/components/ShortTaxYearSettings.vue
Normal file
@@ -0,0 +1,92 @@
|
|||||||
|
<template>
|
||||||
|
<v-card class="span-12 panel-pad">
|
||||||
|
<div class="toolbar-row mb-3">
|
||||||
|
<div>
|
||||||
|
<h2 class="section-title">Short tax years</h2>
|
||||||
|
<p class="quiet text-body-2">
|
||||||
|
Define a tax year shorter than 12 months for the <strong>active company</strong>. That year's MACRS depreciation is
|
||||||
|
prorated by months ÷ 12 (simplified method — most accurate for a placed-in-service short year).
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<v-spacer />
|
||||||
|
<v-btn color="primary" prepend-icon="mdi-plus" @click="openNew">New year</v-btn>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<DataTable :columns="columns" :rows="years" row-key="tax_year" :page-size="10" has-actions empty-text="No short tax years — all years are full 12-month years.">
|
||||||
|
<template #cell-months="{ row }">{{ row.months }} months ({{ Math.round((row.months / 12) * 100) }}%)</template>
|
||||||
|
<template #actions="{ row }">
|
||||||
|
<v-btn icon="mdi-pencil" size="small" variant="text" @click="openEdit(row)" />
|
||||||
|
<v-btn icon="mdi-delete-outline" size="small" variant="text" color="error" @click="remove(row)" />
|
||||||
|
</template>
|
||||||
|
</DataTable>
|
||||||
|
<v-alert v-if="error" type="error" density="compact" class="mt-3">{{ error }}</v-alert>
|
||||||
|
|
||||||
|
<v-dialog v-model="dialog" max-width="460">
|
||||||
|
<v-card>
|
||||||
|
<v-card-title>{{ form.exists ? `Edit ${form.tax_year}` : 'New short tax year' }}</v-card-title>
|
||||||
|
<v-card-text>
|
||||||
|
<div class="form-grid">
|
||||||
|
<v-text-field v-model.number="form.tax_year" type="number" label="Tax year *" :disabled="form.exists" />
|
||||||
|
<v-text-field v-model.number="form.months" type="number" label="Months in the tax year (1–12)" min="1" max="12" />
|
||||||
|
<v-text-field v-model="form.note" class="full" label="Note (optional)" />
|
||||||
|
</div>
|
||||||
|
<v-alert v-if="dialogError" type="error" density="compact" class="mt-2">{{ dialogError }}</v-alert>
|
||||||
|
</v-card-text>
|
||||||
|
<v-card-actions>
|
||||||
|
<v-spacer />
|
||||||
|
<v-btn variant="text" @click="dialog = false">Cancel</v-btn>
|
||||||
|
<v-btn color="primary" prepend-icon="mdi-content-save" :loading="saving" :disabled="!form.tax_year" @click="save">Save</v-btn>
|
||||||
|
</v-card-actions>
|
||||||
|
</v-card>
|
||||||
|
</v-dialog>
|
||||||
|
</v-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import DataTable from './DataTable.vue';
|
||||||
|
import { apiRequest } from '../services/api';
|
||||||
|
|
||||||
|
// Short-tax-year config (Admin → Application Configuration), company-scoped via the active company. CRUD
|
||||||
|
// against /api/short-tax-years; the engine prorates that year's MACRS depreciation by months ÷ 12.
|
||||||
|
function blankForm() {
|
||||||
|
return { exists: false, tax_year: new Date().getFullYear(), months: 12, note: '' };
|
||||||
|
}
|
||||||
|
|
||||||
|
export default {
|
||||||
|
components: { DataTable },
|
||||||
|
props: { token: { type: String, required: true } },
|
||||||
|
emits: ['updated'],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
years: [], dialog: false, form: blankForm(), saving: false, error: '', dialogError: '',
|
||||||
|
columns: [{ key: 'tax_year', label: 'Tax year' }, { key: 'months', label: 'Length' }, { key: 'note', label: 'Note' }]
|
||||||
|
};
|
||||||
|
},
|
||||||
|
mounted() { this.load(); },
|
||||||
|
methods: {
|
||||||
|
async api(path, options = {}) { return apiRequest(path, this.token, options); },
|
||||||
|
async load() {
|
||||||
|
this.error = '';
|
||||||
|
try { this.years = (await (await this.api('/api/short-tax-years')).json()).years; }
|
||||||
|
catch (error) { this.error = error.message; }
|
||||||
|
},
|
||||||
|
openNew() { this.form = blankForm(); this.dialogError = ''; this.dialog = true; },
|
||||||
|
openEdit(row) { this.form = { exists: true, tax_year: row.tax_year, months: row.months, note: row.note || '' }; this.dialogError = ''; this.dialog = true; },
|
||||||
|
async save() {
|
||||||
|
if (!this.form.tax_year) return;
|
||||||
|
this.saving = true; this.dialogError = '';
|
||||||
|
try {
|
||||||
|
await this.api('/api/short-tax-years', { method: 'POST', body: JSON.stringify(this.form) });
|
||||||
|
this.dialog = false;
|
||||||
|
await this.load();
|
||||||
|
this.$emit('updated');
|
||||||
|
} catch (error) { this.dialogError = error.message; } finally { this.saving = false; }
|
||||||
|
},
|
||||||
|
async remove(row) {
|
||||||
|
if (!window.confirm(`Remove the short tax year ${row.tax_year}?`)) return;
|
||||||
|
try { await this.api(`/api/short-tax-years/${row.tax_year}`, { method: 'DELETE' }); await this.load(); this.$emit('updated'); }
|
||||||
|
catch (error) { this.error = error.message; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
@@ -298,10 +298,17 @@
|
|||||||
<p>
|
<p>
|
||||||
Each book’s depreciation is computed from the depreciation-critical fields: property type, placed-in-service date,
|
Each book’s depreciation is computed from the depreciation-critical fields: property type, placed-in-service date,
|
||||||
acquisition value, depreciation method, estimated life, salvage value, Section 168 (bonus) allowance %, Section 179, and
|
acquisition value, depreciation method, estimated life, salvage value, Section 168 (bonus) allowance %, Section 179, and
|
||||||
business-use %. Supported 200% declining-balance methods include <strong>MF200</strong> (MACRS formula),
|
business-use %. Supported 200% declining-balance methods include <strong>MF200</strong> (MACRS by formula),
|
||||||
<strong>MT200</strong> (MACRS tables), and <strong>MI200</strong> (MACRS, Indian-reservation) — which ignore salvage and
|
<strong>MT200</strong> (MACRS by the <strong>literal IRS Pub 946 Appendix-A percentage tables</strong> for half-year
|
||||||
recover the full basis — and <strong>DB200</strong>, <strong>DH200</strong> (half-year), and <strong>DD200</strong> (full-year),
|
3/5/7/10/15/20-year property — mid-quarter falls back to the equivalent formula), and <strong>MI200</strong>
|
||||||
which honor salvage (depreciating only down to it). All switch to straight-line when that yields a larger deduction.
|
(MACRS, Indian-reservation) — which ignore salvage and recover the full basis — and <strong>DB200</strong>,
|
||||||
|
<strong>DH200</strong> (half-year), and <strong>DD200</strong> (full-year), which honor salvage. All switch to
|
||||||
|
straight-line when that yields a larger deduction.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
<strong>Short tax years.</strong> If a company's tax year is shorter than 12 months (for example, the year it is
|
||||||
|
formed), define it in <strong>Admin → Application Configuration → Short tax years</strong> and that year's MACRS
|
||||||
|
depreciation is prorated by months ÷ 12 (the simplified method).
|
||||||
</p>
|
</p>
|
||||||
<v-alert type="info" variant="tonal" density="comfortable" class="manual-callout">
|
<v-alert type="info" variant="tonal" density="comfortable" class="manual-callout">
|
||||||
When you change a depreciation-critical field on an asset that already has depreciation, a prompt asks <strong>when to apply</strong>
|
When you change a depreciation-critical field on an asset that already has depreciation, a prompt asks <strong>when to apply</strong>
|
||||||
@@ -346,7 +353,8 @@
|
|||||||
<ul>
|
<ul>
|
||||||
<li><strong>Listed property</strong> — when such an asset is used <strong>50% or less</strong> for qualified
|
<li><strong>Listed property</strong> — when such an asset is used <strong>50% or less</strong> for qualified
|
||||||
business (its <em>Business use %</em>), the engine automatically switches it to <strong>ADS straight-line</strong>
|
business (its <em>Business use %</em>), the engine automatically switches it to <strong>ADS straight-line</strong>
|
||||||
and disallows §179 and bonus depreciation, exactly as §280F requires. Above 50%, it depreciates normally.</li>
|
(over the <strong>ADS recovery period from the asset's IRS class</strong> when set) and disallows §179 and bonus
|
||||||
|
depreciation, exactly as §280F requires. Above 50%, it depreciates normally.</li>
|
||||||
<li><strong>Passenger automobile</strong> — flags the asset for the annual <strong>luxury-auto depreciation caps</strong>.
|
<li><strong>Passenger automobile</strong> — flags the asset for the annual <strong>luxury-auto depreciation caps</strong>.
|
||||||
Each year’s depreciation is limited to the §280F cap for the placed-in-service year (the higher first-year figure
|
Each year’s depreciation is limited to the §280F cap for the placed-in-service year (the higher first-year figure
|
||||||
applies when bonus is claimed), reduced by the business-use %. Any basis not recovered within the recovery period
|
applies when bonus is claimed), reduced by the business-use %. Any basis not recovered within the recovery period
|
||||||
@@ -369,6 +377,52 @@
|
|||||||
picks it up</strong> as additional available §179.
|
picks it up</strong> as additional available §179.
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
|
<h3>Depreciation recapture (Form 4797) & §280F recapture</h3>
|
||||||
|
<p>
|
||||||
|
When you dispose of an asset at a <strong>gain</strong>, the system characterizes that gain for Form 4797:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>§1245</strong> (equipment / personal property) — the gain is <strong>ordinary-income recapture</strong>
|
||||||
|
up to the total depreciation taken (including §179 and bonus); any gain beyond that is a <strong>§1231</strong>
|
||||||
|
(capital) gain.</li>
|
||||||
|
<li><strong>§1250</strong> (real property) — ordinary recapture applies only to <em>additional</em> depreciation
|
||||||
|
(accelerated over straight-line); since modern MACRS real property is straight-line this is usually zero, and the
|
||||||
|
gain up to straight-line depreciation is shown as <strong>unrecaptured §1250 gain</strong> (the 25% bucket).</li>
|
||||||
|
</ul>
|
||||||
|
<p>
|
||||||
|
The breakdown appears on the <strong>disposal preview</strong> (and is stored on the disposal and shown on the
|
||||||
|
Disposals report). A loss is not recaptured. Recapture is computed on the book you dispose against — use a tax book
|
||||||
|
(e.g. FEDERAL) for the tax characterization.
|
||||||
|
</p>
|
||||||
|
<v-alert type="info" variant="tonal" density="comfortable" class="manual-callout">
|
||||||
|
<strong>§280F recapture.</strong> If a listed-property or passenger-automobile asset's qualified business use drops to
|
||||||
|
<strong>50% or less</strong>, the excess of the accelerated depreciation already claimed over what ADS straight-line
|
||||||
|
would have allowed must be recaptured as ordinary income. The asset's detail view can <strong>preview</strong> this
|
||||||
|
recapture for a chosen conversion year (excess of accelerated-over-ADS through the prior year).
|
||||||
|
</v-alert>
|
||||||
|
|
||||||
|
<h3>Farm property (IRS Pub 225)</h3>
|
||||||
|
<p>
|
||||||
|
Agricultural assets are handled through the same depreciation engine, with farm-specific support:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li><strong>Recovery periods.</strong> The IRS asset-class catalog includes the farm classes — agricultural
|
||||||
|
machinery (7-yr), farm buildings (20-yr), single-purpose agricultural/horticultural structures (10-yr), fences
|
||||||
|
and grain bins (7-yr), land improvements like wells/irrigation/drainage (15-yr), breeding/dairy/draft livestock
|
||||||
|
and horses (3/5/7-yr by type), and orchards & vineyards — so picking the right asset class sets the period.</li>
|
||||||
|
<li><strong>Method.</strong> Farm property generally uses <strong>150% declining balance</strong> (the
|
||||||
|
<code>macrs_gds_150db_*</code> methods); 3/5/7/10-year farm property placed in service after 2017 may use 200% DB,
|
||||||
|
while 15- and 20-year property stays at 150%. ADS straight-line is available (and required when you elect out of
|
||||||
|
the uniform-capitalization rules — choose an ADS method for that book).</li>
|
||||||
|
<li><strong>§179, bonus & conventions</strong> apply to farm property (including single-purpose structures and
|
||||||
|
grain bins), with the half-year and mid-quarter conventions handled automatically.</li>
|
||||||
|
<li><strong>Farm disposition recapture.</strong> Beyond §1245/§1250, disposing of farm property recaptures
|
||||||
|
<strong>§1252</strong> (soil & water conservation deductions, §175) and <strong>§1255</strong> (excluded
|
||||||
|
cost-sharing payments, §126) as ordinary income, by an applicable percentage based on years held. Enter those
|
||||||
|
amounts on the asset's Details tab (Soil & water conservation / Cost-sharing) and the disposal preview and
|
||||||
|
Disposals report show the §1252/§1255 recapture.</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h3>The PM (maintenance) book</h3>
|
<h3>The PM (maintenance) book</h3>
|
||||||
<p>
|
<p>
|
||||||
A dedicated <strong>PM book</strong> tracks actual maintenance <em>spend</em> per asset rather than depreciation. Its ledger
|
A dedicated <strong>PM book</strong> tracks actual maintenance <em>spend</em> per asset rather than depreciation. Its ledger
|
||||||
@@ -894,7 +948,7 @@
|
|||||||
<strong>Admin</strong> (admin role) is where the application is configured. It is split into sub-screens in the
|
<strong>Admin</strong> (admin role) is where the application is configured. It is split into sub-screens in the
|
||||||
navigation rail: <strong>Users & Teams</strong>, <strong>Password & Security</strong>,
|
navigation rail: <strong>Users & Teams</strong>, <strong>Password & Security</strong>,
|
||||||
<strong>Activity & Audit Trail</strong>, <strong>Application Configuration</strong> (companies, application
|
<strong>Activity & Audit Trail</strong>, <strong>Application Configuration</strong> (companies, application
|
||||||
settings, asset classes, depreciation zones, Section 179 limits, §280F passenger-auto limits, asset ID templates, asset categories,
|
settings, asset classes, depreciation zones, Section 179 limits, §280F passenger-auto limits, short tax years, asset ID templates, asset categories,
|
||||||
asset departments, PM categories, parts categories, maintenance defaults, tax rule sets), and
|
asset departments, PM categories, parts categories, maintenance defaults, tax rule sets), and
|
||||||
<strong>Integrations</strong> (email, webhook, Microsoft Teams, ServiceNow, Workday). Each is described below.
|
<strong>Integrations</strong> (email, webhook, Microsoft Teams, ServiceNow, Workday). Each is described below.
|
||||||
</p>
|
</p>
|
||||||
@@ -1216,7 +1270,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: '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: '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: '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' },
|
{ 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: '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: '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: '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' },
|
{ id: 'templates', title: 'Templates', icon: 'mdi-form-select', keywords: 'template defaults custom fields data entry' },
|
||||||
|
|||||||
@@ -196,6 +196,8 @@
|
|||||||
|
|
||||||
<Section280fLimitSettings :token="token" />
|
<Section280fLimitSettings :token="token" />
|
||||||
|
|
||||||
|
<ShortTaxYearSettings :token="token" />
|
||||||
|
|
||||||
<AssetIdTemplateSettings :token="token" />
|
<AssetIdTemplateSettings :token="token" />
|
||||||
|
|
||||||
<CategorySettings :token="token" @updated="$emit('categories-updated')" />
|
<CategorySettings :token="token" @updated="$emit('categories-updated')" />
|
||||||
@@ -326,6 +328,7 @@ import DepartmentSettings from '../components/DepartmentSettings.vue';
|
|||||||
import DepreciationZoneSettings from '../components/DepreciationZoneSettings.vue';
|
import DepreciationZoneSettings from '../components/DepreciationZoneSettings.vue';
|
||||||
import Section179LimitSettings from '../components/Section179LimitSettings.vue';
|
import Section179LimitSettings from '../components/Section179LimitSettings.vue';
|
||||||
import Section280fLimitSettings from '../components/Section280fLimitSettings.vue';
|
import Section280fLimitSettings from '../components/Section280fLimitSettings.vue';
|
||||||
|
import ShortTaxYearSettings from '../components/ShortTaxYearSettings.vue';
|
||||||
import NotificationSettings from '../components/NotificationSettings.vue';
|
import NotificationSettings from '../components/NotificationSettings.vue';
|
||||||
import PartCategorySettings from '../components/PartCategorySettings.vue';
|
import PartCategorySettings from '../components/PartCategorySettings.vue';
|
||||||
import PmCategorySettings from '../components/PmCategorySettings.vue';
|
import PmCategorySettings from '../components/PmCategorySettings.vue';
|
||||||
@@ -335,7 +338,7 @@ import TeamsSettings from '../components/TeamsSettings.vue';
|
|||||||
import WebhookSettings from '../components/WebhookSettings.vue';
|
import WebhookSettings from '../components/WebhookSettings.vue';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: { AppLogsSettings, AssetClassSettings, AssetIdTemplateSettings, AuditTrailSettings, CategorySettings, CompanySettings, DataTable, DepartmentSettings, DepreciationZoneSettings, NotificationSettings, PartCategorySettings, PasswordPolicySettings, PmCategorySettings, PmSettings, Section179LimitSettings, Section280fLimitSettings, ServiceNowSettings, TeamsSettings, WebhookSettings },
|
components: { AppLogsSettings, AssetClassSettings, AssetIdTemplateSettings, AuditTrailSettings, CategorySettings, CompanySettings, DataTable, DepartmentSettings, DepreciationZoneSettings, NotificationSettings, PartCategorySettings, PasswordPolicySettings, PmCategorySettings, PmSettings, Section179LimitSettings, Section280fLimitSettings, ShortTaxYearSettings, ServiceNowSettings, TeamsSettings, WebhookSettings },
|
||||||
props: {
|
props: {
|
||||||
token: { type: String, default: '' },
|
token: { type: String, default: '' },
|
||||||
section: { type: String, default: 'admin-users' },
|
section: { type: String, default: 'admin-users' },
|
||||||
|
|||||||
Reference in New Issue
Block a user