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):

  1. aggregate += base.
  2. If the aggregate now exceeds the threshold (or already did): taxable = pending + base, then pending = 0. The pending part is the catch-up and appears in catchup_base and in explain.
  3. Otherwise, for 194C only, if this single payment exceeds 30,000, taxable = base. Pending is left alone.
  4. Otherwise pending += base and 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:

  1. r = slab rate for the deductee type on d (individual and HUF use the first column, everyone else the second).
  2. Candidates: r; 206AA rate if PAN is missing/invalid/inoperative; 206AB rate if flagged and enabled for the FY. The highest wins.
  3. A matching section 197 certificate takes the first limit - used of the base at the certificate rate (PAN permitting); the remainder uses step 2.
  4. 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):

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.