@mudraforge/statutory 2.12.1 → 2.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/README.md +13 -0
- package/docs/ARCHITECTURE.md +70 -47
- package/docs/ARCHITECTURE_DECISIONS.md +22 -2
- package/docs/CORPUS_QUALITY.md +3 -3
- package/docs/DOMAIN_BREADTH_AND_DEPTH_COMPARISON.md +4 -4
- package/docs/ENTERPRISE_GOVERNANCE.md +2 -1
- package/docs/OKF_MAINTENANCE_SOP.md +5 -5
- package/docs/PILOT_EVALUATION_GUIDE.md +5 -4
- package/docs/PROVENANCE.md +6 -6
- package/docs/VERIFICATION.md +6 -6
- package/okf/corporate_ma_restructuring/business_combinations_indas_103/ast.json +20 -0
- package/okf/corporate_ma_restructuring/impairment_of_assets_indas_36/ast.json +20 -0
- package/okf/corporate_ma_restructuring/loss_carryforward_sec79/ast.json +33 -0
- package/okf/corporate_ma_restructuring/share_based_payments_indas_102/ast.json +20 -0
- package/okf/corporate_ma_restructuring/slump_sale_50b/ast.json +21 -0
- package/okf/corporate_ma_restructuring/tax_clearance_sec281/ast.json +21 -0
- package/okf/direct_tax/corporate_tax_115baa_115jb/ast.json +23 -0
- package/okf/direct_tax/income_taxes_deferred_tax_indas_12/ast.json +20 -0
- package/okf/direct_tax/special_rates_non_filer_206ab/ast.json +26 -0
- package/okf/direct_tax/statutory_disallowance_43b/ast.json +35 -0
- package/okf/direct_tax/tcs_goods_206c_1h/ast.json +69 -0
- package/okf/direct_tax/tds_commission_194h/ast.json +60 -0
- package/okf/direct_tax/tds_contractor_194c/ast.json +71 -0
- package/okf/direct_tax/tds_ecommerce_194o/ast.json +48 -0
- package/okf/direct_tax/tds_goods_194q/ast.json +71 -0
- package/okf/direct_tax/tds_goods_393/ast.json +59 -0
- package/okf/direct_tax/tds_individual_contract_194m/ast.json +59 -0
- package/okf/direct_tax/tds_interest_194a/ast.json +59 -0
- package/okf/direct_tax/tds_perquisites_194r/ast.json +47 -0
- package/okf/direct_tax/tds_property_194ia/ast.json +70 -0
- package/okf/direct_tax/tds_rent_194i/ast.json +60 -0
- package/okf/direct_tax/tds_rent_individual_194ib/ast.json +59 -0
- package/okf/direct_tax/tds_services_194j/ast.json +60 -0
- package/okf/indirect_tax_gst/blocked_itc_17_5/ast.json +26 -0
- package/okf/indirect_tax_gst/common_itc_reversal_rule42_43/ast.json +26 -0
- package/okf/indirect_tax_gst/composite_mixed_supplies_sec8/ast.json +21 -0
- package/okf/indirect_tax_gst/composition_levy_sec10/ast.json +23 -0
- package/okf/indirect_tax_gst/credit_debit_notes_sec34/ast.json +21 -0
- package/okf/indirect_tax_gst/e_invoicing_rule48_4/ast.json +20 -0
- package/okf/indirect_tax_gst/export_services_lut_sec16/ast.json +20 -0
- package/okf/indirect_tax_gst/gst_tds_sec51/ast.json +23 -0
- package/okf/indirect_tax_gst/itc_eligibility_16_2/ast.json +20 -0
- package/okf/indirect_tax_gst/itc_reversal_rule37_37a/ast.json +45 -0
- package/okf/indirect_tax_gst/rcm_liability_sec9_3/ast.json +26 -0
- package/okf/international_tax/cross_border_remittance_195/ast.json +39 -0
- package/okf/international_tax/financial_instruments_ecl_indas_109/ast.json +24 -0
- package/okf/international_tax/foreign_exchange_indas_21_109/ast.json +20 -0
- package/okf/international_tax/foreign_tax_credit_irc_901_904/ast.json +20 -0
- package/okf/international_tax/transfer_pricing_alp_92c/ast.json +25 -0
- package/okf/manifest.json +49 -3
- package/okf/msme_commercial/delayed_payments_15_16/ast.json +38 -0
- package/okf/msme_commercial/enterprise_classification_sec7/ast.json +25 -0
- package/okf/msme_commercial/lease_accounting_indas_116/ast.json +20 -0
- package/okf/msme_commercial/mca_msme_1_filing/ast.json +20 -0
- package/okf/msme_commercial/provisions_contingencies_indas_37/ast.json +20 -0
- package/okf/msme_commercial/revenue_recognition_indas_115/ast.json +20 -0
- package/okf/msme_commercial/statutory_disclosures_sec22/ast.json +21 -0
- package/package.json +16 -1
- package/src/constants.js +1 -1
- package/src/index.js +16 -2
- package/src/payroll.js +506 -0
- package/src/usvm/evaluation_vm.js +504 -0
- package/src/usvm/index.js +65 -0
- package/src/usvm/math_rational.js +165 -0
- package/src/usvm/rules/sec_115baa_115jb_mat.json +23 -0
- package/src/usvm/rules/sec_192_salary_tds.json +23 -0
- package/src/usvm/rules/sec_194a_interest.json +59 -0
- package/src/usvm/rules/sec_194c_contractors.json +71 -0
- package/src/usvm/rules/sec_194h_commission.json +60 -0
- package/src/usvm/rules/sec_194i_rent.json +60 -0
- package/src/usvm/rules/sec_194ia_property.json +70 -0
- package/src/usvm/rules/sec_194ib_rent_ind.json +59 -0
- package/src/usvm/rules/sec_194j_services.json +60 -0
- package/src/usvm/rules/sec_194m_contract_ind.json +59 -0
- package/src/usvm/rules/sec_194o_ecommerce.json +48 -0
- package/src/usvm/rules/sec_194q_goods.json +71 -0
- package/src/usvm/rules/sec_194r_perquisites.json +47 -0
- package/src/usvm/rules/sec_195_cross_border.json +39 -0
- package/src/usvm/rules/sec_206ab_non_filer.json +26 -0
- package/src/usvm/rules/sec_206c1h_tcs.json +69 -0
- package/src/usvm/rules/sec_281_tax_clearance.json +21 -0
- package/src/usvm/rules/sec_393_goods.json +59 -0
- package/src/usvm/rules/sec_43bh_disallowance.json +35 -0
- package/src/usvm/rules/sec_50b_slump_sale.json +21 -0
- package/src/usvm/rules/sec_79_loss_continuity.json +33 -0
- package/src/usvm/rules/sec_92c_transfer_pricing.json +25 -0
- package/src/usvm/rules/sec_ftc_dtaa_relief.json +20 -0
- package/src/usvm/rules/sec_gst_blocked_itc_17_5.json +26 -0
- package/src/usvm/rules/sec_gst_composite_mixed_8.json +21 -0
- package/src/usvm/rules/sec_gst_composition_levy_10.json +23 -0
- package/src/usvm/rules/sec_gst_credit_debit_notes_34.json +21 -0
- package/src/usvm/rules/sec_gst_einvoicing_rule48_4.json +20 -0
- package/src/usvm/rules/sec_gst_ewaybill_rule138.json +21 -0
- package/src/usvm/rules/sec_gst_export_lut_16.json +20 -0
- package/src/usvm/rules/sec_gst_itc_eligibility_16_2.json +20 -0
- package/src/usvm/rules/sec_gst_itc_reversal_rule37_37a.json +45 -0
- package/src/usvm/rules/sec_gst_rcm_liability_9_3.json +26 -0
- package/src/usvm/rules/sec_gst_rule42_43_apportionment.json +26 -0
- package/src/usvm/rules/sec_gst_tds_sec51.json +23 -0
- package/src/usvm/rules/sec_indas102_esop.json +20 -0
- package/src/usvm/rules/sec_indas103_mna.json +20 -0
- package/src/usvm/rules/sec_indas109_ecl.json +24 -0
- package/src/usvm/rules/sec_indas115_revenue.json +20 -0
- package/src/usvm/rules/sec_indas116_leases.json +20 -0
- package/src/usvm/rules/sec_indas12_deferred_tax.json +20 -0
- package/src/usvm/rules/sec_indas21_fx_reval.json +20 -0
- package/src/usvm/rules/sec_indas36_impairment.json +20 -0
- package/src/usvm/rules/sec_indas37_provisions.json +20 -0
- package/src/usvm/rules/sec_msme_classification_sec7.json +25 -0
- package/src/usvm/rules/sec_msme_delayed_payments_15_16.json +38 -0
- package/src/usvm/rules/sec_msme_disclosures_sec22.json +21 -0
- package/src/usvm/rules/sec_msme_mca_form_1.json +20 -0
- package/src/usvm/schemas/statutory_ast_schema.js +203 -0
- package/src/usvm/usvm_domain_evaluators.js +1021 -0
- package/types/index.d.ts +39 -0
|
@@ -0,0 +1,504 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ═══════════════════════════════════════════════════════════════════════════
|
|
3
|
+
* MUDRAFORGE UNIVERSAL STATUTORY VIRTUAL MACHINE (USVM) - EVALUATION KERNEL
|
|
4
|
+
* ═══════════════════════════════════════════════════════════════════════════
|
|
5
|
+
*
|
|
6
|
+
* Implements the stateless evaluation kernel for declarative statutory AST packages.
|
|
7
|
+
* Evaluates predicates, triggers, thresholds, rational math, and dual-Act regimes
|
|
8
|
+
* with sub-microsecond latency and zero external dependencies.
|
|
9
|
+
*
|
|
10
|
+
* Specification: RFC-009 (Revision 2.1)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { validateStatutoryAst } from './schemas/statutory_ast_schema.js';
|
|
14
|
+
import {
|
|
15
|
+
toBigIntPaise,
|
|
16
|
+
fromBigIntPaise,
|
|
17
|
+
multiplyRational,
|
|
18
|
+
quantizeCurrencyPaisa,
|
|
19
|
+
roundStatutoryAssessment
|
|
20
|
+
} from './math_rational.js';
|
|
21
|
+
import {
|
|
22
|
+
evaluateMsmeDelayedPaymentCompoundInterest,
|
|
23
|
+
evaluateSection43bHFiscalBoundary,
|
|
24
|
+
evaluateMsmeEnterpriseClassification,
|
|
25
|
+
evaluateGstRule37ProportionalReversal,
|
|
26
|
+
evaluateGstRule37ASupplierNonFiling,
|
|
27
|
+
evaluateGstRule42CommonCreditApportionment,
|
|
28
|
+
evaluateCrossBorderDtaaMatrix,
|
|
29
|
+
evaluateTransferPricingPercentileAlp,
|
|
30
|
+
evaluateSection79VotingContinuity,
|
|
31
|
+
evaluateSection281VoidTransfer,
|
|
32
|
+
evaluateCorporateTaxMatVsConcessionalUsvm,
|
|
33
|
+
evaluateSalaryProgressiveTds,
|
|
34
|
+
evaluateNonFilerPenalRate206AB,
|
|
35
|
+
evaluateDeferredTaxIndAs12Usvm,
|
|
36
|
+
evaluateBlockedCreditSection17_5,
|
|
37
|
+
evaluateItcEligibilitySection16_2,
|
|
38
|
+
evaluateRcmLiabilitySection9_3,
|
|
39
|
+
evaluateCompositeMixedSuppliesSection8,
|
|
40
|
+
evaluateCompositionLevySection10,
|
|
41
|
+
evaluateCreditDebitNotesSection34,
|
|
42
|
+
evaluateExportLutSection16,
|
|
43
|
+
evaluateEInvoicingRule48_4,
|
|
44
|
+
evaluateEWayBillRule138,
|
|
45
|
+
evaluateGstTdsSection51,
|
|
46
|
+
evaluateMcaMsme1Aging,
|
|
47
|
+
evaluateMsmeDisclosuresSection22,
|
|
48
|
+
evaluateLeaseAmortizationIndAs116Usvm,
|
|
49
|
+
evaluateRevenueAllocationIndAs115Usvm,
|
|
50
|
+
evaluateProvisionsAndContingenciesIndAs37Usvm,
|
|
51
|
+
evaluateForeignExchangeRevaluationUsvm,
|
|
52
|
+
evaluateExpectedCreditLossIndAs109Usvm,
|
|
53
|
+
evaluateForeignTaxCreditRelief,
|
|
54
|
+
evaluateSlumpSaleSection50B,
|
|
55
|
+
evaluateBusinessCombinationsIndAs103Usvm,
|
|
56
|
+
evaluateImpairmentIndAs36Usvm,
|
|
57
|
+
evaluateShareBasedPaymentsIndAs102Usvm
|
|
58
|
+
} from './usvm_domain_evaluators.js';
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Safely extracts a nested property using dot-separated notation.
|
|
62
|
+
*/
|
|
63
|
+
function getNestedValue(obj, path) {
|
|
64
|
+
if (!obj || !path) return undefined;
|
|
65
|
+
const parts = String(path).split('.');
|
|
66
|
+
let curr = obj;
|
|
67
|
+
for (const part of parts) {
|
|
68
|
+
if (curr === null || curr === undefined) return undefined;
|
|
69
|
+
curr = curr[part];
|
|
70
|
+
}
|
|
71
|
+
return curr;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Evaluates a typed comparison operator against fact value and target value.
|
|
76
|
+
*/
|
|
77
|
+
function evaluateOperator(factValue, operator, targetValue) {
|
|
78
|
+
switch (operator) {
|
|
79
|
+
case 'EQ':
|
|
80
|
+
return factValue === targetValue;
|
|
81
|
+
case 'NEQ':
|
|
82
|
+
return factValue !== targetValue;
|
|
83
|
+
case 'GT':
|
|
84
|
+
return factValue > targetValue;
|
|
85
|
+
case 'GTE':
|
|
86
|
+
return factValue >= targetValue;
|
|
87
|
+
case 'LT':
|
|
88
|
+
return factValue < targetValue;
|
|
89
|
+
case 'LTE':
|
|
90
|
+
return factValue <= targetValue;
|
|
91
|
+
case 'IN':
|
|
92
|
+
return Array.isArray(targetValue) && targetValue.includes(factValue);
|
|
93
|
+
case 'NOT_IN':
|
|
94
|
+
return Array.isArray(targetValue) && !targetValue.includes(factValue);
|
|
95
|
+
case 'IS_TRUE':
|
|
96
|
+
return Boolean(factValue) === true;
|
|
97
|
+
case 'IS_FALSE':
|
|
98
|
+
return Boolean(factValue) === false;
|
|
99
|
+
case 'EXISTS':
|
|
100
|
+
return factValue !== undefined && factValue !== null;
|
|
101
|
+
case 'NOT_EXISTS':
|
|
102
|
+
return factValue === undefined || factValue === null;
|
|
103
|
+
default:
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Resolves the statutory crystallization date and governing regime (1961 vs 2025 Act)
|
|
110
|
+
* based on the provision's trigger policy and transaction facts.
|
|
111
|
+
*/
|
|
112
|
+
export function resolveTemporalCrystallization(triggerPolicy, facts, cutoverDate = '2026-04-01') {
|
|
113
|
+
const creditDate = facts.creditDate || facts.temporalFacts?.bookCreditDate || null;
|
|
114
|
+
const paymentDate = facts.paymentDate || facts.temporalFacts?.paymentDate || null;
|
|
115
|
+
const transactionDate = facts.transactionDate || facts.temporalFacts?.transactionDate || null;
|
|
116
|
+
|
|
117
|
+
let crystallizedDate = null;
|
|
118
|
+
let crystallizationEvent = 'UNDETERMINED';
|
|
119
|
+
let isStraddling = false;
|
|
120
|
+
|
|
121
|
+
const policyType = triggerPolicy?.type || 'EARLIER_OF';
|
|
122
|
+
|
|
123
|
+
if (policyType === 'EARLIER_OF') {
|
|
124
|
+
if (creditDate && paymentDate) {
|
|
125
|
+
if (creditDate <= paymentDate) {
|
|
126
|
+
crystallizedDate = creditDate;
|
|
127
|
+
crystallizationEvent = 'BOOK_CREDIT';
|
|
128
|
+
} else {
|
|
129
|
+
crystallizedDate = paymentDate;
|
|
130
|
+
crystallizationEvent = 'PAYMENT_DISBURSEMENT';
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const creditIsPre = creditDate < cutoverDate;
|
|
134
|
+
const paymentIsPre = paymentDate < cutoverDate;
|
|
135
|
+
if (creditIsPre !== paymentIsPre) {
|
|
136
|
+
isStraddling = true;
|
|
137
|
+
}
|
|
138
|
+
} else if (creditDate) {
|
|
139
|
+
crystallizedDate = creditDate;
|
|
140
|
+
crystallizationEvent = 'BOOK_CREDIT';
|
|
141
|
+
} else if (paymentDate) {
|
|
142
|
+
crystallizedDate = paymentDate;
|
|
143
|
+
crystallizationEvent = 'PAYMENT_DISBURSEMENT';
|
|
144
|
+
} else if (transactionDate) {
|
|
145
|
+
crystallizedDate = transactionDate;
|
|
146
|
+
crystallizationEvent = 'TRANSACTION_DATE';
|
|
147
|
+
}
|
|
148
|
+
} else if (policyType === 'PAYMENT_ONLY') {
|
|
149
|
+
crystallizedDate = paymentDate || transactionDate || null;
|
|
150
|
+
crystallizationEvent = 'PAYMENT_DISBURSEMENT';
|
|
151
|
+
} else {
|
|
152
|
+
crystallizedDate = transactionDate || creditDate || paymentDate || null;
|
|
153
|
+
crystallizationEvent = 'TRANSACTION_DATE';
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const hasDate = Boolean(crystallizedDate);
|
|
157
|
+
const isAct2025 = hasDate ? crystallizedDate >= cutoverDate : false;
|
|
158
|
+
const governingRegime = hasDate ? (isAct2025 ? 'ACT_2025' : 'ACT_1961') : 'INDETERMINATE_PENDING_TRANSACTION_DATE';
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
crystallizedDate,
|
|
162
|
+
crystallizationEvent,
|
|
163
|
+
isAct2025,
|
|
164
|
+
governingRegime,
|
|
165
|
+
isStraddling,
|
|
166
|
+
creditDate,
|
|
167
|
+
paymentDate
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Pure stateless evaluation kernel for the Universal Statutory Virtual Machine (USVM).
|
|
173
|
+
* Evaluates an approved declarative AST rule package against transaction facts.
|
|
174
|
+
*
|
|
175
|
+
* @param {Object} ruleAst - Validated declarative AST package
|
|
176
|
+
* @param {Object} facts - Ingress transaction facts
|
|
177
|
+
* @returns {Object} Fiduciary statutory evaluation voucher
|
|
178
|
+
*/
|
|
179
|
+
export function evaluateStatutoryAst(ruleAst, facts = {}) {
|
|
180
|
+
// 1. Strict AST validation
|
|
181
|
+
validateStatutoryAst(ruleAst);
|
|
182
|
+
|
|
183
|
+
const trace = [];
|
|
184
|
+
|
|
185
|
+
// 2. Fiduciary base result initialization
|
|
186
|
+
const result = {
|
|
187
|
+
isAuthorizedToDisburse: false, // Fiduciary invariant: computation NEVER authorizes money movement
|
|
188
|
+
ruleId: ruleAst.sectionId,
|
|
189
|
+
ruleVersion: ruleAst.version,
|
|
190
|
+
disposition: 'EVALUATING',
|
|
191
|
+
isTdsApplicable: false,
|
|
192
|
+
isSectionApplicable: false,
|
|
193
|
+
invoiceAmountInr: 0,
|
|
194
|
+
taxableBase: 0,
|
|
195
|
+
tdsRatePercent: 0,
|
|
196
|
+
calculatedTdsInr: 0,
|
|
197
|
+
netWireInr: 0,
|
|
198
|
+
executionTrace: trace
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
// 3. Temporal Crystallization & Dual-Act Resolution
|
|
202
|
+
const cutoverDate = ruleAst.transitionalCutoverDate || '2026-04-01';
|
|
203
|
+
const temporal = resolveTemporalCrystallization(ruleAst.triggerPolicy, facts, cutoverDate);
|
|
204
|
+
|
|
205
|
+
result.isAct2025 = temporal.isAct2025;
|
|
206
|
+
result.effectiveDate = temporal.crystallizedDate;
|
|
207
|
+
result.regimeStatus = temporal.governingRegime === 'INDETERMINATE_PENDING_TRANSACTION_DATE' ? 'INDETERMINATE_PENDING_TRANSACTION_DATE' : 'DETERMINED';
|
|
208
|
+
|
|
209
|
+
if (temporal.isStraddling) {
|
|
210
|
+
result.temporalStraddlingAnalysis = {
|
|
211
|
+
crystallizedDate: temporal.crystallizedDate,
|
|
212
|
+
crystallizationEvent: temporal.crystallizationEvent,
|
|
213
|
+
governingRegime: temporal.governingRegime,
|
|
214
|
+
creditDate: temporal.creditDate,
|
|
215
|
+
paymentDate: temporal.paymentDate,
|
|
216
|
+
legalRule: 'Under Indian direct tax jurisprudence, tax deduction attaches at the earlier of credit or payment. Pre-1 April 2026 crystallization event cements Income-tax Act, 1961 regime.'
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const governingSection = temporal.isAct2025
|
|
221
|
+
? `${ruleAst.successorStatute || 'Income-tax Act, 2025'} ${ruleAst.successorSection || ''}`.trim()
|
|
222
|
+
: `${ruleAst.governingStatute || 'Income-tax Act, 1961'} ${ruleAst.statutorySection || ''}`.trim();
|
|
223
|
+
|
|
224
|
+
result.governingWithholdingSection = governingSection;
|
|
225
|
+
result.governingSection = governingSection;
|
|
226
|
+
result.governingStatute = temporal.governingRegime;
|
|
227
|
+
|
|
228
|
+
// 4. Evaluate Guards (Pre-conditions & Penalties)
|
|
229
|
+
let overrideRateBps = null;
|
|
230
|
+
let panPenaltySection = null;
|
|
231
|
+
|
|
232
|
+
if (Array.isArray(ruleAst.guards)) {
|
|
233
|
+
for (const guard of ruleAst.guards) {
|
|
234
|
+
// Check direct fact or nested path (supporting legacy and normalized facts)
|
|
235
|
+
let factValue = getNestedValue(facts, guard.field);
|
|
236
|
+
if (factValue === undefined && guard.field.startsWith('payee.')) {
|
|
237
|
+
// Support flat legacy keys (e.g. payee.isResident -> isResident or hasPan)
|
|
238
|
+
const flatKey = guard.field.replace('payee.', '');
|
|
239
|
+
if (flatKey === 'isPanValid' && facts.hasPan !== undefined) {
|
|
240
|
+
factValue = Boolean(facts.hasPan) && !Boolean(facts.isPanInoperative);
|
|
241
|
+
} else if (facts[flatKey] !== undefined) {
|
|
242
|
+
factValue = facts[flatKey];
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (factValue === undefined && guard.field === 'payee.isResident' && facts.sellerIsNonResident !== undefined) {
|
|
246
|
+
factValue = !Boolean(facts.sellerIsNonResident);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const isMatch = evaluateOperator(factValue, guard.operator, guard.value);
|
|
250
|
+
|
|
251
|
+
if (isMatch) {
|
|
252
|
+
trace.push(`Guard matched: ${guard.id}`);
|
|
253
|
+
const onMatch = guard.onMatch;
|
|
254
|
+
|
|
255
|
+
if (onMatch?.action === 'HALT_EXEMPT') {
|
|
256
|
+
const rawInvoice = Number(facts.invoiceAmount || facts.grossAmountInr || facts.lineItem?.grossAmountPaisa || 0);
|
|
257
|
+
result.disposition = onMatch.disposition || 'EXEMPT';
|
|
258
|
+
result.statutoryDisposition = onMatch.disposition || 'EXEMPT';
|
|
259
|
+
result.statutoryReason = onMatch.reason || 'Guard halted computation as exempt';
|
|
260
|
+
result.isExemptForeignImport = Boolean(facts.isImportOfGoods || facts.sellerIsNonResident);
|
|
261
|
+
result.invoiceAmountInr = rawInvoice;
|
|
262
|
+
result.netWireInr = rawInvoice;
|
|
263
|
+
result.governingSection = temporal.isAct2025
|
|
264
|
+
? 'CBDT Circular No. 13 of 2021 Question 3 (Read with Section 393(2))'
|
|
265
|
+
: 'CBDT Circular No. 13 of 2021 Question 3 (Read with Section 195)';
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
if (onMatch?.action === 'OVERRIDE_RATE') {
|
|
270
|
+
overrideRateBps = onMatch.overrideRateBps;
|
|
271
|
+
panPenaltySection = temporal.isAct2025
|
|
272
|
+
? onMatch.governingSection2025
|
|
273
|
+
: onMatch.governingSection1961;
|
|
274
|
+
trace.push(`Override applied: rate set to ${overrideRateBps} bps (${overrideRateBps / 100}%)`);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
result.governingPanPenaltySection = panPenaltySection || (temporal.isAct2025 ? 'Section 397 (PAN compliant)' : 'Section 206AA (PAN compliant)');
|
|
281
|
+
|
|
282
|
+
const archetype = ruleAst.computation?.archetype || 'EXCESS_OVER_THRESHOLD';
|
|
283
|
+
|
|
284
|
+
// Advanced Domain Archetype Dispatches
|
|
285
|
+
if (archetype === 'COMPOUND_INTEREST_MONTHLY_RESTS') {
|
|
286
|
+
return evaluateMsmeDelayedPaymentCompoundInterest(ruleAst, facts, trace);
|
|
287
|
+
}
|
|
288
|
+
if (archetype === 'MSME_FISCAL_BOUNDARY_43BH') {
|
|
289
|
+
return evaluateSection43bHFiscalBoundary(ruleAst, facts, trace);
|
|
290
|
+
}
|
|
291
|
+
if (archetype === 'ENTERPRISE_CLASSIFICATION_MSME') {
|
|
292
|
+
return evaluateMsmeEnterpriseClassification(ruleAst, facts, trace);
|
|
293
|
+
}
|
|
294
|
+
if (archetype === 'PROPORTIONAL_REVERSAL_180_DAYS') {
|
|
295
|
+
return evaluateGstRule37ProportionalReversal(ruleAst, facts, trace);
|
|
296
|
+
}
|
|
297
|
+
if (archetype === 'SUPPLIER_NON_FILING_RULE37A') {
|
|
298
|
+
return evaluateGstRule37ASupplierNonFiling(ruleAst, facts, trace);
|
|
299
|
+
}
|
|
300
|
+
if (archetype === 'COMMON_CREDIT_APPORTIONMENT_RULE42') {
|
|
301
|
+
return evaluateGstRule42CommonCreditApportionment(ruleAst, facts, trace);
|
|
302
|
+
}
|
|
303
|
+
if (archetype === 'DTAA_TREATY_TAX_HAVEN_MATRIX') {
|
|
304
|
+
return evaluateCrossBorderDtaaMatrix(ruleAst, facts, temporal, trace);
|
|
305
|
+
}
|
|
306
|
+
if (archetype === 'TRANSFER_PRICING_PERCENTILE_ALP') {
|
|
307
|
+
return evaluateTransferPricingPercentileAlp(ruleAst, facts, trace);
|
|
308
|
+
}
|
|
309
|
+
if (archetype === 'VOTING_CONTINUITY_PERCENTAGE') {
|
|
310
|
+
return evaluateSection79VotingContinuity(ruleAst, facts, trace);
|
|
311
|
+
}
|
|
312
|
+
if (archetype === 'TAX_CLEARANCE_VOID_TRANSFER_SEC281') {
|
|
313
|
+
return evaluateSection281VoidTransfer(ruleAst, facts, trace);
|
|
314
|
+
}
|
|
315
|
+
if (archetype === 'CORPORATE_TAX_MAT_VS_CONCESSIONAL') {
|
|
316
|
+
return evaluateCorporateTaxMatVsConcessionalUsvm(ruleAst, facts, trace);
|
|
317
|
+
}
|
|
318
|
+
if (archetype === 'SALARY_SLAB_PROGRESSIVE_TDS') {
|
|
319
|
+
return evaluateSalaryProgressiveTds(ruleAst, facts, trace);
|
|
320
|
+
}
|
|
321
|
+
if (archetype === 'NON_FILER_PENAL_RATE_206AB') {
|
|
322
|
+
return evaluateNonFilerPenalRate206AB(ruleAst, facts, trace);
|
|
323
|
+
}
|
|
324
|
+
if (archetype === 'DEFERRED_TAX_TIMING_DIFFERENCE') {
|
|
325
|
+
return evaluateDeferredTaxIndAs12Usvm(ruleAst, facts, trace);
|
|
326
|
+
}
|
|
327
|
+
if (archetype === 'BLOCKED_CREDIT_GATE_17_5') {
|
|
328
|
+
return evaluateBlockedCreditSection17_5(ruleAst, facts, trace);
|
|
329
|
+
}
|
|
330
|
+
if (archetype === 'ITC_ELIGIBILITY_GATE_16_2') {
|
|
331
|
+
return evaluateItcEligibilitySection16_2(ruleAst, facts, trace);
|
|
332
|
+
}
|
|
333
|
+
if (archetype === 'RCM_LIABILITY_EVALUATION_9_3') {
|
|
334
|
+
return evaluateRcmLiabilitySection9_3(ruleAst, facts, trace);
|
|
335
|
+
}
|
|
336
|
+
if (archetype === 'COMPOSITE_MIXED_SUPPLY_SEC8') {
|
|
337
|
+
return evaluateCompositeMixedSuppliesSection8(ruleAst, facts, trace);
|
|
338
|
+
}
|
|
339
|
+
if (archetype === 'COMPOSITION_LEVY_SEC10') {
|
|
340
|
+
return evaluateCompositionLevySection10(ruleAst, facts, trace);
|
|
341
|
+
}
|
|
342
|
+
if (archetype === 'CREDIT_NOTE_TIMELINES_SEC34') {
|
|
343
|
+
return evaluateCreditDebitNotesSection34(ruleAst, facts, trace);
|
|
344
|
+
}
|
|
345
|
+
if (archetype === 'EXPORT_LUT_ZERO_RATED_SEC16') {
|
|
346
|
+
return evaluateExportLutSection16(ruleAst, facts, trace);
|
|
347
|
+
}
|
|
348
|
+
if (archetype === 'EINVOICING_MANDATE_RULE48_4') {
|
|
349
|
+
return evaluateEInvoicingRule48_4(ruleAst, facts, trace);
|
|
350
|
+
}
|
|
351
|
+
if (archetype === 'EWAYBILL_CONSIGNMENT_RULE138') {
|
|
352
|
+
return evaluateEWayBillRule138(ruleAst, facts, trace);
|
|
353
|
+
}
|
|
354
|
+
if (archetype === 'GST_TDS_DEDUCTION_SEC51') {
|
|
355
|
+
return evaluateGstTdsSection51(ruleAst, facts, trace);
|
|
356
|
+
}
|
|
357
|
+
if (archetype === 'MCA_MSME_1_AGING_BIFURCATION') {
|
|
358
|
+
return evaluateMcaMsme1Aging(ruleAst, facts, trace);
|
|
359
|
+
}
|
|
360
|
+
if (archetype === 'MSME_DISCLOSURES_SEC22') {
|
|
361
|
+
return evaluateMsmeDisclosuresSection22(ruleAst, facts, trace);
|
|
362
|
+
}
|
|
363
|
+
if (archetype === 'LEASE_AMORTIZATION_INDAS116') {
|
|
364
|
+
return evaluateLeaseAmortizationIndAs116Usvm(ruleAst, facts, trace);
|
|
365
|
+
}
|
|
366
|
+
if (archetype === 'REVENUE_ALLOCATION_INDAS115') {
|
|
367
|
+
return evaluateRevenueAllocationIndAs115Usvm(ruleAst, facts, trace);
|
|
368
|
+
}
|
|
369
|
+
if (archetype === 'PROVISIONS_CONTINGENCY_INDAS37') {
|
|
370
|
+
return evaluateProvisionsAndContingenciesIndAs37Usvm(ruleAst, facts, trace);
|
|
371
|
+
}
|
|
372
|
+
if (archetype === 'FX_SPOT_REVALUATION_INDAS21_109') {
|
|
373
|
+
return evaluateForeignExchangeRevaluationUsvm(ruleAst, facts, trace);
|
|
374
|
+
}
|
|
375
|
+
if (archetype === 'EXPECTED_CREDIT_LOSS_INDAS109') {
|
|
376
|
+
return evaluateExpectedCreditLossIndAs109Usvm(ruleAst, facts, trace);
|
|
377
|
+
}
|
|
378
|
+
if (archetype === 'FOREIGN_TAX_CREDIT_RELIEF') {
|
|
379
|
+
return evaluateForeignTaxCreditRelief(ruleAst, facts, trace);
|
|
380
|
+
}
|
|
381
|
+
if (archetype === 'SLUMP_SALE_NET_WORTH_50B') {
|
|
382
|
+
return evaluateSlumpSaleSection50B(ruleAst, facts, trace);
|
|
383
|
+
}
|
|
384
|
+
if (archetype === 'BUSINESS_COMBINATIONS_INDAS103') {
|
|
385
|
+
return evaluateBusinessCombinationsIndAs103Usvm(ruleAst, facts, trace);
|
|
386
|
+
}
|
|
387
|
+
if (archetype === 'IMPAIRMENT_TESTING_INDAS36') {
|
|
388
|
+
return evaluateImpairmentIndAs36Usvm(ruleAst, facts, trace);
|
|
389
|
+
}
|
|
390
|
+
if (archetype === 'SHARE_BASED_PAYMENTS_INDAS102') {
|
|
391
|
+
return evaluateShareBasedPaymentsIndAs102Usvm(ruleAst, facts, trace);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
// 5. Ingress Invoice Consideration Quantization
|
|
395
|
+
let currentInvoicePaise = 0n;
|
|
396
|
+
let rawInvoiceInr = 0;
|
|
397
|
+
|
|
398
|
+
if (facts.lineItem?.grossAmountPaisa !== undefined) {
|
|
399
|
+
currentInvoicePaise = BigInt(facts.lineItem.grossAmountPaisa);
|
|
400
|
+
rawInvoiceInr = fromBigIntPaise(currentInvoicePaise);
|
|
401
|
+
} else {
|
|
402
|
+
rawInvoiceInr = Number(facts.invoiceAmount ?? facts.grossAmountInr ?? 0);
|
|
403
|
+
const gstAmountInr = Number(facts.gstAmountInr || 0);
|
|
404
|
+
const isAdvancePayment = Boolean(facts.isAdvancePayment);
|
|
405
|
+
const creditNoteInr = Number(facts.creditNoteInr || 0);
|
|
406
|
+
const creditNoteTiming = facts.creditNoteTiming || 'BEFORE_TAX_DEDUCTION';
|
|
407
|
+
|
|
408
|
+
// CBDT Circular No. 13 of 2021 Question 1: GST excluded when separately indicated & not advance
|
|
409
|
+
const effectivePurchaseInr = isAdvancePayment
|
|
410
|
+
? rawInvoiceInr
|
|
411
|
+
: Math.max(0, rawInvoiceInr - gstAmountInr);
|
|
412
|
+
|
|
413
|
+
// CBDT Circular No. 13 of 2021 Question 4: Credit Note treatment
|
|
414
|
+
const isCreditNoteBefore = creditNoteTiming !== 'AFTER_TAX_DEDUCTION';
|
|
415
|
+
const amountInr = isCreditNoteBefore
|
|
416
|
+
? Math.max(0, effectivePurchaseInr - creditNoteInr)
|
|
417
|
+
: effectivePurchaseInr;
|
|
418
|
+
|
|
419
|
+
currentInvoicePaise = toBigIntPaise(amountInr);
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// 6. Cumulative Threshold Consideration Evaluation
|
|
423
|
+
let priorPurchasesPaise = 0n;
|
|
424
|
+
if (facts.aggregateSnapshot?.aggregatePaisa !== undefined) {
|
|
425
|
+
priorPurchasesPaise = BigInt(facts.aggregateSnapshot.aggregatePaisa);
|
|
426
|
+
} else {
|
|
427
|
+
const priorPurchasesInr = Number(
|
|
428
|
+
facts.priorPurchasesInCurrentFyInr ??
|
|
429
|
+
facts.currentFyPriorPurchasesInr ??
|
|
430
|
+
facts.priorFyPurchases ??
|
|
431
|
+
0
|
|
432
|
+
);
|
|
433
|
+
priorPurchasesPaise = toBigIntPaise(priorPurchasesInr);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const thresholdPaise = ruleAst.computation?.thresholdPaisa !== undefined
|
|
437
|
+
? BigInt(ruleAst.computation.thresholdPaisa)
|
|
438
|
+
: 500000000n; // Default ₹50L = 500,000,000 paise
|
|
439
|
+
|
|
440
|
+
const cumulativeBeforePaise = priorPurchasesPaise;
|
|
441
|
+
const cumulativeAfterPaise = priorPurchasesPaise + currentInvoicePaise;
|
|
442
|
+
|
|
443
|
+
let taxableBasePaise = 0n;
|
|
444
|
+
let isThresholdBreached = false;
|
|
445
|
+
|
|
446
|
+
if (archetype === 'EXCESS_OVER_THRESHOLD') {
|
|
447
|
+
if (cumulativeAfterPaise <= thresholdPaise) {
|
|
448
|
+
taxableBasePaise = 0n;
|
|
449
|
+
isThresholdBreached = false;
|
|
450
|
+
} else if (cumulativeBeforePaise >= thresholdPaise) {
|
|
451
|
+
// Threshold was already fully breached by prior purchases; entire current invoice is in excess
|
|
452
|
+
taxableBasePaise = currentInvoicePaise;
|
|
453
|
+
isThresholdBreached = true;
|
|
454
|
+
} else {
|
|
455
|
+
// Breaches threshold during this specific invoice: tax strictly on the incremental excess
|
|
456
|
+
taxableBasePaise = cumulativeAfterPaise - thresholdPaise;
|
|
457
|
+
isThresholdBreached = true;
|
|
458
|
+
}
|
|
459
|
+
} else if (archetype === 'CLIFF_THRESHOLD') {
|
|
460
|
+
if (cumulativeAfterPaise > thresholdPaise) {
|
|
461
|
+
taxableBasePaise = currentInvoicePaise;
|
|
462
|
+
isThresholdBreached = true;
|
|
463
|
+
}
|
|
464
|
+
} else if (archetype === 'FLAT_RATE') {
|
|
465
|
+
taxableBasePaise = currentInvoicePaise;
|
|
466
|
+
isThresholdBreached = true;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// 7. Rate Resolution & 5-Stage Rational Arithmetic
|
|
470
|
+
const standardRateBps = ruleAst.computation?.standardRateBps ?? 10; // Default 10 bps (0.1%)
|
|
471
|
+
const effectiveRateBps = overrideRateBps !== null ? overrideRateBps : standardRateBps;
|
|
472
|
+
|
|
473
|
+
let lineItemTdsPaise = 0n;
|
|
474
|
+
|
|
475
|
+
if (taxableBasePaise > 0n && effectiveRateBps > 0) {
|
|
476
|
+
// Stage 1 & 2: Exact Rational Multiplication & Remainder Extraction
|
|
477
|
+
const { quotientPaise, subPaisaRemainder } = multiplyRational(taxableBasePaise, effectiveRateBps, 10000n);
|
|
478
|
+
|
|
479
|
+
// Stage 3: Currency Quantization (Commercial Paisa Rounding)
|
|
480
|
+
lineItemTdsPaise = quantizeCurrencyPaisa(quotientPaise, subPaisaRemainder, 10000n);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
// Stage 4: Statutory Assessment Rounding (Section 288B / 516)
|
|
484
|
+
const assessmentRoundingPolicy = temporal.isAct2025 ? 'INCOME_TAX_ACT_2025_SEC516' : 'INCOME_TAX_ACT_1961_SEC288B';
|
|
485
|
+
const assessmentTdsPaise = roundStatutoryAssessment(lineItemTdsPaise, assessmentRoundingPolicy);
|
|
486
|
+
|
|
487
|
+
// 8. Final Fiduciary Return Formulation
|
|
488
|
+
const taxableBaseInr = fromBigIntPaise(taxableBasePaise);
|
|
489
|
+
const calculatedTdsInr = fromBigIntPaise(lineItemTdsPaise);
|
|
490
|
+
const netWireInr = Math.max(0, rawInvoiceInr - calculatedTdsInr);
|
|
491
|
+
|
|
492
|
+
result.invoiceAmountInr = rawInvoiceInr;
|
|
493
|
+
result.taxableBase = taxableBaseInr;
|
|
494
|
+
result.tdsRatePercent = effectiveRateBps / 100;
|
|
495
|
+
result.calculatedTdsInr = calculatedTdsInr;
|
|
496
|
+
result.assessmentRoundedTdsInr = fromBigIntPaise(assessmentTdsPaise);
|
|
497
|
+
result.netWireInr = netWireInr;
|
|
498
|
+
result.isTdsApplicable = calculatedTdsInr > 0;
|
|
499
|
+
result.isSection194QApplicable = isThresholdBreached;
|
|
500
|
+
result.disposition = isThresholdBreached ? 'TAX_DEDUCTIBLE' : 'THRESHOLD_NOT_BREACHED';
|
|
501
|
+
result.statutoryDisposition = isThresholdBreached ? 'SECTION_194Q_DEDUCTION_MANDATORY' : 'EXEMPT_BELOW_STATUTORY_THRESHOLD';
|
|
502
|
+
|
|
503
|
+
return result;
|
|
504
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MUDRAFORGE UNIVERSAL STATUTORY VIRTUAL MACHINE (USVM)
|
|
3
|
+
* Zero-dependency declarative calculation runtime for statutory tax & accounting.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { fileURLToPath } from 'node:url';
|
|
9
|
+
|
|
10
|
+
export {
|
|
11
|
+
evaluateStatutoryAst,
|
|
12
|
+
resolveTemporalCrystallization
|
|
13
|
+
} from './evaluation_vm.js';
|
|
14
|
+
|
|
15
|
+
export {
|
|
16
|
+
validateStatutoryAst,
|
|
17
|
+
StatutoryAstValidationError,
|
|
18
|
+
MATH_ARCHETYPES,
|
|
19
|
+
TRIGGER_POLICIES,
|
|
20
|
+
ROUNDING_POLICIES,
|
|
21
|
+
TYPED_OPERATORS
|
|
22
|
+
} from './schemas/statutory_ast_schema.js';
|
|
23
|
+
|
|
24
|
+
export {
|
|
25
|
+
toBigIntPaise,
|
|
26
|
+
fromBigIntPaise,
|
|
27
|
+
multiplyRational,
|
|
28
|
+
quantizeCurrencyPaisa,
|
|
29
|
+
roundStatutoryAssessment
|
|
30
|
+
} from './math_rational.js';
|
|
31
|
+
|
|
32
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
33
|
+
const __dirname = path.dirname(__filename);
|
|
34
|
+
|
|
35
|
+
const RULE_MAP = new Map();
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Loads and caches an AST rule by section key.
|
|
39
|
+
*/
|
|
40
|
+
export function getStatutoryAst(sectionKey) {
|
|
41
|
+
const key = String(sectionKey || '').toUpperCase().trim();
|
|
42
|
+
if (RULE_MAP.has(key)) return RULE_MAP.get(key);
|
|
43
|
+
|
|
44
|
+
const rulesDir = path.resolve(__dirname, 'rules');
|
|
45
|
+
if (!fs.existsSync(rulesDir)) return null;
|
|
46
|
+
|
|
47
|
+
// Search rules dir for matching sectionId or filename
|
|
48
|
+
const files = fs.readdirSync(rulesDir).filter(f => f.endsWith('.json'));
|
|
49
|
+
for (const f of files) {
|
|
50
|
+
try {
|
|
51
|
+
const raw = fs.readFileSync(path.join(rulesDir, f), 'utf8');
|
|
52
|
+
const ast = JSON.parse(raw);
|
|
53
|
+
if (
|
|
54
|
+
ast.sectionId?.toUpperCase() === key ||
|
|
55
|
+
ast.statutorySection?.toUpperCase().includes(key) ||
|
|
56
|
+
f.toUpperCase().includes(key.replace(/[^A-Z0-9]/g, '_'))
|
|
57
|
+
) {
|
|
58
|
+
RULE_MAP.set(key, ast);
|
|
59
|
+
return ast;
|
|
60
|
+
}
|
|
61
|
+
} catch (_) {}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return null;
|
|
65
|
+
}
|