File size: 10,524 Bytes
6733714
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
"""
LETA Calculation Engine β€” Stage 5A of TITAN architecture.

Deterministic GST computation for Rule 42/43, Section 50 interest,
and basic GST liability. The LLM explains; this engine computes.

Keeps all arithmetic out of the LLM to eliminate hallucinated figures
and remove thousands of thinking-tokens from the generation budget.
"""
import re
from dataclasses import dataclass
from typing import Optional


@dataclass
class CalculationResult:
    applicable: bool
    formula: str
    computation: str
    result_text: str
    statutory_ref: str


# ─── Amount / rate extractors ─────────────────────────────────────────────────

def _extract_amount(text: str) -> Optional[float]:
    """Pull the first monetary value from query text."""
    text_l = text.lower()
    patterns = [
        r'(?:rs\.?|inr|β‚Ή)\s*([\d,]+(?:\.\d+)?)\s*(?:lakh|lakhs|crore|crores)?',
        r'([\d,]+(?:\.\d+)?)\s+(?:lakh|lakhs|crore|crores)',
        r'(?:itc|credit|amount|tax)\s+(?:of\s+)?(?:rs\.?|inr|β‚Ή)?\s*([\d,]+)',
    ]
    for pat in patterns:
        m = re.search(pat, text_l)
        if m:
            raw = m.group(1).replace(',', '')
            try:
                val = float(raw)
            except ValueError:
                continue
            if 'crore' in text_l:
                val *= 10_000_000
            elif 'lakh' in text_l:
                val *= 100_000
            return val
    return None


def _extract_percentage(text: str) -> Optional[float]:
    """Pull the first percentage value from query text."""
    m = re.search(r'([\d.]+)\s*%', text)
    if m:
        try:
            return float(m.group(1))
        except ValueError:
            return None
    return None


def _extract_days(text: str) -> Optional[int]:
    m = re.search(r'(\d+)\s+days?', text.lower())
    if m:
        return int(m.group(1))
    return None


# ─── Public API ───────────────────────────────────────────────────────────────

def detect_and_calculate(query: str) -> Optional[CalculationResult]:
    """
    Inspect the query for computable GST patterns and return a pre-computed
    result block. Returns None when no computation applies.
    """
    q = query.lower()

    if re.search(r'rule\s*42|itc.*revers|revers.*itc|common.*input.*service|exempt.*turnover.*itc', q):
        return _rule42(query)

    if re.search(r'rule\s*43|capital\s+goods.*itc|itc.*capital\s+goods|cg.*itc', q):
        return _rule43(query)

    if re.search(r'(?:sec(?:tion)?\.?\s*50|interest.*gst|gst.*interest|delayed\s+payment|interest.*delay)', q):
        return _section50(query)

    if re.search(r'(?:calculate|compute|how\s+much)\s+(?:gst|igst|cgst|sgst|tax)', q):
        return _gst_amount(query)

    return None


def format_for_context(result: CalculationResult) -> str:
    """Render a CalculationResult as a pre-computed facts block for the LLM context."""
    return (
        "\n╔══════════════════════════════════════════╗\n"
        "β•‘   PRE-COMPUTED STATUTORY CALCULATION     β•‘\n"
        "β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•\n"
        f"Statutory Reference: {result.statutory_ref}\n"
        f"Formula: {result.formula}\n"
        f"Computation:\n{result.computation}\n"
        f"Engine Result: {result.result_text}\n"
        "INSTRUCTION: Use these values verbatim. Do NOT recalculate independently.\n"
    )


# ─── Individual calculators ───────────────────────────────────────────────────

def _rule42(query: str) -> CalculationResult:
    itc = _extract_amount(query)
    exempt_pct = _extract_percentage(query)

    formula = (
        "Rule 42 Monthly Reversal = ITC on common inputs/services "
        "Γ— (Exempt + Non-business Turnover) Γ· Total Turnover\n"
        "Annual reconciliation required under Rule 42(2)."
    )

    if itc and exempt_pct:
        reversal = round(itc * exempt_pct / 100.0, 2)
        computation = (
            f"  Total ITC on common inputs: β‚Ή{itc:,.2f}\n"
            f"  Exempt turnover ratio used: {exempt_pct}%\n"
            f"  Monthly provisional reversal: β‚Ή{reversal:,.2f}\n"
            f"  Annual provisional reversal (Γ—12): β‚Ή{reversal * 12:,.2f}"
        )
        result_text = (
            f"Monthly reversal β‰ˆ β‚Ή{reversal:,.2f}. "
            "Subject to annual reconciliation β€” compare cumulative provisional "
            "reversals to final annual calculation at year-end."
        )
    else:
        computation = "  (Insufficient numerical data in query β€” formula guidance only)"
        result_text = (
            "Rule 42 applies to ITC on inputs/services used for taxable + exempt/non-business "
            "supplies. Monthly provisional reversal; annual reconciliation mandatory."
        )

    return CalculationResult(
        applicable=True,
        formula=formula,
        computation=computation,
        result_text=result_text,
        statutory_ref="Rule 42, CGST Rules 2017 | Section 17(3), CGST Act 2017",
    )


def _rule43(query: str) -> CalculationResult:
    itc = _extract_amount(query)
    exempt_pct = _extract_percentage(query)

    formula = (
        "Rule 43 Monthly Reversal = (ITC on Capital Goods Γ· 60) "
        "Γ— (Exempt Turnover Γ· Total Turnover)\n"
        "Useful life of capital goods = 60 months (5 years)."
    )

    if itc and exempt_pct:
        monthly_spread = round(itc / 60.0, 2)
        monthly_reversal = round(monthly_spread * exempt_pct / 100.0, 2)
        computation = (
            f"  Total ITC on capital goods: β‚Ή{itc:,.2f}\n"
            f"  Monthly spread (Γ·60 months): β‚Ή{monthly_spread:,.2f}\n"
            f"  Exempt turnover ratio: {exempt_pct}%\n"
            f"  Monthly reversal: β‚Ή{monthly_reversal:,.2f}\n"
            f"  Annual reversal (Γ—12): β‚Ή{monthly_reversal * 12:,.2f}"
        )
        result_text = (
            f"Monthly reversal β‰ˆ β‚Ή{monthly_reversal:,.2f}, "
            f"annual β‰ˆ β‚Ή{monthly_reversal * 12:,.2f}. "
            "Annual reconciliation under Rule 43(2) required."
        )
    else:
        computation = "  (Insufficient numerical data in query β€” formula guidance only)"
        result_text = (
            "Rule 43: ITC on capital goods used for taxable + exempt supplies "
            "must be reversed monthly over 60-month useful life."
        )

    return CalculationResult(
        applicable=True,
        formula=formula,
        computation=computation,
        result_text=result_text,
        statutory_ref="Rule 43, CGST Rules 2017 | Section 17(3), CGST Act 2017",
    )


def _section50(query: str) -> CalculationResult:
    amount = _extract_amount(query)
    days = _extract_days(query)

    formula = (
        "Section 50 Interest = Tax Amount Γ— (Rate Γ· 100) Γ· 365 Γ— Days\n"
        "Rate: 18% p.a. for delayed payment | 24% p.a. for wrongful ITC utilisation\n"
        "Ref: Notification 13/2017-CT (as amended)"
    )

    if amount and days:
        i18 = round(amount * 0.18 / 365 * days, 2)
        i24 = round(amount * 0.24 / 365 * days, 2)
        computation = (
            f"  Principal tax: β‚Ή{amount:,.2f}\n"
            f"  Delay: {days} days\n"
            f"  Interest @ 18% p.a.: β‚Ή{i18:,.2f}\n"
            f"  Interest @ 24% p.a. (wrongful ITC): β‚Ή{i24:,.2f}"
        )
        result_text = (
            f"Interest = β‚Ή{i18:,.2f} (@ 18%) or β‚Ή{i24:,.2f} (@ 24% if wrongful ITC). "
            "Confirm applicable rate from demand notice."
        )
    elif amount:
        i18_daily = round(amount * 0.18 / 365, 2)
        computation = (
            f"  Principal tax: β‚Ή{amount:,.2f}\n"
            f"  Daily interest @ 18%: β‚Ή{i18_daily:,.2f}\n"
            f"  Daily interest @ 24%: β‚Ή{round(amount * 0.24 / 365, 2):,.2f}\n"
            "  (Provide delay in days for total interest)"
        )
        result_text = (
            f"Daily interest: β‚Ή{i18_daily:,.2f} (@ 18%) on β‚Ή{amount:,.2f}. "
            "Multiply by number of delayed days for total."
        )
    else:
        computation = "  (Provide tax amount and delay period for exact computation)"
        result_text = "Sec 50: 18% p.a. for delayed payment; 24% p.a. for wrongful ITC utilisation."

    return CalculationResult(
        applicable=True,
        formula=formula,
        computation=computation,
        result_text=result_text,
        statutory_ref="Section 50, CGST Act 2017 | Notification 13/2017-CT (as amended)",
    )


def _gst_amount(query: str) -> CalculationResult:
    amount = _extract_amount(query)

    # Detect rate from query text
    rate: Optional[float] = _extract_percentage(query)
    if rate is None:
        for r in [28.0, 18.0, 12.0, 5.0, 3.0, 0.25]:
            if str(int(r)) in query or str(r) in query:
                rate = r
                break

    formula = (
        "GST Liability = Taxable Value Γ— GST Rate%\n"
        "For intra-state: CGST = Rate/2 | SGST = Rate/2\n"
        "For inter-state: IGST = Full Rate"
    )

    if amount and rate:
        gst = round(amount * rate / 100.0, 2)
        half = round(gst / 2.0, 2)
        total = round(amount + gst, 2)
        computation = (
            f"  Taxable value: β‚Ή{amount:,.2f}\n"
            f"  GST @ {rate}%: β‚Ή{gst:,.2f}\n"
            f"    Intra-state β€” CGST ({rate/2}%): β‚Ή{half:,.2f} | SGST ({rate/2}%): β‚Ή{half:,.2f}\n"
            f"    Inter-state β€” IGST ({rate}%): β‚Ή{gst:,.2f}\n"
            f"  Total invoice value: β‚Ή{total:,.2f}"
        )
        result_text = (
            f"GST = β‚Ή{gst:,.2f} on β‚Ή{amount:,.2f} @ {rate}%. "
            f"Total invoice value = β‚Ή{total:,.2f}."
        )
    else:
        computation = "  (Provide taxable amount and GST rate for exact computation)"
        result_text = "GST = Taxable Value Γ— Applicable Rate under CGST Act / IGST Act."

    return CalculationResult(
        applicable=True,
        formula=formula,
        computation=computation,
        result_text=result_text,
        statutory_ref="Section 9, CGST Act 2017 | Section 5, IGST Act 2017",
    )