@mudraforge/statutory 2.8.1 → 2.10.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 +49 -0
- package/README.md +112 -130
- package/SECURITY.md +2 -2
- package/SKILL.md +7 -2
- package/bin/cli.js +215 -8
- package/calculators.d.ts +1 -0
- package/calculators.js +1 -0
- package/docs/ARCHITECTURE.md +8 -8
- package/docs/ARCHITECTURE_DECISIONS.md +4 -3
- package/docs/CATALOG.md +259 -64
- package/docs/CORPUS_QUALITY.md +14 -9
- package/docs/DOMAIN_BREADTH_AND_DEPTH_COMPARISON.md +1 -1
- package/docs/ENTERPRISE_GOVERNANCE.md +81 -22
- package/docs/PILOT_EVALUATION_GUIDE.md +155 -90
- package/docs/PROVENANCE.md +4 -4
- package/docs/VERIFICATION.md +7 -7
- package/okf/corporate_ma_restructuring/loss_carryforward_sec79/circulars/cbdt_circular_09_2020_startup_loss_continuity.md +37 -0
- package/okf/corporate_ma_restructuring/loss_carryforward_sec79/forms/section_79_shareholding_continuity_schedule.json +51 -0
- package/okf/corporate_ma_restructuring/loss_carryforward_sec79/section.json +66 -0
- package/okf/direct_tax/corporate_tax_115baa_115jb/circulars/cbdt_circular_29_2019_concessional_tax_regime.md +35 -0
- package/okf/direct_tax/corporate_tax_115baa_115jb/forms/form_10ic_concessional_tax_election.json +31 -0
- package/okf/direct_tax/corporate_tax_115baa_115jb/section.json +72 -0
- package/okf/direct_tax/special_rates_non_filer_206ab/section.json +7 -6
- package/okf/indirect_tax_gst/export_services_lut_sec16/circulars/cbic_circular_161_2021_intermediary_clarification.md +37 -0
- package/okf/indirect_tax_gst/export_services_lut_sec16/forms/lut_form_gst_rfnd_11_declaration.json +58 -0
- package/okf/indirect_tax_gst/export_services_lut_sec16/section.json +66 -0
- package/okf/international_tax/foreign_exchange_indas_21_109/circulars/mca_indas_21_foreign_exchange_rates.md +39 -0
- package/okf/international_tax/foreign_exchange_indas_21_109/forms/indas_109_cash_flow_hedge_reserve_schedule.json +45 -0
- package/okf/international_tax/foreign_exchange_indas_21_109/section.json +63 -0
- package/okf/international_tax/transfer_pricing_alp_92c/circulars/cbdt_notification_86_2015_range_concept_rule_10ca.md +54 -0
- package/okf/international_tax/transfer_pricing_alp_92c/forms/form_3ceb_transfer_pricing_report.json +67 -0
- package/okf/international_tax/transfer_pricing_alp_92c/section.json +72 -0
- package/okf/manifest.json +33 -8
- package/package.json +48 -1
- package/src/calculators.js +780 -0
- package/src/constants.js +10 -0
- package/src/index.js +235 -46
- package/src/outcomes.js +483 -0
- package/types/index.d.ts +69 -0
package/src/outcomes.js
ADDED
|
@@ -0,0 +1,483 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Canonical Statutory Outcomes & Semantic Disposition Dictionary.
|
|
3
|
+
* Educates developers and autonomous AI agents on all possible non-numerical
|
|
4
|
+
* states, legal preconditions, missing facts, and required downstream actions.
|
|
5
|
+
*
|
|
6
|
+
* Implements hybrid offline-first resolution with graceful edge synchronization.
|
|
7
|
+
*
|
|
8
|
+
* @license MIT
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { STATUTORY_CLIENT_VERSION } from './constants.js';
|
|
12
|
+
|
|
13
|
+
export const STATUTORY_OUTCOMES_DICTIONARY = [
|
|
14
|
+
{
|
|
15
|
+
code: 'DETERMINED_APPLICABLE',
|
|
16
|
+
aliases: ['APPLICABLE', 'STATUTORY_WITHHOLDING_APPLICABLE', 'APPLICABLE_STANDARD_RATE'],
|
|
17
|
+
category: 'STATUTORY_WITHHOLDING',
|
|
18
|
+
title: 'Statutory Obligation Established',
|
|
19
|
+
description: 'Statutory deduction or tax withholding obligation is actively established on the transaction.',
|
|
20
|
+
triggerCondition: 'All statutory criteria met: cumulative FY threshold crossed, buyer/seller eligibility verified, and statute actively in force.',
|
|
21
|
+
recommendedAction: 'Deduct statutory tax at rate in force from basic invoice amount; credit vendor payable net of TDS; credit TDS payable.',
|
|
22
|
+
remedy: 'Proceed with standard invoice booking. Formulate double-entry voucher: Debit Expense, Debit Input GST, Credit Vendor Payable (Net), Credit TDS Payable (Section rate).',
|
|
23
|
+
consequenceIfUnresolved: 'Failure to deduct leads to interest under Section 201(1A) at 1% per month and 30% expenditure disallowance under Section 40(a)(ia).',
|
|
24
|
+
applicableProvisions: ['194Q', '194C', '194J', '194I', '194H', '195', '51', '393'],
|
|
25
|
+
governingAuthority: 'Direct Tax / Central Board of Direct Taxes (CBDT)'
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
code: 'DETERMINED_THRESHOLD_NOT_MET',
|
|
29
|
+
aliases: ['EXEMPT', 'BELOW_THRESHOLD', 'EXEMPT_THRESHOLD_NOT_MET', 'THRESHOLD_NOT_CROSSED'],
|
|
30
|
+
category: 'STATUTORY_SAFE_HARBOR',
|
|
31
|
+
title: 'Statutory Spend Below Monetary Threshold',
|
|
32
|
+
description: 'Transaction falls below the legal monetary threshold; no tax withholding is legally required.',
|
|
33
|
+
triggerCondition: 'Cumulative FY purchases under ₹50 Lakhs (Section 194Q), single invoice under ₹30,000 / aggregate under ₹1,00,000 (Section 194C), or aggregate fees under ₹30,000 (Section 194J).',
|
|
34
|
+
recommendedAction: 'Disburse invoice in full; record transaction amount toward cumulative financial year spend tracker for subsequent invoice gates.',
|
|
35
|
+
remedy: 'Disburse payment without tax deduction. Ensure the cumulative spend accumulator for this vendor is incremented in ERP to catch future threshold crossings.',
|
|
36
|
+
consequenceIfUnresolved: 'Over-withholding harms vendor commercial relations, triggers vendor dispute tickets, and requires cumbersome TDS refund processing.',
|
|
37
|
+
applicableProvisions: ['194Q', '194C', '194J', '194H', '393'],
|
|
38
|
+
governingAuthority: 'Direct Tax / Central Board of Direct Taxes (CBDT)'
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
code: 'INDETERMINATE_BUYER_TURNOVER_UNVERIFIED',
|
|
42
|
+
aliases: ['BUYER_TURNOVER_UNVERIFIED', 'TURNOVER_UNVERIFIED', 'EXEMPT_BELOW_BUYER_TURNOVER_THRESHOLD'],
|
|
43
|
+
category: 'EVIDENTIARY_AMBIGUITY',
|
|
44
|
+
title: 'Buyer Turnover Precondition Unverified',
|
|
45
|
+
description: 'Preceding financial year buyer turnover is unverified; statutory applicability cannot be determined.',
|
|
46
|
+
triggerCondition: 'Section 194Q requires buyer turnover exceeding ₹10 Crore in the financial year immediately preceding the transaction year (Section 194Q(1) Explanation).',
|
|
47
|
+
recommendedAction: 'Fail closed; hold automated withholding determination until finance or vendor management records verified audited turnover.',
|
|
48
|
+
remedy: 'Supply buyer turnover in Crores INR (buyerTurnoverCr) verified against the company audited financial statements or income tax return for the preceding FY.',
|
|
49
|
+
missingFacts: ['buyerTurnoverCr', 'buyerTurnoverPrecedingFy'],
|
|
50
|
+
missingFactsExplanation: {
|
|
51
|
+
buyerTurnoverCr: {
|
|
52
|
+
type: 'number',
|
|
53
|
+
description: 'Gross turnover / sales / gross receipts of the buyer entity in the financial year preceding the purchase, expressed in Crores INR.',
|
|
54
|
+
example: 15.5,
|
|
55
|
+
source: 'Audited Profit & Loss Statement / Income Tax Return Form ITR-6'
|
|
56
|
+
},
|
|
57
|
+
buyerTurnoverPrecedingFy: {
|
|
58
|
+
type: 'string',
|
|
59
|
+
description: 'The financial year identifier for the preceding turnover evaluation.',
|
|
60
|
+
example: '2024-25',
|
|
61
|
+
source: 'Corporate Tax Master'
|
|
62
|
+
}
|
|
63
|
+
},
|
|
64
|
+
consequenceIfUnresolved: 'Autonomous payment execution halts fail-closed. If turnover actually exceeds ₹10 Cr and TDS is missed, buyer is treated as assessee-in-default.',
|
|
65
|
+
applicableProvisions: ['194Q', '393'],
|
|
66
|
+
governingAuthority: 'Income-tax Act, 1961 Section 194Q(1) Explanation'
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
code: 'INDETERMINATE_PENDING_TRANSACTION_DATE',
|
|
70
|
+
aliases: ['TRANSACTION_DATE_REQUIRED_FOR_REGIME_DETERMINATION', 'PENDING_TRANSACTION_DATE', 'MISSING_TRANSACTION_DATE', 'REGIME_DETERMINATION_PENDING'],
|
|
71
|
+
category: 'TEMPORAL_AMBIGUITY',
|
|
72
|
+
title: 'Transaction Timestamp Required for Regime Resolution',
|
|
73
|
+
description: 'Transaction date, credit date, and payment date are missing; statutory governing Act cannot be determined.',
|
|
74
|
+
triggerCondition: 'Dual-act boundary transition: Income-tax Act, 1961 applies to events crystallized on or before 31 March 2026; Income-tax Act, 2025 applies thereafter.',
|
|
75
|
+
recommendedAction: 'Prompt upstream ERP or invoice capture system for invoice booking/credit timestamp before evaluating legal rules.',
|
|
76
|
+
remedy: 'Provide creditDate (date invoice is booked in accounting ledger) or paymentDate (date funds are disbursed). The earlier event crystallizes the governing Act.',
|
|
77
|
+
missingFacts: ['creditDate', 'paymentDate', 'transactionDate'],
|
|
78
|
+
missingFactsExplanation: {
|
|
79
|
+
creditDate: {
|
|
80
|
+
type: 'string (ISO 8601 YYYY-MM-DD)',
|
|
81
|
+
description: 'Date when amount is credited to the account of the payee or suspense account in corporate books.',
|
|
82
|
+
example: '2026-03-25',
|
|
83
|
+
source: 'ERP General Ledger Booking Timestamp'
|
|
84
|
+
},
|
|
85
|
+
paymentDate: {
|
|
86
|
+
type: 'string (ISO 8601 YYYY-MM-DD)',
|
|
87
|
+
description: 'Date of actual cash disbursement, cheque issuance, or bank wire dispatch.',
|
|
88
|
+
example: '2026-04-10',
|
|
89
|
+
source: 'Bank Rail Payment Instruction'
|
|
90
|
+
},
|
|
91
|
+
transactionDate: {
|
|
92
|
+
type: 'string (ISO 8601 YYYY-MM-DD)',
|
|
93
|
+
description: 'Supplier invoice date on the face of the bill.',
|
|
94
|
+
example: '2026-03-20',
|
|
95
|
+
source: 'Vendor Invoice Document Header'
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
consequenceIfUnresolved: 'Statutory determination cannot establish whether return forms belong to Form 26Q (1961 Act) or successor forms under Income-tax Act, 2025.',
|
|
99
|
+
applicableProvisions: ['194Q', '194C', '194J', '194I', '194H', '393'],
|
|
100
|
+
governingAuthority: 'Income-tax Act, 2025 Section 536 read with Section 6 General Clauses Act, 1897'
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
code: 'SUNSET_OMITTED',
|
|
104
|
+
aliases: ['OMITTED', 'SUNSET', 'REPEALED', 'STATUTE_SUNSET_REPEALED'],
|
|
105
|
+
category: 'LEGISLATIVE_SUNSET',
|
|
106
|
+
title: 'Statute Omitted / Repealed by Finance Act',
|
|
107
|
+
description: 'Statutory provision has been omitted or repealed by Parliament; tax deduction or collection under this section is prohibited.',
|
|
108
|
+
triggerCondition: 'Transaction date falls on or after statutory sunset effective date (e.g. Section 206C(1H) omitted w.e.f. 1 April 2025; Section 206AB omitted w.e.f. 1 April 2025).',
|
|
109
|
+
recommendedAction: 'Halt tax withholding or collection under this section; update ERP tax engine to prevent obsolete tax collection.',
|
|
110
|
+
remedy: 'Update vendor master and ERP billing tax codes. For post-1 April 2025 goods transactions, evaluate Section 194Q buyer withholding instead of seller TCS.',
|
|
111
|
+
consequenceIfUnresolved: 'Charging repealed taxes creates illegal collections under Article 265 of the Constitution of India and damages customer commercial trust.',
|
|
112
|
+
applicableProvisions: ['206C(1H)', '206AB'],
|
|
113
|
+
governingAuthority: 'Finance Act, 2024 / Finance Act, 2025'
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
code: 'PENALTY_RATE_MANDATED',
|
|
117
|
+
aliases: ['PENALTY_RATE', 'INOPERATIVE_PAN_PENALTY', 'PAN_PENALTY_FLOOR', 'SECTION_206AA_PENALTY'],
|
|
118
|
+
category: 'STATUTORY_PENALTY_RATE',
|
|
119
|
+
title: 'Mandatory Non-Furnishing Penalty Rate',
|
|
120
|
+
description: 'Higher penalty rate mandatory due to non-furnishing or invalid/inoperative PAN under Section 206AA.',
|
|
121
|
+
triggerCondition: 'Payee PAN missing, invalid ISO Mod-36 checksum, or designated inoperative due to Aadhaar non-linking.',
|
|
122
|
+
recommendedAction: 'Withhold at statutory penalty floor rate (5% for Section 194Q; 20% for Section 194C, 194J, 194I); flag vendor master for PAN rectification.',
|
|
123
|
+
remedy: 'Apply mandatory penalty rate immediately. Concurrently issue automated notification to vendor requesting valid, operative PAN with Aadhaar linkage proof.',
|
|
124
|
+
missingFactsExplanation: {
|
|
125
|
+
pan: {
|
|
126
|
+
type: 'string (10-char alphanumeric)',
|
|
127
|
+
description: 'Permanent Account Number of the vendor with verified ISO Mod-36 syntax and active/operative filing status.',
|
|
128
|
+
example: 'AAACR5055K',
|
|
129
|
+
source: 'Income Tax Department PAN Verification API / Form 60'
|
|
130
|
+
}
|
|
131
|
+
},
|
|
132
|
+
consequenceIfUnresolved: 'Deducting standard rates on inoperative PANs makes the deductor liable for short-deduction demand plus penal interest under Section 201(1A).',
|
|
133
|
+
applicableProvisions: ['194Q', '194C', '194J', '194I', '194H', '206AA', '393'],
|
|
134
|
+
governingAuthority: 'Income-tax Act, 1961 Section 206AA'
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
code: 'DISQUALIFIED_FROM_ZERO_RATING',
|
|
138
|
+
aliases: ['DISQUALIFIED_ZERO_RATING', 'ZERO_RATING_FAILED', 'EXPORT_ZERO_RATING_DISQUALIFIED'],
|
|
139
|
+
category: 'INDIRECT_TAX_DISQUALIFICATION',
|
|
140
|
+
title: 'Export Zero-Rating Disqualified',
|
|
141
|
+
description: 'Supply disqualified from 0% zero-rated export treatment under Letter of Undertaking (LUT).',
|
|
142
|
+
triggerCondition: 'Non-receipt of convertible foreign exchange or authorized RBI INR within statutory timeline (IGST Sec 2(6)(iv)) or classification as Intermediary (IGST Sec 2(13)).',
|
|
143
|
+
recommendedAction: 'Levy standard 18% Integrated GST (IGST); do not issue zero-rated LUT export invoice.',
|
|
144
|
+
remedy: 'Obtain FIRC (Foreign Inward Remittance Certificate) / BRC or ensure supplier acts as principal service provider rather than intermediary broker.',
|
|
145
|
+
missingFactsExplanation: {
|
|
146
|
+
isConvertibleForexReceivedOrRbiInr: {
|
|
147
|
+
type: 'boolean',
|
|
148
|
+
description: 'Affirmation that payment has been realized in convertible foreign currency or Indian Rupees permitted by RBI.',
|
|
149
|
+
example: true,
|
|
150
|
+
source: 'Bank Swift Inward Advice / EDPMS Inward Remittance'
|
|
151
|
+
}
|
|
152
|
+
},
|
|
153
|
+
consequenceIfUnresolved: 'Claiming zero-rating without satisfying export conditions results in IGST demand with 18% interest and 100% penalty under Section 122 CGST Act.',
|
|
154
|
+
applicableProvisions: ['IGST-SEC-16-LUT', '8'],
|
|
155
|
+
governingAuthority: 'Integrated Goods and Services Tax Act, 2017 Section 2(6) & Section 16'
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
code: 'REQUIRES_HUMAN_REVIEW',
|
|
159
|
+
aliases: ['HUMAN_REVIEW_REQUIRED', 'COMPOSITE_AMBIGUITY_REVIEW', 'MANUAL_APPROVAL_REQUIRED'],
|
|
160
|
+
category: 'COMPOSITE_AMBIGUITY',
|
|
161
|
+
title: 'Composite Contract Evidentiary Ambiguity',
|
|
162
|
+
description: 'Contractual ambiguity or composite scope of work prevents autonomous deterministic classification.',
|
|
163
|
+
triggerCondition: 'Mixed supplies (Section 8 CGST), composite contracts spanning both goods and on-site engineering labour, or unitemized invoices.',
|
|
164
|
+
recommendedAction: 'Route invoice to corporate tax controller with highlighted ambiguity and evidence requirements; do not auto-disburse.',
|
|
165
|
+
remedy: 'Corporate tax counsel must review the underlying Statement of Work (SOW) to classify the principal supply (e.g. pure works contract vs supply of equipment).',
|
|
166
|
+
consequenceIfUnresolved: 'Misclassifying composite services as goods leads to wrong tax rates, disallowed Input Tax Credits, and cross-statute withholding disputes.',
|
|
167
|
+
applicableProvisions: ['8', '194C', '194J', '17(5)'],
|
|
168
|
+
governingAuthority: 'Central Goods and Services Tax Act, 2017 Section 8'
|
|
169
|
+
},
|
|
170
|
+
{
|
|
171
|
+
code: 'MISSING_EVIDENCE_HOLD',
|
|
172
|
+
aliases: ['CERTIFICATE_MISSING', 'EVIDENCE_HOLD', 'MISSING_STATUTORY_FILING'],
|
|
173
|
+
category: 'DOCUMENTARY_PRECONDITION',
|
|
174
|
+
title: 'Statutory Exemption Certificate Missing',
|
|
175
|
+
description: 'Statutory prerequisite filing or exemption certificate not on file; automated claim blocked fail-closed.',
|
|
176
|
+
triggerCondition: 'Missing Section 197 lower-withholding certificate, missing Form 10F / TRC for cross-border remittance, or missing Schedule K-3.',
|
|
177
|
+
recommendedAction: 'Place payment authorization lease on hold; issue automated vendor request for required statutory certificate.',
|
|
178
|
+
remedy: 'Upload verified statutory exemption document to tenant vault. For lower withholding, verify certificate validity on Traces portal.',
|
|
179
|
+
missingFacts: ['lowerWithholdingCertificate', 'taxResidencyCertificate', 'form10F', 'scheduleK3'],
|
|
180
|
+
missingFactsExplanation: {
|
|
181
|
+
lowerWithholdingCertificate: {
|
|
182
|
+
type: 'string (Certificate Number)',
|
|
183
|
+
description: 'Assessing Officer certificate issued under Section 197 specifying lower/nil withholding percentage and valid financial year.',
|
|
184
|
+
example: 'CERT197-2025-00982',
|
|
185
|
+
source: 'TRACES Portal Verification API'
|
|
186
|
+
},
|
|
187
|
+
taxResidencyCertificate: {
|
|
188
|
+
type: 'string / file',
|
|
189
|
+
description: 'Tax Residency Certificate (TRC) issued by government of non-resident vendor home country under Section 90(4).',
|
|
190
|
+
example: 'TRC-IRS-FORM-6166',
|
|
191
|
+
source: 'Vendor Tax Department'
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
consequenceIfUnresolved: 'Withholding at concession rates without certificate makes the deductor liable for the full statutory rate difference plus mandatory interest.',
|
|
195
|
+
applicableProvisions: ['195', '194C', 'IRC-901-904'],
|
|
196
|
+
governingAuthority: 'Income-tax Act, 1961 Section 195 & Section 197'
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
code: 'INDETERMINATE_DISPUTE_UNRESOLVED',
|
|
200
|
+
aliases: ['UNRESOLVED_WRITTEN_OBJECTION', 'DISPUTE_CLOCK_PAUSED', 'WRITTEN_OBJECTION_FILED'],
|
|
201
|
+
category: 'COMMERCIAL_DISPUTE_AMBIGUITY',
|
|
202
|
+
title: 'Written Commercial Dispute Objection Pending',
|
|
203
|
+
description: 'Statutory 15-day or 45-day payment clock is paused due to formal written objection filed within 15 days under MSMED Act Section 2(h)(ii).',
|
|
204
|
+
triggerCondition: 'Buyer filed formal written defect notice or commercial objection within 15 days of invoice receipt.',
|
|
205
|
+
recommendedAction: 'Halt statutory interest clock; resolve vendor dispute or obtain defect rectification confirmation.',
|
|
206
|
+
remedy: 'Resolve buyer objection or deliver defect removal notice. Once formal objection is removed or settled, supply objectionRemovalDate to resume statutory clock.',
|
|
207
|
+
missingFacts: ['objectionRemovalDate'],
|
|
208
|
+
missingFactsExplanation: {
|
|
209
|
+
objectionRemovalDate: {
|
|
210
|
+
type: 'string (ISO 8601 YYYY-MM-DD)',
|
|
211
|
+
description: 'Date when the supplier rectifies the defect or buyer formally withdraws written objection.',
|
|
212
|
+
example: '2025-05-10',
|
|
213
|
+
source: 'Dispute Settlement Agreement / Defect Rectification Note'
|
|
214
|
+
}
|
|
215
|
+
},
|
|
216
|
+
consequenceIfUnresolved: 'Delay in resolving dispute risks retroactive compounding penal interest at 3x RBI rate if objection is ultimately dismissed or deemed invalid.',
|
|
217
|
+
applicableProvisions: ['MSMED-15-16', 'MCA-MSME-1'],
|
|
218
|
+
governingAuthority: 'Micro, Small and Medium Enterprises Development Act, 2006 Section 2(h)(ii)'
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
code: 'INDETERMINATE_RECIPIENT_COUNTRY_REQUIRED',
|
|
222
|
+
aliases: ['MISSING_RECIPIENT_COUNTRY_FOR_DTAA', 'MISSING_COUNTRY', 'DTAA_COUNTRY_REQUIRED'],
|
|
223
|
+
category: 'CROSS_BORDER_TREATY_AMBIGUITY',
|
|
224
|
+
title: 'Recipient Tax Jurisdiction Country Required for DTAA',
|
|
225
|
+
description: 'Cross-border remittance withholding cannot determine DTAA treaty relief without recipient tax residency country.',
|
|
226
|
+
triggerCondition: 'Section 195 remittance payment without recipient country ISO code.',
|
|
227
|
+
recommendedAction: 'Hold remittance authorization; obtain payee tax residency jurisdiction.',
|
|
228
|
+
remedy: 'Provide ISO 3166-1 alpha-2 country code for the non-resident recipient (e.g. US, SG, DE, GB) and verify Tax Residency Certificate (TRC).',
|
|
229
|
+
missingFacts: ['country'],
|
|
230
|
+
missingFactsExplanation: {
|
|
231
|
+
country: {
|
|
232
|
+
type: 'string (ISO 3166-1 2-letter)',
|
|
233
|
+
description: 'Tax residency country of the foreign payee.',
|
|
234
|
+
example: 'US',
|
|
235
|
+
source: 'Tax Residency Certificate (TRC) Form 10F'
|
|
236
|
+
}
|
|
237
|
+
},
|
|
238
|
+
consequenceIfUnresolved: 'Without country verification, statutory withholding must default to higher domestic rate (typically 20% or 40% plus applicable surcharge).',
|
|
239
|
+
applicableProvisions: ['195'],
|
|
240
|
+
governingAuthority: 'Income-tax Act, 1961 Section 195 read with Section 90'
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
code: 'MSME_DELAYED_PAYMENT_INTEREST_DUE',
|
|
244
|
+
aliases: ['MSMED_INTEREST_DUE', 'PENAL_INTEREST_DUE', 'SECTION_16_INTEREST'],
|
|
245
|
+
category: 'COMMERCIAL_STATUTORY_PENALTY',
|
|
246
|
+
title: 'MSME Statutory Delayed Payment Interest Accrued',
|
|
247
|
+
description: 'Payment settlement exceeds statutory 15-day or 45-day window under Section 15 of MSMED Act, 2006; mandatory interest attached.',
|
|
248
|
+
triggerCondition: 'Payment to verified Micro or Small enterprise delayed past statutory clock (15 days oral / max 45 days written contract).',
|
|
249
|
+
recommendedAction: 'Compute compound monthly interest at 3x RBI repo rate under Section 16; flag Section 43B(h) tax disallowance risk at fiscal year-end.',
|
|
250
|
+
remedy: 'Disburse principal payment immediately to cap interest accrual. Calculate compound penal interest using MSMED Section 16 calculator and book to expense.',
|
|
251
|
+
consequenceIfUnresolved: 'Section 43B(h) permanently disallows income tax expense deduction if unpaid at year-end; MSMED interest is inadmissible as a business deduction under Section 23.',
|
|
252
|
+
applicableProvisions: ['MSMED-15-16', 'MSMED-SEC-22', 'MCA-MSME-1'],
|
|
253
|
+
governingAuthority: 'Micro, Small and Medium Enterprises Development Act, 2006 Sections 15 & 16'
|
|
254
|
+
}
|
|
255
|
+
];
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Retrieve statutory outcomes and disposition codes for developers and AI agents.
|
|
259
|
+
* Evaluates local OKF dictionary first, with optional edge synchronization when online.
|
|
260
|
+
*
|
|
261
|
+
* @param {string} [provision] Optional section code or provision alias (e.g. '194Q', '194C', 'MSMED-15-16')
|
|
262
|
+
* @param {Object} [options]
|
|
263
|
+
* @param {boolean} [options.offline=false] Force offline local evaluation
|
|
264
|
+
* @param {boolean} [options.verbose=false] Include full remediation steps and parameter schemas
|
|
265
|
+
* @param {string} [options.baseUrl] Cloudflare edge base URL (default: https://api.mudraforge.in)
|
|
266
|
+
* @param {number} [options.timeoutMs=2500] Edge request timeout in milliseconds
|
|
267
|
+
* @returns {Promise<{
|
|
268
|
+
* status: 'SUCCESS',
|
|
269
|
+
* source: 'LIVE_EDGE' | 'LOCAL_OKF_SUBSTRATE',
|
|
270
|
+
* provision: string | null,
|
|
271
|
+
* totalOutcomes: number,
|
|
272
|
+
* outcomes: typeof STATUTORY_OUTCOMES_DICTIONARY,
|
|
273
|
+
* version: string,
|
|
274
|
+
* offlineFallback?: boolean
|
|
275
|
+
* }>}
|
|
276
|
+
*/
|
|
277
|
+
export async function getStatutoryOutcomes(provision = null, options = {}) {
|
|
278
|
+
const normProvision = provision ? String(provision).trim().toUpperCase() : null;
|
|
279
|
+
const isOffline = options.offline === true ||
|
|
280
|
+
(typeof process !== 'undefined' && process.env?.MUDRA_OFFLINE === '1');
|
|
281
|
+
|
|
282
|
+
// Local filter function
|
|
283
|
+
const filterLocal = (prov) => {
|
|
284
|
+
if (!prov) return STATUTORY_OUTCOMES_DICTIONARY;
|
|
285
|
+
return STATUTORY_OUTCOMES_DICTIONARY.filter(item => {
|
|
286
|
+
if (!item.applicableProvisions || item.applicableProvisions.length === 0) return true;
|
|
287
|
+
return item.applicableProvisions.some(p => p.toUpperCase() === prov || prov.includes(p.toUpperCase()));
|
|
288
|
+
});
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
if (isOffline) {
|
|
292
|
+
const outcomes = filterLocal(normProvision);
|
|
293
|
+
return {
|
|
294
|
+
status: 'SUCCESS',
|
|
295
|
+
source: 'LOCAL_OKF_SUBSTRATE',
|
|
296
|
+
provision: normProvision,
|
|
297
|
+
totalOutcomes: outcomes.length,
|
|
298
|
+
outcomes,
|
|
299
|
+
version: STATUTORY_CLIENT_VERSION,
|
|
300
|
+
offlineFallback: false
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// Attempt edge synchronization
|
|
305
|
+
const baseUrl = options.baseUrl || 'https://api.mudraforge.in';
|
|
306
|
+
const edgeUrl = new URL('/api/v1/statutory/outcomes', baseUrl);
|
|
307
|
+
if (normProvision) {
|
|
308
|
+
edgeUrl.searchParams.set('provision', normProvision);
|
|
309
|
+
}
|
|
310
|
+
if (options.verbose) {
|
|
311
|
+
edgeUrl.searchParams.set('verbose', 'true');
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
try {
|
|
315
|
+
const timeoutMs = options.timeoutMs || 2500;
|
|
316
|
+
const res = await fetch(edgeUrl.toString(), {
|
|
317
|
+
method: 'GET',
|
|
318
|
+
headers: {
|
|
319
|
+
'Accept': 'application/json',
|
|
320
|
+
'User-Agent': `@mudraforge/statutory/${STATUTORY_CLIENT_VERSION} (CLI/SDK Outcomes)`
|
|
321
|
+
},
|
|
322
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
if (res.ok) {
|
|
326
|
+
const data = await res.json();
|
|
327
|
+
if (data && Array.isArray(data.outcomes)) {
|
|
328
|
+
return {
|
|
329
|
+
status: 'SUCCESS',
|
|
330
|
+
source: 'LIVE_EDGE',
|
|
331
|
+
provision: normProvision,
|
|
332
|
+
totalOutcomes: data.outcomes.length,
|
|
333
|
+
outcomes: data.outcomes,
|
|
334
|
+
version: data.version || STATUTORY_CLIENT_VERSION,
|
|
335
|
+
offlineFallback: false
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
} catch (_) {
|
|
340
|
+
// Fall through cleanly to local dictionary on network error, 404, or timeout
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// Graceful fallback to local substrate
|
|
344
|
+
const localOutcomes = filterLocal(normProvision);
|
|
345
|
+
return {
|
|
346
|
+
status: 'SUCCESS',
|
|
347
|
+
source: 'LOCAL_OKF_SUBSTRATE',
|
|
348
|
+
provision: normProvision,
|
|
349
|
+
totalOutcomes: localOutcomes.length,
|
|
350
|
+
outcomes: localOutcomes,
|
|
351
|
+
version: STATUTORY_CLIENT_VERSION,
|
|
352
|
+
offlineFallback: true
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Look up in-depth explanation, legal rationale, and remediation steps for a specific disposition or indeterminate code.
|
|
358
|
+
*
|
|
359
|
+
* @param {string} codeOrQuery Disposition code (e.g. 'BUYER_TURNOVER_UNVERIFIED', 'INDETERMINATE', 'PENALTY_RATE_MANDATED')
|
|
360
|
+
* @param {Object} [options]
|
|
361
|
+
* @returns {Object} Explanation result with matched outcome(s) and remediation instructions
|
|
362
|
+
*/
|
|
363
|
+
export function explainStatutoryDisposition(codeOrQuery, options = {}) {
|
|
364
|
+
if (!codeOrQuery || typeof codeOrQuery !== 'string' || !codeOrQuery.trim()) {
|
|
365
|
+
throw new Error('[@mudraforge/statutory] A valid disposition code or query string is required, e.g. "BUYER_TURNOVER_UNVERIFIED" or "INDETERMINATE".');
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
const query = codeOrQuery.trim().toUpperCase();
|
|
369
|
+
|
|
370
|
+
// Special case: "INDETERMINATE" returns all indeterminate outcomes
|
|
371
|
+
if (query === 'INDETERMINATE' || query === 'INDETERMINACY') {
|
|
372
|
+
const matched = STATUTORY_OUTCOMES_DICTIONARY.filter(item =>
|
|
373
|
+
item.code.startsWith('INDETERMINATE_') ||
|
|
374
|
+
item.category.includes('AMBIGUITY') ||
|
|
375
|
+
item.code === 'MISSING_EVIDENCE_HOLD' ||
|
|
376
|
+
item.code === 'REQUIRES_HUMAN_REVIEW'
|
|
377
|
+
);
|
|
378
|
+
return {
|
|
379
|
+
status: 'SUCCESS',
|
|
380
|
+
query,
|
|
381
|
+
matchType: 'CATEGORY_GROUP',
|
|
382
|
+
totalMatches: matched.length,
|
|
383
|
+
matches: matched,
|
|
384
|
+
summary: 'All system dispositions where automated tax determination halts fail-closed pending human review or missing evidentiary facts.'
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// Search by exact code or registered alias
|
|
389
|
+
let directMatch = STATUTORY_OUTCOMES_DICTIONARY.find(item =>
|
|
390
|
+
item.code === query ||
|
|
391
|
+
(item.aliases && item.aliases.some(a => a.toUpperCase() === query))
|
|
392
|
+
);
|
|
393
|
+
|
|
394
|
+
if (!directMatch) {
|
|
395
|
+
// Search by partial code, alias substring, or missingFact match
|
|
396
|
+
directMatch = STATUTORY_OUTCOMES_DICTIONARY.find(item =>
|
|
397
|
+
item.code.includes(query) ||
|
|
398
|
+
(item.aliases && item.aliases.some(a => a.toUpperCase().includes(query) || query.includes(a.toUpperCase()))) ||
|
|
399
|
+
(item.missingFacts && item.missingFacts.some(f => f.toUpperCase() === query))
|
|
400
|
+
);
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
if (directMatch) {
|
|
404
|
+
return {
|
|
405
|
+
status: 'SUCCESS',
|
|
406
|
+
query,
|
|
407
|
+
matchType: 'EXACT_OR_KEY_MATCH',
|
|
408
|
+
totalMatches: 1,
|
|
409
|
+
matches: [directMatch],
|
|
410
|
+
remedy: directMatch.remedy,
|
|
411
|
+
missingFactsExplanation: directMatch.missingFactsExplanation || null,
|
|
412
|
+
consequenceIfUnresolved: directMatch.consequenceIfUnresolved || null
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
// Fuzzy search across titles, descriptions, and categories
|
|
417
|
+
const fuzzyMatches = STATUTORY_OUTCOMES_DICTIONARY.filter(item =>
|
|
418
|
+
item.title.toUpperCase().includes(query) ||
|
|
419
|
+
item.description.toUpperCase().includes(query) ||
|
|
420
|
+
item.category.toUpperCase().includes(query)
|
|
421
|
+
);
|
|
422
|
+
|
|
423
|
+
return {
|
|
424
|
+
status: fuzzyMatches.length > 0 ? 'SUCCESS' : 'NOT_FOUND',
|
|
425
|
+
query,
|
|
426
|
+
matchType: fuzzyMatches.length > 0 ? 'FUZZY_MATCH' : 'NO_MATCH',
|
|
427
|
+
totalMatches: fuzzyMatches.length,
|
|
428
|
+
matches: fuzzyMatches,
|
|
429
|
+
availableCodes: STATUTORY_OUTCOMES_DICTIONARY.map(i => i.code)
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/**
|
|
434
|
+
* Automatically enriches a calculation result with canonical explanations, remediation steps,
|
|
435
|
+
* and missing fact schemas when the result is INDETERMINATE or when verbose is requested.
|
|
436
|
+
*
|
|
437
|
+
* @param {Object} result Calculation response or outcome object
|
|
438
|
+
* @param {Object} [options]
|
|
439
|
+
* @param {boolean} [options.verbose=false] Force rich explanatory fields on all dispositions
|
|
440
|
+
* @param {boolean} [options.explain=false] Alias for verbose
|
|
441
|
+
* @returns {Object} Enriched result object
|
|
442
|
+
*/
|
|
443
|
+
export function enrichWithOutcomeExplanation(result, options = {}) {
|
|
444
|
+
if (!result || typeof result !== 'object') return result;
|
|
445
|
+
|
|
446
|
+
const determination = result.statutoryDetermination || result.status || null;
|
|
447
|
+
const isIndeterminate = determination && (
|
|
448
|
+
determination === 'INDETERMINATE' ||
|
|
449
|
+
determination.startsWith('INDETERMINATE_') ||
|
|
450
|
+
determination === 'DISPUTE_CLOCK_PAUSED'
|
|
451
|
+
);
|
|
452
|
+
const hasReason = Boolean(result.indeterminacyReason);
|
|
453
|
+
const isVerbose = options.verbose === true || options.explain === true;
|
|
454
|
+
|
|
455
|
+
if (isIndeterminate || hasReason || isVerbose) {
|
|
456
|
+
const lookupKey = result.indeterminacyReason || determination;
|
|
457
|
+
if (lookupKey) {
|
|
458
|
+
try {
|
|
459
|
+
const expl = explainStatutoryDisposition(lookupKey);
|
|
460
|
+
if (expl && expl.matches && expl.matches.length > 0) {
|
|
461
|
+
const match = expl.matches[0];
|
|
462
|
+
const shouldAttachExplanation = options.attachExplanationProperty !== false &&
|
|
463
|
+
options.explain !== false &&
|
|
464
|
+
options.includeExplanation !== false;
|
|
465
|
+
if (shouldAttachExplanation && !result.explanation) {
|
|
466
|
+
result.explanation = match.description || result.regimeNotice || result.contingentNotice || null;
|
|
467
|
+
}
|
|
468
|
+
if (!result.remedy && match.remedy) {
|
|
469
|
+
result.remedy = match.remedy;
|
|
470
|
+
}
|
|
471
|
+
if (!result.consequenceIfUnresolved && match.consequenceIfUnresolved) {
|
|
472
|
+
result.consequenceIfUnresolved = match.consequenceIfUnresolved;
|
|
473
|
+
}
|
|
474
|
+
if (!result.missingFactsExplanation && match.missingFactsExplanation) {
|
|
475
|
+
result.missingFactsExplanation = match.missingFactsExplanation;
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
} catch (_) {}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
return result;
|
|
483
|
+
}
|
package/types/index.d.ts
CHANGED
|
@@ -142,6 +142,7 @@ export interface SyncResult {
|
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
export declare const DEFAULT_BASE_URL: string;
|
|
145
|
+
export declare const STATUTORY_CLIENT_VERSION: string;
|
|
145
146
|
export declare const DEFAULT_LOCAL_OKF_DIR: string;
|
|
146
147
|
|
|
147
148
|
export declare function getLocalManifest(okfDir?: string): any;
|
|
@@ -193,6 +194,13 @@ export declare class StatutoryClient {
|
|
|
193
194
|
diff(options?: StatutoryDiffOptions): StatutoryDiffResult;
|
|
194
195
|
verify(targetDir?: string): any;
|
|
195
196
|
doctor(options?: any): Promise<any>;
|
|
197
|
+
calculateCorporateTaxMatVsConcessional(payload?: any): any;
|
|
198
|
+
evaluatePlaceOfSupplyAndLut(payload?: any): any;
|
|
199
|
+
calculateTransferPricingAlp(payload?: any): any;
|
|
200
|
+
evaluateSection79LossContinuity(payload?: any): any;
|
|
201
|
+
revalueForeignExchangeIndAs109(payload?: any): any;
|
|
202
|
+
getOutcomes(provision?: string, options?: StatutoryOutcomesOptions): Promise<StatutoryOutcomesResult>;
|
|
203
|
+
explain(codeOrQuery: string, options?: any): any;
|
|
196
204
|
}
|
|
197
205
|
|
|
198
206
|
export interface CrosswalkOptions {
|
|
@@ -315,3 +323,64 @@ export declare const PAN_ENTITY_TYPES: Record<string, { code: string; name: stri
|
|
|
315
323
|
export declare function isValidGstinChecksum(gstin: string): boolean;
|
|
316
324
|
export declare function decodeTaxIdentifier(rawId: string): TaxIdentifierResult;
|
|
317
325
|
|
|
326
|
+
export declare function roundToPaisa(amount: number | string): number;
|
|
327
|
+
export declare function roundSection288B(amount: number | string): number;
|
|
328
|
+
export declare function calculateCorporateTaxMatVsConcessional(payload?: any): any;
|
|
329
|
+
export declare function evaluatePlaceOfSupplyAndLut(payload?: any): any;
|
|
330
|
+
export declare function calculateTransferPricingAlp(payload?: any): any;
|
|
331
|
+
export declare function evaluateSection79LossContinuity(payload?: any): any;
|
|
332
|
+
export declare function revalueForeignExchangeIndAs109(payload?: any): any;
|
|
333
|
+
|
|
334
|
+
export interface StatutoryOutcome {
|
|
335
|
+
code: string;
|
|
336
|
+
category: string;
|
|
337
|
+
title: string;
|
|
338
|
+
description: string;
|
|
339
|
+
triggerCondition: string;
|
|
340
|
+
recommendedAction: string;
|
|
341
|
+
remedy?: string;
|
|
342
|
+
consequenceIfUnresolved?: string;
|
|
343
|
+
missingFacts?: string[];
|
|
344
|
+
missingFactsExplanation?: Record<string, { type: string; description: string; example: any; source: string }>;
|
|
345
|
+
applicableProvisions?: string[];
|
|
346
|
+
governingAuthority: string;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export interface StatutoryOutcomesOptions {
|
|
350
|
+
offline?: boolean;
|
|
351
|
+
verbose?: boolean;
|
|
352
|
+
baseUrl?: string;
|
|
353
|
+
timeoutMs?: number;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export interface StatutoryOutcomesResult {
|
|
357
|
+
status: 'SUCCESS' | 'ERROR';
|
|
358
|
+
source: 'LIVE_EDGE' | 'LOCAL_OKF_SUBSTRATE';
|
|
359
|
+
provision: string | null;
|
|
360
|
+
totalOutcomes: number;
|
|
361
|
+
outcomes: StatutoryOutcome[];
|
|
362
|
+
version: string;
|
|
363
|
+
offlineFallback?: boolean;
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
export declare const STATUTORY_OUTCOMES_DICTIONARY: StatutoryOutcome[];
|
|
367
|
+
export declare function getStatutoryOutcomes(
|
|
368
|
+
provision?: string,
|
|
369
|
+
options?: StatutoryOutcomesOptions
|
|
370
|
+
): Promise<StatutoryOutcomesResult>;
|
|
371
|
+
|
|
372
|
+
export declare function explainStatutoryDisposition(
|
|
373
|
+
codeOrQuery: string,
|
|
374
|
+
options?: any
|
|
375
|
+
): any;
|
|
376
|
+
|
|
377
|
+
export declare function enrichWithOutcomeExplanation<T extends Record<string, any>>(
|
|
378
|
+
result: T,
|
|
379
|
+
options?: { verbose?: boolean; explain?: boolean }
|
|
380
|
+
): T;
|
|
381
|
+
|
|
382
|
+
export declare function ignorePackageUpdate(
|
|
383
|
+
versionToIgnore: string
|
|
384
|
+
): { status: 'SUCCESS' | 'ERROR'; ignoredVersion: string; error?: string };
|
|
385
|
+
|
|
386
|
+
|