tr-kdv
v0.1.0
Published
Turkish VAT (KDV) and partial withholding (tevkifat) arithmetic. Zero dependencies, no network calls, no API key.
Maintainers
Readme
tr-kdv
Turkish VAT (KDV) and partial withholding (tevkifat) arithmetic. Zero dependencies. No network calls. No API key.
Install
npm install tr-kdvRequires Node.js >=20. This package is ESM-only — require() is not supported.
Usage
import { vatBreakdown, reviewFreshness } from 'tr-kdv';
vatBreakdown({ amount: 1000, rate: 20 });
// { ok: true, net: 1000, vatRate: 20, vatAmount: 200, gross: 1200, withholding: null }
vatBreakdown({ amount: 1200, amountIncludesVat: true, rate: 20 });
// { ok: true, net: 1000, vatRate: 20, vatAmount: 200, gross: 1200, withholding: null }
vatBreakdown({ amount: 10000, rate: 20, withholding: 'reklam' });
// { ok: true, net: 10000, vatRate: 20, vatAmount: 2000, gross: 12000,
// withholding: { ok: true, input: 'reklam', fraction: '3/10', numerator: 3, denominator: 10,
// withheldVat: 600, vatPayableBySeller: 1400, buyerPaysSeller: 11400,
// listedCode: 'reklam' } }
reviewFreshness();
// { reviewedAt: '2026-08-23', daysSinceReview: 0, stale: false, note: '...' }API
| Function | Signature | Description |
| --- | --- | --- |
| vatBreakdown | (input: VatBreakdownInput) => VatBreakdownResult | Splits an amount into net, VAT and gross for rate 0, 1, 10 or 20 percent; optionally applies a withholding fraction. |
| applyWithholding | (spec: WithholdingSpec, raw: string) => WithholdingResult | Applies a withholding fraction (e.g. '5/10') or a named code (e.g. 'reklam') to a {net, vatAmount} pair. |
| reviewFreshness | (now?: Date) => FreshnessResult | Reports how many days since the rate table was last checked against the official source, and whether that exceeds STALE_AFTER_DAYS. |
An invalid rate or an unrecognised withholding string returns { ok: false, reason } — this
package never throws.
Honesty notes
Whether a supply is actually subject to tevkifat depends on both parties' taxpayer status, which
this package does not know. applyWithholding computes the arithmetic for a given fraction; it
never decides whether tevkifat applies.
The 2026 partial-withholding threshold (THRESHOLD_GROSS, TRY 12,000 gross, effective
THRESHOLD_FROM = '2026-01-01') is exported for comparison, but this package never applies it —
whether the threshold matters depends on the transaction type and is a legal judgment this package
does not make.
Rates are a snapshot reviewed on 2026-08-23; verify against the official source before relying on
them. RATES_EFFECTIVE_FROM is the legal effective date of the rate table (unchanged since
2023-07-10); RATES_REVIEWED_AT is when a human last checked it against the official source —
reviewFreshness() measures staleness against the latter, not the former, so the flag does not
fire just because the rates themselves are old and unchanged.
reviewFreshness and stale never throw and never call process.exit — a stale rate table does
not crash your build. It is the caller's responsibility to check stale and act on it.
License
MIT — see LICENSE.
