@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/constants.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @fileoverview Canonical Single-Source-of-Truth Constants for @mudraforge/statutory.
|
|
3
|
+
* Pure ESM module with zero Node.js/browser-specific dependencies.
|
|
4
|
+
* Safe to import across Node.js, Cloudflare Workers, browsers, and Edge runtimes.
|
|
5
|
+
*
|
|
6
|
+
* @license MIT
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export const STATUTORY_CLIENT_VERSION = '2.10.0';
|
|
10
|
+
export const DEFAULT_BASE_URL = 'https://api.mudraforge.in';
|
package/src/index.js
CHANGED
|
@@ -12,19 +12,46 @@ import fs from 'node:fs';
|
|
|
12
12
|
import path from 'node:path';
|
|
13
13
|
import os from 'node:os';
|
|
14
14
|
import crypto from 'node:crypto';
|
|
15
|
+
import dns from 'node:dns';
|
|
15
16
|
import { fileURLToPath } from 'node:url';
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
import {
|
|
18
|
+
roundToPaisa,
|
|
19
|
+
roundSection288B,
|
|
20
|
+
calculateCorporateTaxMatVsConcessional,
|
|
21
|
+
evaluatePlaceOfSupplyAndLut,
|
|
22
|
+
calculateTransferPricingAlp,
|
|
23
|
+
evaluateSection79LossContinuity,
|
|
24
|
+
revalueForeignExchangeIndAs109
|
|
25
|
+
} from './calculators.js';
|
|
26
|
+
import {
|
|
27
|
+
getStatutoryOutcomes,
|
|
28
|
+
explainStatutoryDisposition,
|
|
29
|
+
STATUTORY_OUTCOMES_DICTIONARY,
|
|
30
|
+
enrichWithOutcomeExplanation
|
|
31
|
+
} from './outcomes.js';
|
|
32
|
+
|
|
33
|
+
import { STATUTORY_CLIENT_VERSION, DEFAULT_BASE_URL } from './constants.js';
|
|
34
|
+
|
|
35
|
+
let defaultOkfDir = null;
|
|
36
|
+
try {
|
|
37
|
+
if (typeof import.meta?.url === 'string' && import.meta.url.startsWith('file:')) {
|
|
38
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
39
|
+
const __dirname = path.dirname(__filename);
|
|
40
|
+
defaultOkfDir = path.resolve(__dirname, '..', 'okf');
|
|
41
|
+
}
|
|
42
|
+
} catch (_) {
|
|
43
|
+
defaultOkfDir = null;
|
|
44
|
+
}
|
|
45
|
+
export const DEFAULT_LOCAL_OKF_DIR = defaultOkfDir;
|
|
46
|
+
export { STATUTORY_CLIENT_VERSION, DEFAULT_BASE_URL };
|
|
22
47
|
|
|
23
48
|
/**
|
|
24
49
|
* Pinned official release Merkle root digests.
|
|
25
50
|
* Prevents unauthorized bundle substitution attacks even if an attacker controls the remote endpoint.
|
|
26
51
|
*/
|
|
27
52
|
export const PINNED_RELEASE_ROOTS = new Set([
|
|
53
|
+
'sha256-4bbe36c3db841b833dd2bebbd6c1c5ed735498e7b20d1221c323ceb211529fe0', // v2.9.0 Corporate Finance 5 Domains Merkle Root (29 Sections across 5 Domains)
|
|
54
|
+
'sha256-3fb34bdcd34eec794b1f8e6abb2c0e0e87664746ec2f50ad2599c34bf57565b3', // v2.8.1 Finance Act, 2025 Section 206AB Sunset Root (24 Sections across 5 Domains)
|
|
28
55
|
'sha256-239641de2bef05917673f693f9ba4d14bd5e137d64554fba5db87e426bc13e14', // v2.8.0 Deep Semantic Citation Merkle Root (24 Sections across 5 Domains)
|
|
29
56
|
'sha256-6953ce79b63bb2a5809dc928178b4631f08448aa2dcde107dde10f84890bf5cb', // v2.7.0 Edge Sync Bundle Root (24 Sections across 5 Domains)
|
|
30
57
|
'sha256-ce88c2255df1c30c15bfe184c8117336ebc302c2ac5ef25e96a6957402ae11ae', // v2.6.0 Verified Citation Merkle Root (24 Sections across 5 Domains - 100% Complete AP)
|
|
@@ -874,7 +901,11 @@ export async function checkUpdates(options = {}) {
|
|
|
874
901
|
* @returns {Promise<{ updateAvailable: boolean, currentVersion: string, latestVersion: string } | null>}
|
|
875
902
|
*/
|
|
876
903
|
export async function checkForPackageUpdate(options = {}) {
|
|
877
|
-
if (!options.force && (
|
|
904
|
+
if (!options.force && (
|
|
905
|
+
process.env.NO_UPDATE_NOTIFIER === '1' ||
|
|
906
|
+
process.env.MUDRA_NO_UPDATE_NOTIFIER === '1' ||
|
|
907
|
+
(process.env.CI === 'true' && !options.registryUrl)
|
|
908
|
+
)) {
|
|
878
909
|
return null;
|
|
879
910
|
}
|
|
880
911
|
|
|
@@ -910,7 +941,9 @@ export async function checkForPackageUpdate(options = {}) {
|
|
|
910
941
|
latestVersion = json.version;
|
|
911
942
|
if (cacheTtlMs > 0) {
|
|
912
943
|
try {
|
|
944
|
+
const prev = fs.existsSync(cacheFile) ? JSON.parse(fs.readFileSync(cacheFile, 'utf8')) : {};
|
|
913
945
|
fs.writeFileSync(cacheFile, JSON.stringify({
|
|
946
|
+
...prev,
|
|
914
947
|
lastChecked: Date.now(),
|
|
915
948
|
latestVersion
|
|
916
949
|
}), 'utf8');
|
|
@@ -927,13 +960,44 @@ export async function checkForPackageUpdate(options = {}) {
|
|
|
927
960
|
if (!latestVersion) return null;
|
|
928
961
|
|
|
929
962
|
const cmp = compareSemver(latestVersion, currentVersion);
|
|
963
|
+
const isIgnored = Boolean(cached?.ignoredVersion && cached.ignoredVersion === latestVersion);
|
|
964
|
+
|
|
930
965
|
return {
|
|
931
|
-
updateAvailable: cmp > 0,
|
|
966
|
+
updateAvailable: cmp > 0 && (!isIgnored || Boolean(options.force)),
|
|
967
|
+
isIgnored,
|
|
932
968
|
currentVersion,
|
|
933
969
|
latestVersion
|
|
934
970
|
};
|
|
935
971
|
}
|
|
936
972
|
|
|
973
|
+
/**
|
|
974
|
+
* Configure the CLI to ignore update notifications for a specific version.
|
|
975
|
+
* @param {string} versionToIgnore Package version to suppress notifications for
|
|
976
|
+
* @returns {{ status: 'SUCCESS' | 'ERROR', ignoredVersion: string, error?: string }}
|
|
977
|
+
*/
|
|
978
|
+
export function ignorePackageUpdate(versionToIgnore) {
|
|
979
|
+
if (!versionToIgnore || typeof versionToIgnore !== 'string') {
|
|
980
|
+
throw new Error('[@mudraforge/statutory] A valid version string is required to ignore update notifications.');
|
|
981
|
+
}
|
|
982
|
+
const cacheFile = path.join(os.tmpdir(), '.mudraforge_statutory_npm_update.json');
|
|
983
|
+
let current = {};
|
|
984
|
+
try {
|
|
985
|
+
if (fs.existsSync(cacheFile)) {
|
|
986
|
+
current = JSON.parse(fs.readFileSync(cacheFile, 'utf8')) || {};
|
|
987
|
+
}
|
|
988
|
+
} catch (_) {}
|
|
989
|
+
|
|
990
|
+
current.ignoredVersion = versionToIgnore.trim();
|
|
991
|
+
current.ignoredAt = Date.now();
|
|
992
|
+
|
|
993
|
+
try {
|
|
994
|
+
fs.writeFileSync(cacheFile, JSON.stringify(current, null, 2), 'utf8');
|
|
995
|
+
return { status: 'SUCCESS', ignoredVersion: versionToIgnore.trim() };
|
|
996
|
+
} catch (err) {
|
|
997
|
+
return { status: 'ERROR', ignoredVersion: versionToIgnore.trim(), error: err.message };
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
|
|
937
1001
|
/**
|
|
938
1002
|
* Download and synchronize the latest OKF knowledge bundle to disk.
|
|
939
1003
|
* Hardened with:
|
|
@@ -1514,17 +1578,24 @@ export class StatutoryClient {
|
|
|
1514
1578
|
/**
|
|
1515
1579
|
* @param {Object} [options]
|
|
1516
1580
|
* @param {string} [options.apiKey] Enterprise API key (obtain from hello@mudraforge.in)
|
|
1517
|
-
* @param {'PUBLIC_COMMONS_FREE' | 'ENTERPRISE_PAID'} [options.tier] Client
|
|
1581
|
+
* @param {'PUBLIC_COMMONS_FREE' | 'ENTERPRISE_PAID'} [options.tier] Client-side telemetry tier hint. Note: Edge server authorizes and throttles strictly based on the Bearer token, ignoring client tier claims.
|
|
1518
1582
|
* @param {string} [options.baseUrl] Custom edge base URL
|
|
1519
1583
|
* @param {string} [options.okfDir] Path to local OKF directory
|
|
1520
1584
|
* @param {boolean} [options.offlineFirst=true] Query local filesystem before hitting edge
|
|
1585
|
+
* @param {boolean} [options.preferIpv4=false] Opt-in preference for IPv4 DNS resolution (calls node:dns.setDefaultResultOrder('ipv4first')). Note: In Node.js this operates at process scope to resolve Windows IPv6 Anycast timeouts.
|
|
1521
1586
|
*/
|
|
1522
1587
|
constructor(options = {}) {
|
|
1523
|
-
this.apiKey = options.apiKey || process.env?.MUDRA_STATUTORY_API_KEY || null;
|
|
1588
|
+
this.apiKey = options.apiKey || (typeof process !== 'undefined' ? process.env?.MUDRA_STATUTORY_API_KEY : null) || null;
|
|
1524
1589
|
this.tier = options.tier || (this.apiKey ? 'ENTERPRISE_PAID' : 'PUBLIC_COMMONS_FREE');
|
|
1525
|
-
this.baseUrl = options.baseUrl || process.env?.MUDRA_STATUTORY_BASE_URL || DEFAULT_BASE_URL;
|
|
1590
|
+
this.baseUrl = options.baseUrl || (typeof process !== 'undefined' ? process.env?.MUDRA_STATUTORY_BASE_URL : null) || DEFAULT_BASE_URL;
|
|
1526
1591
|
this.okfDir = options.okfDir || DEFAULT_LOCAL_OKF_DIR;
|
|
1527
1592
|
this.offlineFirst = options.offlineFirst !== false;
|
|
1593
|
+
this.preferIpv4 = options.preferIpv4 === true || (typeof process !== 'undefined' && process.env?.MUDRA_STATUTORY_PREFER_IPV4 === '1');
|
|
1594
|
+
if (this.preferIpv4 && typeof dns?.setDefaultResultOrder === 'function') {
|
|
1595
|
+
try {
|
|
1596
|
+
dns.setDefaultResultOrder('ipv4first');
|
|
1597
|
+
} catch (_) {}
|
|
1598
|
+
}
|
|
1528
1599
|
}
|
|
1529
1600
|
|
|
1530
1601
|
/**
|
|
@@ -1555,9 +1626,10 @@ export class StatutoryClient {
|
|
|
1555
1626
|
async calculate(payload = {}, options = {}) {
|
|
1556
1627
|
if (!this.apiKey) {
|
|
1557
1628
|
const err = new Error(
|
|
1558
|
-
'[@mudraforge/statutory]
|
|
1559
|
-
'
|
|
1560
|
-
'
|
|
1629
|
+
'[@mudraforge/statutory] The hosted cloud calculation endpoint (\'/api/v1/statutory/calculate\') requires an Enterprise API key.\n' +
|
|
1630
|
+
'To calculate deterministically offline without an API key or remote server, import pure calculation functions directly:\n' +
|
|
1631
|
+
' import { calculateCorporateTaxMatVsConcessional, evaluatePlaceOfSupplyAndLut, calculateTransferPricingAlp, evaluateSection79LossContinuity, revalueForeignExchangeIndAs109 } from \'@mudraforge/statutory\';\n' +
|
|
1632
|
+
'For hosted cloud edge calculation access or custom enterprise models, contact hello@mudraforge.in or support@mudraforge.in.'
|
|
1561
1633
|
);
|
|
1562
1634
|
err.code = 'API_KEY_REQUIRED';
|
|
1563
1635
|
err.contact = 'hello@mudraforge.in';
|
|
@@ -1594,18 +1666,38 @@ export class StatutoryClient {
|
|
|
1594
1666
|
assertSecureEndpoint(this.baseUrl);
|
|
1595
1667
|
|
|
1596
1668
|
const url = new URL('/api/v1/statutory/calculate', this.baseUrl);
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1669
|
+
let res;
|
|
1670
|
+
try {
|
|
1671
|
+
res = await fetch(url.toString(), {
|
|
1672
|
+
method: 'POST',
|
|
1673
|
+
headers: {
|
|
1674
|
+
'Content-Type': 'application/json',
|
|
1675
|
+
'Accept': 'application/json',
|
|
1676
|
+
'Authorization': `Bearer ${this.apiKey}`,
|
|
1677
|
+
'X-Mudra-Tier': this.tier,
|
|
1678
|
+
'User-Agent': `@mudraforge/statutory/${STATUTORY_CLIENT_VERSION} (${this.tier})`
|
|
1679
|
+
},
|
|
1680
|
+
body: JSON.stringify(payload),
|
|
1681
|
+
signal: AbortSignal.timeout(options.timeoutMs || 20000)
|
|
1682
|
+
});
|
|
1683
|
+
} catch (fetchErr) {
|
|
1684
|
+
const isTimeout = fetchErr.name === 'TimeoutError' ||
|
|
1685
|
+
fetchErr.code === 'UND_ERR_CONNECT_TIMEOUT' ||
|
|
1686
|
+
fetchErr.code === 'ETIMEDOUT' ||
|
|
1687
|
+
fetchErr.message?.includes('timeout') ||
|
|
1688
|
+
fetchErr.cause?.code === 'UND_ERR_CONNECT_TIMEOUT';
|
|
1689
|
+
if (isTimeout) {
|
|
1690
|
+
const timeoutErr = new Error(
|
|
1691
|
+
`[@mudraforge/statutory] Connection timeout reaching ${this.baseUrl}.\n` +
|
|
1692
|
+
`Diagnostic advice: On Windows environments, broken or unrouted IPv6 Anycast routing can cause connection timeouts to Cloudflare edge IPs.\n` +
|
|
1693
|
+
`To resolve, enable IPv4-first resolution on the client by passing { preferIpv4: true } to StatutoryClient or setting environment variable MUDRA_STATUTORY_PREFER_IPV4=1.`
|
|
1694
|
+
);
|
|
1695
|
+
timeoutErr.code = 'CONNECTION_TIMEOUT';
|
|
1696
|
+
timeoutErr.cause = fetchErr;
|
|
1697
|
+
throw timeoutErr;
|
|
1698
|
+
}
|
|
1699
|
+
throw fetchErr;
|
|
1700
|
+
}
|
|
1609
1701
|
|
|
1610
1702
|
if (!res.ok) {
|
|
1611
1703
|
const isJson = (res.headers.get('content-type') || '').toLowerCase().includes('application/json');
|
|
@@ -1617,7 +1709,8 @@ export class StatutoryClient {
|
|
|
1617
1709
|
throw err;
|
|
1618
1710
|
}
|
|
1619
1711
|
|
|
1620
|
-
|
|
1712
|
+
const parsedResult = await safeJsonParse(res, 'StatutoryClient.calculate');
|
|
1713
|
+
return enrichWithOutcomeExplanation(parsedResult, { ...options, verbose: options.verbose || payload.verbose });
|
|
1621
1714
|
}
|
|
1622
1715
|
|
|
1623
1716
|
/**
|
|
@@ -1676,6 +1769,62 @@ export class StatutoryClient {
|
|
|
1676
1769
|
async doctor(options = {}) {
|
|
1677
1770
|
return await runStatutoryDoctor(options);
|
|
1678
1771
|
}
|
|
1772
|
+
|
|
1773
|
+
/**
|
|
1774
|
+
* Evaluate Corporate Tax: Normal MAT (Section 115JB) vs Concessional Regime (Section 115BAA).
|
|
1775
|
+
*/
|
|
1776
|
+
calculateCorporateTaxMatVsConcessional(payload) {
|
|
1777
|
+
return calculateCorporateTaxMatVsConcessional(payload);
|
|
1778
|
+
}
|
|
1779
|
+
|
|
1780
|
+
/**
|
|
1781
|
+
* Evaluate Place of Supply (POS) & Zero-Rated Export of Services under LUT (IGST Sections 10, 12, 13, 16).
|
|
1782
|
+
*/
|
|
1783
|
+
evaluatePlaceOfSupplyAndLut(payload) {
|
|
1784
|
+
return evaluatePlaceOfSupplyAndLut(payload);
|
|
1785
|
+
}
|
|
1786
|
+
|
|
1787
|
+
/**
|
|
1788
|
+
* Compute Transfer Pricing Arm's Length Price (ALP) under Section 92C & Rule 10CA.
|
|
1789
|
+
*/
|
|
1790
|
+
calculateTransferPricingAlp(payload) {
|
|
1791
|
+
return calculateTransferPricingAlp(payload);
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
/**
|
|
1795
|
+
* Evaluate Section 79 Loss Carry-Forward & Shareholding Continuity with unabsorbed depreciation protection.
|
|
1796
|
+
*/
|
|
1797
|
+
evaluateSection79LossContinuity(payload) {
|
|
1798
|
+
return evaluateSection79LossContinuity(payload);
|
|
1799
|
+
}
|
|
1800
|
+
|
|
1801
|
+
/**
|
|
1802
|
+
* Revalue foreign exchange monetary items at closing spot rates under Ind AS 21 and Ind AS 109.
|
|
1803
|
+
*/
|
|
1804
|
+
revalueForeignExchangeIndAs109(payload) {
|
|
1805
|
+
return revalueForeignExchangeIndAs109(payload);
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
/**
|
|
1809
|
+
* Retrieve statutory outcomes and disposition codes.
|
|
1810
|
+
* @param {string} [provision] Optional section code or provision alias
|
|
1811
|
+
* @param {Object} [options]
|
|
1812
|
+
*/
|
|
1813
|
+
async getOutcomes(provision = null, options = {}) {
|
|
1814
|
+
return await getStatutoryOutcomes(provision, {
|
|
1815
|
+
...options,
|
|
1816
|
+
baseUrl: this.baseUrl
|
|
1817
|
+
});
|
|
1818
|
+
}
|
|
1819
|
+
|
|
1820
|
+
/**
|
|
1821
|
+
* Retrieve in-depth explanation and remediation steps for a disposition code.
|
|
1822
|
+
* @param {string} codeOrQuery Disposition code (e.g. 'BUYER_TURNOVER_UNVERIFIED', 'INDETERMINATE')
|
|
1823
|
+
* @param {Object} [options]
|
|
1824
|
+
*/
|
|
1825
|
+
explain(codeOrQuery, options = {}) {
|
|
1826
|
+
return explainStatutoryDisposition(codeOrQuery, options);
|
|
1827
|
+
}
|
|
1679
1828
|
}
|
|
1680
1829
|
|
|
1681
1830
|
/**
|
|
@@ -2122,8 +2271,9 @@ export const STATUTORY_CROSSWALK_CATALOG = [
|
|
|
2122
2271
|
aliases: ['195', 'sec195', 'section 195', '393(2)', 'foreign remittance tds', 'non resident tds']
|
|
2123
2272
|
},
|
|
2124
2273
|
{
|
|
2125
|
-
id: '
|
|
2274
|
+
id: 'CROSSWALK_206AB_SUNSET',
|
|
2126
2275
|
domain: 'direct_tax',
|
|
2276
|
+
isOmittedSunset: true,
|
|
2127
2277
|
predecessor: {
|
|
2128
2278
|
act: 'Income-tax Act, 1961 (Act No. 43 of 1961)',
|
|
2129
2279
|
sectionCode: '206AB',
|
|
@@ -2136,24 +2286,24 @@ export const STATUTORY_CROSSWALK_CATALOG = [
|
|
|
2136
2286
|
returnForm: 'Form 26Q'
|
|
2137
2287
|
},
|
|
2138
2288
|
successor: {
|
|
2139
|
-
act: '
|
|
2140
|
-
sectionCode: '
|
|
2141
|
-
statutorySection: 'Section
|
|
2142
|
-
title: '
|
|
2143
|
-
effectiveDate: '
|
|
2144
|
-
threshold: '
|
|
2145
|
-
standardRate: '
|
|
2146
|
-
penaltyRate: '
|
|
2147
|
-
challan: '
|
|
2148
|
-
returnForm: '
|
|
2289
|
+
act: 'Finance Act, 2025 (Statutory Sunset & Omission)',
|
|
2290
|
+
sectionCode: '206AB [OMITTED]',
|
|
2291
|
+
statutorySection: 'Section 206AB Omitted',
|
|
2292
|
+
title: 'Formally omitted by Finance Act, 2025 effective 1 April 2025 (No higher rate for non-filers)',
|
|
2293
|
+
effectiveDate: '2025-04-01',
|
|
2294
|
+
threshold: 'N/A (Provision repealed; compliance check utility discontinued)',
|
|
2295
|
+
standardRate: '0.0%',
|
|
2296
|
+
penaltyRate: '0.0%',
|
|
2297
|
+
challan: 'N/A',
|
|
2298
|
+
returnForm: 'N/A'
|
|
2149
2299
|
},
|
|
2150
|
-
transitionCutoffDate: '
|
|
2300
|
+
transitionCutoffDate: '2025-04-01',
|
|
2151
2301
|
savingsAuthority: {
|
|
2152
|
-
statute: '
|
|
2153
|
-
subsidiary: 'General Clauses Act, 1897 Section 6
|
|
2154
|
-
legalPrinciple: '
|
|
2302
|
+
statute: 'Finance Act, 2025 Section 206AB Sunset Clause',
|
|
2303
|
+
subsidiary: 'General Clauses Act, 1897 Section 6',
|
|
2304
|
+
legalPrinciple: 'Deductions on or before 31 March 2025 remain assessable under Section 206AB. Deductions on or after 1 April 2025 attract ZERO non-filer penalty rate; standard statutory rates apply. Higher rates under Section 206AA for invalid/inoperative PAN remain independent and fully active at 20%.'
|
|
2155
2305
|
},
|
|
2156
|
-
aliases: ['206ab', 'sec206ab', 'section 206ab', '
|
|
2306
|
+
aliases: ['206ab', 'sec206ab', 'section 206ab', 'non filer higher tds', '206ab sunset', 'non filer sunset']
|
|
2157
2307
|
},
|
|
2158
2308
|
{
|
|
2159
2309
|
id: 'CROSSWALK_206C_1H_SUNSET',
|
|
@@ -2367,17 +2517,25 @@ export function crosswalkStatute(sectionCode, options = {}) {
|
|
|
2367
2517
|
let isOmitted = Boolean(entry.isOmittedSunset);
|
|
2368
2518
|
|
|
2369
2519
|
if (entry.isOmittedSunset) {
|
|
2370
|
-
// e.g. 206C(1H)
|
|
2520
|
+
// e.g. 206C(1H), 206AB
|
|
2371
2521
|
if (triggerDate < cutoff) {
|
|
2372
2522
|
governingAct = entry.predecessor.act;
|
|
2373
2523
|
applicableSection = entry.predecessor.statutorySection;
|
|
2374
2524
|
temporalStatus = 'GOVERNED_BY_1961_ACT';
|
|
2375
|
-
|
|
2525
|
+
if (entry.predecessor.sectionCode === '206AB') {
|
|
2526
|
+
legalAnalysis = `Trigger event crystallized on ${triggerDate} (prior to 1 April 2025 sunset). Governed by Section 206AB of the Income-tax Act, 1961. Higher withholding rate (twice applicable rate or 5%) applied to non-filer specified persons.`;
|
|
2527
|
+
} else {
|
|
2528
|
+
legalAnalysis = `Trigger event crystallized on ${triggerDate} (prior to 1 April 2025 sunset). Governed by ${entry.predecessor.statutorySection} of the ${entry.predecessor.act}. Seller was obligated to collect 0.1% TCS on receipts exceeding ₹50 Lakhs unless buyer was liable and deducted TDS u/s 194Q.`;
|
|
2529
|
+
}
|
|
2376
2530
|
} else {
|
|
2377
2531
|
governingAct = entry.successor.act;
|
|
2378
2532
|
applicableSection = entry.successor.statutorySection;
|
|
2379
2533
|
temporalStatus = 'SUNSET_OMITTED';
|
|
2380
|
-
|
|
2534
|
+
if (entry.predecessor.sectionCode === '206AB') {
|
|
2535
|
+
legalAnalysis = `Section 206AB was formally omitted by the Finance Act, 2025 w.e.f. 1 April 2025. No non-filer punitive TDS rate applies on or after 1 April 2025; standard statutory TDS rates apply. Higher rates under Section 206AA for missing/inoperative PAN remain independent and fully active at 20%.`;
|
|
2536
|
+
} else {
|
|
2537
|
+
legalAnalysis = `Section 206C(1H) was formally omitted by the Finance Act, 2025 w.e.f. 1 April 2025 to eliminate double-compliance overlap. No seller TCS liability attaches to transactions on or after 1 April 2025. Tax withholding on sale of goods is governed exclusively by buyer TDS under Section 194Q / Section 393.`;
|
|
2538
|
+
}
|
|
2381
2539
|
}
|
|
2382
2540
|
} else {
|
|
2383
2541
|
// Standard transition to 2025 Act
|
|
@@ -2489,6 +2647,21 @@ export const STATUTORY_AMENDMENTS_CHANGELOG = [
|
|
|
2489
2647
|
statutoryImpact: 'Payers must verify non-filer status using the Compliance Check functionality on the Income Tax Portal.',
|
|
2490
2648
|
aliases: ['206ab', 'non filer']
|
|
2491
2649
|
},
|
|
2650
|
+
{
|
|
2651
|
+
id: 'DIFF-2025-04-01-206AB',
|
|
2652
|
+
sectionCode: '206AB',
|
|
2653
|
+
domain: 'direct_tax',
|
|
2654
|
+
title: 'Statutory Sunset & Omission of Section 206AB Higher Non-Filer TDS',
|
|
2655
|
+
effectiveDate: '2025-04-01',
|
|
2656
|
+
amendmentType: 'STATUTORY_OMISSION',
|
|
2657
|
+
statute: 'Finance Act, 2025',
|
|
2658
|
+
legalAuthority: 'Finance Act, 2025 (Section 206AB Sunset Clause)',
|
|
2659
|
+
previousValue: 'Higher TDS rate (higher of 2x specified rate, 2x rates in force, or 5%) for non-filers',
|
|
2660
|
+
amendedValue: 'OMITTED (Section 206AB repealed; standard TDS rates apply without non-filer penalty)',
|
|
2661
|
+
changeSummary: 'Finance Act, 2025 formally omitted Section 206AB and Section 206CCA effective 1 April 2025 to relieve deductors from non-filer compliance checks.',
|
|
2662
|
+
statutoryImpact: 'Tax deductors are no longer required to run portal compliance checks for specified persons; standard statutory rates apply. Higher rates under Section 206AA for invalid/inoperative PAN remain active.',
|
|
2663
|
+
aliases: ['206ab', 'non filer sunset', '206ab omitted']
|
|
2664
|
+
},
|
|
2492
2665
|
{
|
|
2493
2666
|
id: 'DIFF-2022-07-01-194R',
|
|
2494
2667
|
sectionCode: '194R',
|
|
@@ -2646,4 +2819,20 @@ export function getStatutoryDiff(options = {}) {
|
|
|
2646
2819
|
};
|
|
2647
2820
|
}
|
|
2648
2821
|
|
|
2822
|
+
// ── Deterministic Zero-Dependency Statutory Calculators Export ──────────────
|
|
2823
|
+
export {
|
|
2824
|
+
roundToPaisa,
|
|
2825
|
+
roundSection288B,
|
|
2826
|
+
calculateCorporateTaxMatVsConcessional,
|
|
2827
|
+
evaluatePlaceOfSupplyAndLut,
|
|
2828
|
+
calculateTransferPricingAlp,
|
|
2829
|
+
evaluateSection79LossContinuity,
|
|
2830
|
+
revalueForeignExchangeIndAs109,
|
|
2831
|
+
getStatutoryOutcomes,
|
|
2832
|
+
explainStatutoryDisposition,
|
|
2833
|
+
STATUTORY_OUTCOMES_DICTIONARY,
|
|
2834
|
+
enrichWithOutcomeExplanation
|
|
2835
|
+
};
|
|
2836
|
+
|
|
2837
|
+
|
|
2649
2838
|
|