const { all } = require('../db'); // IRS Pub 946 mid-quarter convention determination. The mid-quarter convention is MANDATORY for all of a // tax year's MACRS personal property when more than 40% of the aggregate depreciable basis of such // property is placed in service during the last three months (Q4) of the year. This is a portfolio- and // year-level fact, computed here once per (company, book, year) and consulted by the depreciation engine // (depreciation.conventionFor). Real property (mid-month) is excluded. Cached; invalidated on asset writes. const cache = new Map(); // `${entityId}|${bookCode}|${year}` -> boolean function clearCache() { cache.clear(); } function bookObject(row, bookCode) { return { book_type: bookCode, cost: row.book_cost, depreciation_method: row.depreciation_method, convention: row.convention, useful_life_months: row.book_life, section_179_amount: row.section_179_amount, bonus_percent: row.bonus_percent, business_use_percent: row.book_bup, investment_use_percent: row.book_ivp, manual_depreciation: row.manual_depreciation }; } // True when the mid-quarter convention applies to (company, book) MACRS personal property placed in // service in `year`. No-op-friendly: returns false when the portfolio doesn't trip the 40% threshold. function applies(entityId, bookCode, year) { if (!entityId || !bookCode || !year) return false; const key = `${entityId}|${bookCode}|${Number(year)}`; if (cache.has(key)) return cache.get(key); const dep = require('../depreciation'); const { ruleSetForBook } = require('./ruleSets'); const { assetFromRow } = require('./assets'); const ruleSet = ruleSetForBook(bookCode); const rows = all( `SELECT a.*, b.cost AS book_cost, b.depreciation_method, b.convention, b.useful_life_months AS book_life, b.section_179_amount, b.bonus_percent, b.business_use_percent AS book_bup, b.investment_use_percent AS book_ivp, b.manual_depreciation FROM assets a JOIN asset_books b ON b.asset_id = a.id WHERE b.book_type = ? AND b.active = 1 AND a.entity_id = ? AND a.status != 'disposed'`, [bookCode, entityId] ); let total = 0; let lastQuarter = 0; for (const row of rows) { const pis = row.in_service_date || row.acquired_date; if (!pis || dep.yearFromDate(pis) !== Number(year)) continue; const book = bookObject(row, bookCode); const methodRule = dep.getMethodRule(ruleSet, book.depreciation_method); if (!dep.isMidQuarterEligible(book, methodRule)) continue; const basis = dep.midQuarterBasis(assetFromRow(row), book); if (basis <= 0) continue; total += basis; if (Math.ceil(dep.monthFromDate(pis) / 3) === 4) lastQuarter += basis; } const result = total > 0 && lastQuarter / total > 0.4; cache.set(key, result); return result; } module.exports = { applies, clearCache };