Algorithms and worked examples
Every example below is also a test (tests/test_engine.py, tests/test_dates_interest.py).
1. Threshold tracking and catch-up
State per (deductee, section) for the FY: running aggregate of TDS bases, and pending, the sum of earlier
bases on which nothing has been deducted yet. Payments are processed in order of event date.
Sections with an aggregate threshold (194C, 194J, 194I, 194H, 194A):
aggregate += base.- If the aggregate now exceeds the threshold (or already did): taxable =
pending + base, thenpending = 0. The pending part is the catch-up and appears incatchup_baseand inexplain. - Otherwise, for 194C only, if this single payment exceeds 30,000, taxable =
base. Pending is left alone. - Otherwise
pending += baseand nothing is deducted.
"Exceeds" is strict: exactly 1,00,000 triggers nothing.
Example, individual, 194C at 1%:
| Payment | Base | Aggregate | Pending | Taxable | TDS |
|---|---|---|---|---|---|
| 1 Apr to 1 Jul, 4 x 25,000 | 25,000 each | 1,00,000 | 1,00,000 | 0 | 0 |
| 1 Aug | 10,000 | 1,10,000 | 0 | 1,10,000 | 1,100 |
| 1 Sep | 20,000 | 1,30,000 | 0 | 20,000 | 200 |
A single payment above 30,000 is taxed on its own and also counts in the aggregate, but it does not sweep the pending amounts: 40,000 (taxed, 400), 20,000, 30,000, 20,000 gives pending 70,000 and the fourth payment crosses the aggregate with taxable 70,000 and TDS 700, catch-up 50,000.
194Q works differently: taxable = max(0, aggregate_after - max(aggregate_before, 50,00,000)). Purchases of
40,00,000 then 30,00,000 give taxable 20,00,000 and TDS 2,000 at 0.1%; there is no catch-up on the first 50 lakh.
The deductible date of a catch-up is the crossing payment's event date, and the rate (including effective-dated slabs and the 206AA / 206AB / 197 checks) is evaluated on that date for the whole taxable base.
2. Rate selection
For a taxable base on event date d:
r= slab rate for the deductee type ond(individual and HUF use the first column, everyone else the second).- Candidates:
r; 206AA rate if PAN is missing/invalid/inoperative; 206AB rate if flagged and enabled for the FY. The highest wins. - A matching section 197 certificate takes the first
limit - usedof the base at the certificate rate (PAN permitting); the remainder uses step 2. TDS = round_half_up(sum(base_i x rate_i / 100)).
Example: certificate 2% with limit 5,00,000 valid until 30 Sep; professional fees 3,00,000 on 1 Jun gives 6,000. A second 3,00,000 on 1 Jul gives 2,00,000 x 2% + 1,00,000 x 10% = 4,000 + 10,000 = 14,000. On 1 Oct the certificate has expired: 1,00,000 x 10% = 10,000.
3. Interest under section 201(1A)
Month-or-part counting: months_or_part(start, end) is the number of whole months from start plus one if any
days remain, counting by the start date's anniversary (month-end clamped).
| start | end | months |
|---|---|---|
| 15 Jan | 15 Feb | 1 |
| 15 Jan | 16 Feb | 2 |
| 15 Jul | 10 Aug | 1 |
| 15 Jul | 20 Aug | 2 |
| 31 Jan | 28 Feb | 1 |
| 31 Jan | 1 Mar | 2 |
| 20 Mar | 2 May | 2 |
(i) Late deduction, 1% per month or part from the date the tax was deductible to the date it was deducted.
Deductible 10 Jun, deducted 25 Aug, TDS 10,000: 3 months, 3 x 1% x 10,000 = 300. A payment gets a different
deduction date through the optional deduction_date column; otherwise it is deducted on the event date.
(ii) Late deposit, 1.5% per month or part from the date of deduction to the date of deposit, only when the deposit is after the due date (a deposit by the due date carries no interest even if made weeks after deduction). TDS 10,000 deducted 15 Jul, due 7 Aug:
| Deposited | Months from 15 Jul | Interest |
|---|---|---|
| 7 Aug | none, on time | 0 |
| 10 Aug | 1 | 150 |
| 20 Aug | 2 | 300 |
March deduction on 20 Mar is due 30 Apr: deposited 30 Apr costs nothing, deposited 2 May costs 2 x 1.5% x 10,000 = 300.
Tax that is not covered by any challan accrues (ii) up to --as-on once the due date has passed: 10,000 deducted
15 Jul and unpaid on 20 Sep is 3 months, 450. A challan that covers only part of a deduction is split: each
portion is charged on its own deposit date.
4. Challan allocation
Per section group (194C, 194J, 194I, 194H, 194A, 194Q; challan sections may be written 194J, 94J, 94JB or
194J(b)), deductions are ordered by deduction date and each takes from the oldest challan that still has a
balance and is dated on or after the deduction (a challan cannot pay tax not yet deducted).
Left-over deduction is reported as SHORTFALL, left-over challan balance as EXCESS.
Example: company 194C deductions of 2,000 (10 Jul) and 4,000 (20 Jul); challans of 3,000 and 1,000. The first deduction takes 2,000 of challan 1, the second takes 1,000 of challan 1 and 1,000 of challan 2 and is 2,000 short.
FIFO means a shortfall surfaces on the newest deductions, not necessarily the month in which the underpayment
happened. Use the monthly figures from return and the challan CSV to trace it.
5. Late filing fee, section 234E
fee = min(200 x days_late, total TDS of the statement) where days late is the filing date minus the quarter's
due date. Q2 due 31 Oct: filed 10 Nov is 10 days, 2,000, or 1,500 when the quarter's TDS is only 1,500.
6. 26Q intermediate format
return 26Q writes four CSVs per quarter (a deduction belongs to the quarter of its deduction date):
- deductee annexure: columns follow the NSDL RPU deductee-record layout (deductee code 01 company / 02 other, PAN
or
PANNOTAVBL, section code, dates, amount, tax, deposit, BSR and challan serial, rate, reason code, certificate number). Amount paid is the taxable base, so a catch-up row reports the full aggregate. When a deduction was funded by several challans the BSR codes and serials are joined with|. - summary per section, challan details with the amount allocated to the quarter, and 16A certificate data per deductee, section and quarter.
These files are an intermediate format. They are not the final FVU file produced by the NSDL File Validation Utility, and the reason codes (A for certificate, C for higher rate) are illustrative.
7. Reconciliation
Books are grouped by (PAN, NSDL section code) over deductions with TDS above zero and compared with the 26AS-style
CSV on amount, tax deducted and tax deposited with a Rs. 1 tolerance. Results: MATCH, MISMATCH (with the
fields), MISSING_IN_26AS, MISSING_IN_BOOKS. Deductees without a PAN show up as PANNOTAVBL and can never match,
which is expected.