@trajectonomics/tax 0.2.0-alpha.0 → 0.2.0-alpha.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["toMinorUnits","fromMinorUnits","clampCap","clampCap","clampCap","toMinorUnits","evaluateBrackets","fromMinorUnits","clampCap","clampCap","clampCap","clampCap","toMinorUnits","applyPercentage","fromMinorUnits","TaxValidationError","applyGrossUp","applyPercentage","multiplySafeInteger","applyPercentage","roundHalfUp","multiplySafeInteger","applyPercentage","TaxValidationError","thb"],"sources":["../../src/th/parameters-brackets.ts","../../src/th/parameters-special.ts","../../src/th/parameters.ts","../../src/th/types/special-input.ts","../../src/th/units.ts","../../src/th/stages/allowances-donations.ts","../../src/th/stages/allowances-family.ts","../../src/th/stages/allowances-insurance.ts","../../src/th/stages/allowances-retirement.ts","../../src/th/stages/allowances.ts","../../src/th/stages/assessable.ts","../../src/th/stages/bracket-eval.ts","../../src/th/stages/combine.ts","../../src/th/stages/expenses-40-1-2.ts","../../src/th/stages/expenses-40-3.ts","../../src/th/stages/expenses-40-4.ts","../../src/th/stages/electable-expense.ts","../../src/th/stages/expenses-40-5.ts","../../src/th/stages/expenses-40-6.ts","../../src/th/stages/expenses-40-7.ts","../../src/th/stages/expenses-40-8.ts","../../src/th/stages/expenses.ts","../../src/th/stages/minimum-tax.ts","../../src/th/validate.ts","../../src/th/stages/dividend-credit.ts","../../src/th/stages/final-withholding-source.ts","../../src/th/stages/final-withholding.ts","../../src/th/stages/real-estate-expense.ts","../../src/th/stages/real-estate.ts","../../src/th/stages/regular-income.ts","../../src/th/stages/separate-sheet.ts","../../src/th/stages/special-regimes.ts","../../src/th/stages/pipeline.ts","../../src/th/calculate.ts","../../src/th/system.ts"],"sourcesContent":["import type { TaxBracket } from \"../core/brackets\"\n\n/**\n * Section 48(1) progressive net-income brackets for Tax Year 2026 (THB major units, rates as ratios).\n *\n * @statute Thailand Revenue Code Section 48(1); Royal Decree No. 650.\n */\nexport const TH_2026_BRACKETS: readonly TaxBracket[] = [\n\t{ upTo: 150_000, rate: 0.0 },\n\t{ upTo: 300_000, rate: 0.05 },\n\t{ upTo: 500_000, rate: 0.1 },\n\t{ upTo: 750_000, rate: 0.15 },\n\t{ upTo: 1_000_000, rate: 0.2 },\n\t{ upTo: 2_000_000, rate: 0.25 },\n\t{ upTo: 5_000_000, rate: 0.3 },\n\t{ upTo: null, rate: 0.35 },\n]\n","import { TH_2026_BRACKETS } from \"./parameters-brackets\"\nimport type { ThParameters } from \"./parameters-types\"\n\n/**\n * Section 40(4) final withholding rates for Tax Year 2026.\n *\n * @statute Thailand Revenue Code Sections 40(4), 50(2)(g), 57 ter.\n */\nexport const TH_2026_FINAL_WITHHOLDING: ThParameters[\"finalWithholding\"] = {\n\tinterestRate: 0.15,\n\tdividendRate: 0.1,\n\t// Assumption: the issue gives no digital asset rate; 15% mirrors interest.\n\tdigitalAssetRate: 0.15,\n}\n\n/**\n * Section 48(5) separate sheet parameters for Tax Year 2026: exemption, expenses, and schedule.\n * The schedule is the Section 48(1) ladder without its 0% tier (same upper bounds and rates).\n *\n * @statute Thailand Revenue Code Section 48(5), Section 42(1).\n */\nexport const TH_2026_SEPARATE_SHEET: ThParameters[\"separateSheet\"] = {\n\tminYearsOfService: 5,\n\texemptionWageDays: 400,\n\texemptionCeiling: 600_000,\n\texpensePerYearOfService: 7_000,\n\tremainderExpenseRate: 0.5,\n\tbrackets: TH_2026_BRACKETS.filter((bracket) => bracket.rate > 0),\n}\n\n/**\n * Section 48(4) real estate transfer parameters for Tax Year 2026: the commercial holding-year\n * expense table, the flat inheritance/gift rate, the 10-year holding cap, the annualised schedule\n * (no 0% tier, per the Revenue Department's worked examples), and the 20% tax cap.\n *\n * @statute Thailand Revenue Code Section 48(4), Ministerial Regulation No. 126.\n */\nexport const TH_2026_REAL_ESTATE: ThParameters[\"realEstate\"] = {\n\texpenseTable: [\n\t\t{ minYears: 1, rate: 0.92 },\n\t\t{ minYears: 2, rate: 0.84 },\n\t\t{ minYears: 3, rate: 0.77 },\n\t\t{ minYears: 4, rate: 0.71 },\n\t\t{ minYears: 5, rate: 0.65 },\n\t\t{ minYears: 6, rate: 0.6 },\n\t\t{ minYears: 7, rate: 0.55 },\n\t\t{ minYears: 8, rate: 0.5 },\n\t],\n\tinheritanceExpenseRate: 0.5,\n\tmaxHoldingYears: 10,\n\tbrackets: TH_2026_SEPARATE_SHEET.brackets,\n\ttaxCapRate: 0.2,\n}\n","import { TH_2026_BRACKETS } from \"./parameters-brackets\"\nimport {\n\tTH_2026_FINAL_WITHHOLDING,\n\tTH_2026_REAL_ESTATE,\n\tTH_2026_SEPARATE_SHEET,\n} from \"./parameters-special\"\nimport type { ThParameters } from \"./parameters-types\"\n\n/**\n * Thailand Statutory Tax Parameters for Tax Year 2026 (Revenue Code ภ.ง.ด.90/91).\n *\n * @statute Thailand Revenue Code Sections 40, 42 bis, 47, 47 bis, 48(1)–(5); Royal Decrees No. 11, 650.\n */\nexport const TH_2026_PARAMETERS: ThParameters = {\n\ttaxYear: 2026,\n\tcurrency: \"THB\",\n\texpenses: {\n\t\tsection40_1_2: { rate: 0.5, ceiling: 100_000 },\n\t\tsection40_3: { rate: 0.5, ceiling: 100_000 },\n\t\tsection40_4: { rate: 0.0 },\n\t\tsection40_5: {\n\t\t\tbuilding: 0.3,\n\t\t\tagriculturalLand: 0.2,\n\t\t\tnonAgriculturalLand: 0.15,\n\t\t\tvehicles: 0.3,\n\t\t\tother: 0.1,\n\t\t},\n\t\tsection40_6: {\n\t\t\tmedical: 0.6,\n\t\t\tother: 0.3,\n\t\t},\n\t\tsection40_7: { rate: 0.6 },\n\t\tsection40_8: {\n\t\t\tgeneralRate: 0.6,\n\t\t\tentertainerFirstTierRate: 0.6,\n\t\t\tentertainerFirstTierThreshold: 300_000,\n\t\t\tentertainerSecondTierRate: 0.4,\n\t\t\tentertainerCeiling: 600_000,\n\t\t},\n\t},\n\tallowances: {\n\t\tpersonal: 60_000,\n\t\tspouse: 60_000,\n\t\tchild: {\n\t\t\tbasePerChild: 30_000,\n\t\t\tbonusSecondPlusBorn2018OrLater: 30_000,\n\t\t\tbonusCutoffYear: 2018,\n\t\t},\n\t\tparent: {\n\t\t\tperPerson: 30_000,\n\t\t\tmaxCount: 4,\n\t\t},\n\t\tdisabledPerPerson: 60_000,\n\t\tsocialSecurityFundCeiling: 10_500,\n\t\tlifeInsurance: {\n\t\t\tcombinedCeiling: 100_000,\n\t\t\thealthCeiling: 25_000,\n\t\t},\n\t\tparentHealthInsuranceCeiling: 15_000,\n\t\tspouseLifeInsuranceCeiling: 10_000,\n\t\tmortgageInterestCeiling: 100_000,\n\t\tretirementCluster: {\n\t\t\tcombinedCeiling: 500_000,\n\t\t\trmf: { maxRate: 0.3, ceiling: 500_000 },\n\t\t\tssf: { maxRate: 0.3, ceiling: 200_000 },\n\t\t\tpvd: { maxRate: 0.15, ceiling: 500_000 },\n\t\t\tgpf: { maxRate: 0.3, ceiling: 500_000 },\n\t\t\tnsf: { ceiling: 30_000 },\n\t\t\tpensionLifeInsurance: { maxRate: 0.15, ceiling: 200_000 },\n\t\t},\n\t\tthaiEsg: { maxRate: 0.3, ceiling: 300_000 },\n\t\tdonations: {\n\t\t\tdonation2xMultiplier: 2,\n\t\t\tdonation2xMaxRate: 0.1,\n\t\t\tdonation1xMaxRate: 0.1,\n\t\t},\n\t},\n\tbrackets: TH_2026_BRACKETS,\n\tminimumGrossTax: {\n\t\trate: 0.005,\n\t\tincomeThreshold: 120_000,\n\t\ttaxThreshold: 5_000,\n\t},\n\tfinalWithholding: TH_2026_FINAL_WITHHOLDING,\n\tseparateSheet: TH_2026_SEPARATE_SHEET,\n\trealEstate: TH_2026_REAL_ESTATE,\n}\n","/**\n * Election values for a Section 40(4) source: keep source withholding as final tax, or include\n * the income in the regular progressive computation. Plain `\"final\"`/`\"include\"` strings are\n * accepted wherever {@link ThFinalWithholdingElection} is expected.\n *\n * @statute Thailand Revenue Code Sections 40(4), 50(2)(g), 57 ter.\n */\nexport const ThFinalWithholdingElection = {\n\tFinal: \"final\",\n\tInclude: \"include\",\n} as const\n\n/** Union of {@link ThFinalWithholdingElection} values. */\nexport type ThFinalWithholdingElection =\n\t(typeof ThFinalWithholdingElection)[keyof typeof ThFinalWithholdingElection]\n\n/**\n * Section 40(4) interest or digital asset income with an optional final withholding election.\n * Units: THB major units.\n *\n * @statute Thailand Revenue Code Sections 40(4), 50(2)(g), 57 ter.\n */\nexport interface ThSection40_4Source {\n\t/** Gross amount received, in THB (non-negative, finite). */\n\treadonly amount: number\n\t/** Defaults to `\"include\"`. */\n\treadonly election?: ThFinalWithholdingElection\n}\n\n/**\n * Section 40(4) dividend income with election and optional Thai corporate tax rate.\n * Units: THB major units; `corpTaxRate` is a ratio.\n *\n * @statute Thailand Revenue Code Sections 40(4), 47 bis.\n */\nexport interface ThSection40_4Dividend extends ThSection40_4Source {\n\t/** Corporate income tax rate in [0, 1) paid by the distributing company (Section 47 bis). */\n\treadonly corpTaxRate?: number\n}\n\n/**\n * Structured Section 40(4) input splitting interest, dividends, and digital assets.\n * Units: THB major units.\n *\n * @statute Thailand Revenue Code Section 40(4).\n */\nexport interface ThSection40_4Input {\n\t/** Other Section 40(4) income always included in regular brackets. */\n\treadonly amount?: number\n\t/** Interest; a bare number means included in regular brackets. */\n\treadonly interest?: number | ThSection40_4Source\n\t/** Dividends; a bare number means included, with no Section 47 bis credit. */\n\treadonly dividends?: number | ThSection40_4Dividend\n\t/** Digital asset income; a bare number means included in regular brackets. */\n\treadonly digitalAssets?: number | ThSection40_4Source\n}\n\n/**\n * Kind of Section 48(5) lump sum. Only labour-law `\"severance\"` carries the 400-day / 600,000 THB\n * exemption; other `\"retirement\"` payments (e.g. a pension fund lump sum) get none.\n * Plain `\"severance\"`/`\"retirement\"` strings are accepted wherever\n * {@link ThSeparateSheetPaymentType} is expected.\n *\n * @statute Thailand Revenue Code Section 48(5); Section 42(1) (severance exemption).\n */\nexport const ThSeparateSheetPaymentType = {\n\tSeverance: \"severance\",\n\tRetirement: \"retirement\",\n} as const\n\n/** Union of {@link ThSeparateSheetPaymentType} values. */\nexport type ThSeparateSheetPaymentType =\n\t(typeof ThSeparateSheetPaymentType)[keyof typeof ThSeparateSheetPaymentType]\n\n/**\n * Section 48(5) one-time severance or retirement lump sum.\n * Units: THB major units; `yearsOfService` is a count.\n *\n * @statute Thailand Revenue Code Section 48(5), Section 42(1).\n */\nexport interface ThSeparateSheetInput {\n\t/** Lump sum received, in THB. */\n\treadonly amount: number\n\t/** Completed years of service (non-negative safe integer; callers round part-years up). */\n\treadonly yearsOfService: number\n\t/** Last daily wage in THB, basis for the 400-day exemption. */\n\treadonly lastDailyWage: number\n\t/** Defaults to `\"severance\"`; `\"retirement\"` payments receive no exemption. */\n\treadonly paymentType?: ThSeparateSheetPaymentType\n}\n\n/**\n * How a Section 48(4) property was acquired. Inherited or gifted property takes a flat 50%\n * expense; commercially acquired (or other) property takes the holding-year table. The two are\n * never combined. Plain `\"commercial\"`/`\"inheritance_or_gift\"` strings are accepted wherever\n * {@link ThRealEstateAcquisitionType} is expected.\n *\n * @statute Thailand Revenue Code Section 48(4); Ministerial Regulation No. 126.\n */\nexport const ThRealEstateAcquisitionType = {\n\tCommercial: \"commercial\",\n\tInheritanceOrGift: \"inheritance_or_gift\",\n} as const\n\n/** Union of {@link ThRealEstateAcquisitionType} values. */\nexport type ThRealEstateAcquisitionType =\n\t(typeof ThRealEstateAcquisitionType)[keyof typeof ThRealEstateAcquisitionType]\n\n/**\n * Which expense deduction a Section 48(4) computation used, reported in the breakdown.\n *\n * @statute Thailand Revenue Code Section 48(4); Ministerial Regulation No. 126.\n */\nexport const ThRealEstateExpenseBasis = {\n\tHoldingPeriodTable: \"holding_period_table\",\n\tFlatInheritance: \"flat_inheritance\",\n} as const\n\n/** Union of {@link ThRealEstateExpenseBasis} values. */\nexport type ThRealEstateExpenseBasis =\n\t(typeof ThRealEstateExpenseBasis)[keyof typeof ThRealEstateExpenseBasis]\n\n/**\n * Section 48(4) real estate transfer subject to separate computation.\n * Units: THB major units; `holdingYears` is a count.\n *\n * @statute Thailand Revenue Code Section 48(4).\n */\nexport interface ThRealEstateTransferInput {\n\t/**\n\t * Official appraised value in THB (ราคาประเมินทุนทรัพย์, Section 49 bis), not the actual sale\n\t * price.\n\t */\n\treadonly grossTransferValue: number\n\t/**\n\t * Years the property was held (positive safe integer). Callers round part-years up; the\n\t * calculation counts at most 10 years.\n\t */\n\treadonly holdingYears: number\n\t/** Defaults to `\"commercial\"`; `\"inheritance_or_gift\"` uses a flat 50% expense. */\n\treadonly acquisitionType?: ThRealEstateAcquisitionType\n}\n","import { fromMinorUnits, toMinorUnits } from \"../core/money\"\nimport { TH_2026_PARAMETERS } from \"./parameters\"\n\n/**\n * Converts a THB major-unit amount to integer satang.\n *\n * Purpose: Single THB-bound entry point so stages never repeat the currency threading.\n * Math: Decimal-exact conversion via `toMinorUnits` (half away from zero beyond 2 decimals).\n * Units: THB major units in, integer satang out.\n * Throws: TaxValidationError if the amount is negative (unless allowed), non-finite, or unsafe.\n *\n * @param amount Amount in THB.\n * @param field Diagnostic field name reported on failure.\n * @statute Thailand Revenue Code Section 40 (amounts expressed in baht).\n */\nexport function toSatang(amount: number, field?: string): number {\n\treturn toMinorUnits(amount, TH_2026_PARAMETERS.currency, field)\n}\n\n/**\n * Converts integer satang back to THB major units.\n *\n * Purpose: Counterpart of {@link toSatang}.\n * Math: `satang / 100`, fixed to two decimals.\n * Units: Integer satang in, THB major units out.\n * Throws: TaxValidationError if the input is not a safe integer (or is negative without\n * `allowNegative`).\n *\n * @param satang Integer satang.\n * @param field Diagnostic field name reported on failure.\n * @param options Set `allowNegative` for refund balances.\n * @statute Thailand Revenue Code Section 40 (amounts expressed in baht).\n */\nexport function fromSatang(\n\tsatang: number,\n\tfield?: string,\n\toptions?: { readonly allowNegative?: boolean },\n): number {\n\treturn fromMinorUnits(satang, TH_2026_PARAMETERS.currency, field, options)\n}\n\n/**\n * Sums non-negative THB amounts exactly in integer satang.\n *\n * Purpose: Avoids binary float drift when aggregating gross amounts.\n * Math: Converts each amount to satang, adds, converts back to major units.\n * Units: THB major units in and out.\n * Throws: TaxValidationError if any amount is negative or non-finite, or the sum is not a safe integer.\n *\n * @param values Amounts in THB.\n * @param field Diagnostic field name reported on failure.\n * @statute Thailand Revenue Code Section 40 (gross income aggregation).\n */\nexport function sumThAmounts(values: readonly number[], field?: string): number {\n\treturn fromSatang(\n\t\tvalues.reduce((sum, value) => sum + toSatang(value, field), 0),\n\t\tfield,\n\t)\n}\n","import type { ClampedAmount } from \"../../core/caps\"\nimport { validateMonetaryAmount } from \"../../core/money\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport { ThAllowanceField, type ThDonationsBreakdown } from \"../types\"\n\nexport interface ThDonationAllowancesResult {\n\treadonly breakdown: ThDonationsBreakdown\n\treadonly allowedDonationsTotal: number\n\treadonly netTaxableIncome: number\n}\n\n/**\n * Calculates qualified 2x and general 1x donation allowances.\n *\n * Math: 2x donations capped at 10% of net before donations; 1x donations capped at 10% of remaining net.\n * Units: THB major units.\n *\n * @statute Thailand Revenue Code Section 47(7); Royal Decrees No. 428, 558.\n */\nexport function calculateDonationAllowances(\n\tdonation2x: number | undefined,\n\tdonation1x: number | undefined,\n\tnetBeforeDonations: number,\n): ThDonationAllowancesResult {\n\tconst p = TH_2026_PARAMETERS.allowances.donations\n\tconst net = Math.max(0, netBeforeDonations)\n\n\tconst raw2x = donation2x ?? 0\n\tvalidateMonetaryAmount(raw2x, ThAllowanceField.Donation2x)\n\tconst eligible2x = raw2x * p.donation2xMultiplier\n\tconst cap2x = Math.floor(net * p.donation2xMaxRate)\n\tconst allowed2x = Math.min(eligible2x, cap2x)\n\tconst clamped2x: ClampedAmount = {\n\t\trequested: raw2x,\n\t\tallowed: allowed2x,\n\t\tcap: cap2x,\n\t\twasClamped: eligible2x > cap2x,\n\t}\n\n\tconst remainingNet = Math.max(0, net - allowed2x)\n\tconst raw1x = donation1x ?? 0\n\tvalidateMonetaryAmount(raw1x, ThAllowanceField.Donation1x)\n\tconst cap1x = Math.floor(remainingNet * p.donation1xMaxRate)\n\tconst allowed1x = Math.min(raw1x, cap1x)\n\tconst clamped1x: ClampedAmount = {\n\t\trequested: raw1x,\n\t\tallowed: allowed1x,\n\t\tcap: cap1x,\n\t\twasClamped: raw1x > cap1x,\n\t}\n\n\tconst breakdown: ThDonationsBreakdown = {\n\t\tdonation2x: clamped2x,\n\t\tdonation1x: clamped1x,\n\t\ttotal: allowed2x + allowed1x,\n\t}\n\n\tconst netTaxableIncome = Math.max(0, remainingNet - allowed1x)\n\n\treturn {\n\t\tbreakdown,\n\t\tallowedDonationsTotal: breakdown.total,\n\t\tnetTaxableIncome,\n\t}\n}\n","import { type ClampedAmount, clampCap } from \"../../core/caps\"\nimport { validateMonetaryAmount } from \"../../core/money\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport {\n\tThAllowanceField,\n\ttype ThAllowancesInput,\n\ttype ThChild,\n\ttype ThChildrenInput,\n\ttype ThFilingStatus,\n\ttype ThParentsInput,\n} from \"../types\"\n\nexport interface ThFamilyAllowancesResult {\n\treadonly personal: ClampedAmount\n\treadonly spouse: ClampedAmount\n\treadonly children: ClampedAmount\n\treadonly parents: ClampedAmount\n\treadonly disabled: ClampedAmount\n}\n\nconst unclamped = (n: number): ClampedAmount => ({\n\trequested: n,\n\tallowed: n,\n\tcap: n,\n\twasClamped: false,\n})\n\nfunction countBonusChildren(children: readonly ThChild[], cutoff: number): number {\n\tconst sorted = [...children].sort((a, b) => (a.birthYear ?? 0) - (b.birthYear ?? 0))\n\tlet bonus = 0\n\tfor (let i = 1; i < sorted.length; i++) {\n\t\tif ((sorted[i]?.birthYear ?? 0) >= cutoff) bonus++\n\t}\n\treturn bonus\n}\n\nfunction computeChildAllowance(children: ThAllowancesInput[\"children\"]): ClampedAmount {\n\tif (!children) return unclamped(0)\n\tconst p = TH_2026_PARAMETERS.allowances.child\n\tif (Array.isArray(children)) {\n\t\tconst bonus = countBonusChildren(children, p.bonusCutoffYear)\n\t\treturn unclamped(children.length * p.basePerChild + bonus * p.bonusSecondPlusBorn2018OrLater)\n\t}\n\tconst counts = children as ThChildrenInput\n\tvalidateMonetaryAmount(counts.totalCount ?? 0, ThAllowanceField.Children)\n\tconst count = Math.max(0, Math.floor(counts.totalCount ?? 0))\n\tconst bonusReq = Math.max(0, Math.floor(counts.secondPlusBorn2018OrLaterCount ?? 0))\n\tconst bonus = Math.min(bonusReq, Math.max(0, count - 1))\n\treturn unclamped(count * p.basePerChild + bonus * p.bonusSecondPlusBorn2018OrLater)\n}\n\nfunction computeParentCount(\n\tparents: ThParentsInput | number | undefined,\n\tstatus: ThFilingStatus,\n): number {\n\tif (parents === undefined) return 0\n\tif (typeof parents === \"number\") {\n\t\tvalidateMonetaryAmount(parents, ThAllowanceField.Parents)\n\t\treturn Math.floor(parents)\n\t}\n\tlet count = (parents.taxpayerFather ? 1 : 0) + (parents.taxpayerMother ? 1 : 0)\n\tif (status === \"married_no_income_spouse\") {\n\t\tcount += (parents.spouseFather ? 1 : 0) + (parents.spouseMother ? 1 : 0)\n\t}\n\treturn count\n}\n\n/**\n * Calculates family allowances (personal, spouse, children, parents, disabled).\n *\n * @statute Thailand Revenue Code Section 47(1)(a)–(f).\n */\nexport function calculateFamilyAllowances(\n\tinput: ThAllowancesInput = {},\n\tfilingStatus: ThFilingStatus = \"single\",\n): ThFamilyAllowancesResult {\n\tconst p = TH_2026_PARAMETERS.allowances\n\tconst personalReq = input.personal ?? p.personal\n\tconst personal = clampCap(personalReq, p.personal, ThAllowanceField.Personal)\n\n\tconst isSpouseEligible =\n\t\tfilingStatus === \"married_no_income_spouse\" || filingStatus === \"married_joint\"\n\tconst spouseRequested = input.spouse ?? (isSpouseEligible ? p.spouse : 0)\n\tconst spouse = isSpouseEligible\n\t\t? clampCap(spouseRequested, p.spouse, ThAllowanceField.Spouse)\n\t\t: {\n\t\t\t\trequested: spouseRequested,\n\t\t\t\tallowed: 0,\n\t\t\t\tcap: 0,\n\t\t\t\twasClamped: spouseRequested > 0,\n\t\t\t}\n\n\tconst children = computeChildAllowance(input.children)\n\tconst parentCount = computeParentCount(input.parents, filingStatus)\n\tconst parentsRequested = parentCount * p.parent.perPerson\n\tconst parents = clampCap(\n\t\tparentsRequested,\n\t\tp.parent.perPerson * p.parent.maxCount,\n\t\tThAllowanceField.Parents,\n\t)\n\n\tconst disabledRequested = (input.disabledCareCount ?? 0) * p.disabledPerPerson\n\tvalidateMonetaryAmount(disabledRequested, ThAllowanceField.Disabled)\n\tconst disabled = unclamped(disabledRequested)\n\n\treturn { personal, spouse, children, parents, disabled }\n}\n","import { type ClampedAmount, clampCap } from \"../../core/caps\"\nimport { validateMonetaryAmount } from \"../../core/money\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport { ThAllowanceField, type ThAllowancesInput, type ThFilingStatus } from \"../types\"\n\nexport interface ThInsuranceAllowancesResult {\n\treadonly socialSecurity: ClampedAmount\n\treadonly lifeInsurance: ClampedAmount\n\treadonly parentHealthInsurance: ClampedAmount\n\treadonly spouseLifeInsurance: ClampedAmount\n\treadonly mortgageInterest: ClampedAmount\n}\n\n/**\n * Calculates insurance, social security, and housing mortgage interest allowances.\n *\n * @statute Thailand Revenue Code Section 47(1)(d), (g), (h), (j).\n */\nexport function calculateInsuranceAllowances(\n\tinput: ThAllowancesInput = {},\n\tfilingStatus: ThFilingStatus = \"single\",\n): ThInsuranceAllowancesResult {\n\tconst p = TH_2026_PARAMETERS.allowances\n\n\tconst socialSecurity = clampCap(\n\t\tinput.socialSecurity ?? 0,\n\t\tp.socialSecurityFundCeiling,\n\t\tThAllowanceField.SocialSecurity,\n\t)\n\n\tconst healthClamped = clampCap(\n\t\tinput.healthInsurance ?? 0,\n\t\tp.lifeInsurance.healthCeiling,\n\t\tThAllowanceField.HealthInsurance,\n\t)\n\tconst rawLife = input.lifeInsurance ?? 0\n\tif (input.lifeInsurance !== undefined) {\n\t\tvalidateMonetaryAmount(rawLife, ThAllowanceField.LifeHealthCombined)\n\t}\n\tconst lifeCombinedRequested = rawLife + (input.healthInsurance ?? 0)\n\tconst lifeEffectiveRequested = rawLife + healthClamped.allowed\n\tconst lifeCombined = clampCap(\n\t\tlifeEffectiveRequested,\n\t\tp.lifeInsurance.combinedCeiling,\n\t\tThAllowanceField.LifeHealthCombined,\n\t)\n\tconst lifeInsurance: ClampedAmount = {\n\t\trequested: lifeCombinedRequested,\n\t\tallowed: lifeCombined.allowed,\n\t\tcap: p.lifeInsurance.combinedCeiling,\n\t\twasClamped: lifeCombinedRequested > lifeCombined.allowed,\n\t}\n\n\tconst parentHealthInsurance = clampCap(\n\t\tinput.parentHealthInsurance ?? 0,\n\t\tp.parentHealthInsuranceCeiling,\n\t\tThAllowanceField.ParentHealthInsurance,\n\t)\n\n\tconst isSpouseEligible = filingStatus === \"married_no_income_spouse\"\n\tconst spouseLifeReq = input.spouseLifeInsurance ?? 0\n\tconst spouseLifeInsurance: ClampedAmount = isSpouseEligible\n\t\t? clampCap(spouseLifeReq, p.spouseLifeInsuranceCeiling, ThAllowanceField.SpouseLifeInsurance)\n\t\t: {\n\t\t\t\trequested: spouseLifeReq,\n\t\t\t\tallowed: 0,\n\t\t\t\tcap: 0,\n\t\t\t\twasClamped: spouseLifeReq > 0,\n\t\t\t}\n\n\tconst mortgageInterest = clampCap(\n\t\tinput.mortgageInterest ?? 0,\n\t\tp.mortgageInterestCeiling,\n\t\tThAllowanceField.MortgageInterest,\n\t)\n\n\treturn {\n\t\tsocialSecurity,\n\t\tlifeInsurance,\n\t\tparentHealthInsurance,\n\t\tspouseLifeInsurance,\n\t\tmortgageInterest,\n\t}\n}\n","import { type ClampedAmount, clampCap } from \"../../core/caps\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport {\n\tThAllowanceField,\n\ttype ThAllowancesInput,\n\ttype ThRetirementClusterBreakdown,\n} from \"../types\"\n\nexport interface ThRetirementAllowancesResult {\n\treadonly retirementCluster: ThRetirementClusterBreakdown\n\treadonly thaiEsg: ClampedAmount\n\treadonly allowedClusterTotal: number\n\treadonly allowedThaiEsgTotal: number\n}\n\n/**\n * Calculates Retirement Cluster and ThaiESG allowances.\n *\n * @statute Thailand Revenue Code Section 47; Ministerial Regulations No. 126, 390.\n */\nexport function calculateRetirementAllowances(\n\tinput: ThAllowancesInput = {},\n\tassessableIncome: number,\n\twageBase: number,\n): ThRetirementAllowancesResult {\n\tconst p = TH_2026_PARAMETERS.allowances.retirementCluster\n\tconst currency = TH_2026_PARAMETERS.currency\n\tconst wage = Math.max(wageBase, 0)\n\n\tconst rmf = clampCap(\n\t\tinput.rmf ?? 0,\n\t\t{\n\t\t\tbase: assessableIncome,\n\t\t\trate: p.rmf.maxRate,\n\t\t\tceiling: p.rmf.ceiling,\n\t\t\tcurrency,\n\t\t},\n\t\tThAllowanceField.Rmf,\n\t)\n\tconst ssf = clampCap(\n\t\tinput.ssf ?? 0,\n\t\t{\n\t\t\tbase: assessableIncome,\n\t\t\trate: p.ssf.maxRate,\n\t\t\tceiling: p.ssf.ceiling,\n\t\t\tcurrency,\n\t\t},\n\t\tThAllowanceField.Ssf,\n\t)\n\tconst pvd = clampCap(\n\t\tinput.pvd ?? 0,\n\t\t{ base: wage, rate: p.pvd.maxRate, ceiling: p.pvd.ceiling, currency },\n\t\tThAllowanceField.Pvd,\n\t)\n\tconst gpf = clampCap(\n\t\tinput.gpf ?? 0,\n\t\t{ base: wage, rate: p.gpf.maxRate, ceiling: p.gpf.ceiling, currency },\n\t\tThAllowanceField.Gpf,\n\t)\n\tconst nsf = clampCap(input.nsf ?? 0, p.nsf.ceiling, ThAllowanceField.Nsf)\n\tconst pensionLifeInsurance = clampCap(\n\t\tinput.pensionLifeInsurance ?? 0,\n\t\t{\n\t\t\tbase: assessableIncome,\n\t\t\trate: p.pensionLifeInsurance.maxRate,\n\t\t\tceiling: p.pensionLifeInsurance.ceiling,\n\t\t\tcurrency,\n\t\t},\n\t\tThAllowanceField.PensionLife,\n\t)\n\n\tconst individualSum =\n\t\trmf.allowed +\n\t\tssf.allowed +\n\t\tpvd.allowed +\n\t\tgpf.allowed +\n\t\tnsf.allowed +\n\t\tpensionLifeInsurance.allowed\n\tconst combined = clampCap(individualSum, p.combinedCeiling, ThAllowanceField.RetirementCluster)\n\n\tconst retirementCluster: ThRetirementClusterBreakdown = {\n\t\trmf,\n\t\tssf,\n\t\tpvd,\n\t\tgpf,\n\t\tnsf,\n\t\tpensionLifeInsurance,\n\t\tcombined,\n\t}\n\n\tconst thaiEsgParam = TH_2026_PARAMETERS.allowances.thaiEsg\n\tconst thaiEsg = clampCap(\n\t\tinput.thaiEsg ?? 0,\n\t\t{\n\t\t\tbase: assessableIncome,\n\t\t\trate: thaiEsgParam.maxRate,\n\t\t\tceiling: thaiEsgParam.ceiling,\n\t\t\tcurrency,\n\t\t},\n\t\tThAllowanceField.ThaiEsg,\n\t)\n\n\treturn {\n\t\tretirementCluster,\n\t\tthaiEsg,\n\t\tallowedClusterTotal: combined.allowed,\n\t\tallowedThaiEsgTotal: thaiEsg.allowed,\n\t}\n}\n","import type { ThAllowancesBreakdown, ThAllowancesInput, ThFilingStatus } from \"../types\"\nimport { calculateDonationAllowances } from \"./allowances-donations\"\nimport { calculateFamilyAllowances } from \"./allowances-family\"\nimport { calculateInsuranceAllowances } from \"./allowances-insurance\"\nimport { calculateRetirementAllowances } from \"./allowances-retirement\"\n\nexport interface ThAllowancesResult {\n\treadonly breakdown: ThAllowancesBreakdown\n\treadonly netTaxableIncome: number\n}\n\n/**\n * Aggregates all personal, family, insurance, retirement, and donation allowances.\n *\n * @statute Thailand Revenue Code Section 47.\n */\nexport function calculateAllowances(\n\tinput: ThAllowancesInput = {},\n\tfilingStatus: ThFilingStatus = \"single\",\n\tassessableTotal: number,\n\texpensesTotal: number,\n\twageBase: number,\n): ThAllowancesResult {\n\tconst fam = calculateFamilyAllowances(input, filingStatus)\n\tconst ins = calculateInsuranceAllowances(input, filingStatus)\n\tconst ret = calculateRetirementAllowances(input, assessableTotal, wageBase)\n\n\tconst nonDonationTotal =\n\t\tfam.personal.allowed +\n\t\tfam.spouse.allowed +\n\t\tfam.children.allowed +\n\t\tfam.parents.allowed +\n\t\tfam.disabled.allowed +\n\t\tins.socialSecurity.allowed +\n\t\tins.lifeInsurance.allowed +\n\t\tins.parentHealthInsurance.allowed +\n\t\tins.spouseLifeInsurance.allowed +\n\t\tins.mortgageInterest.allowed +\n\t\tret.allowedClusterTotal +\n\t\tret.allowedThaiEsgTotal\n\n\tconst netBeforeDonations = Math.max(0, assessableTotal - expensesTotal - nonDonationTotal)\n\tconst don = calculateDonationAllowances(input.donation2x, input.donation1x, netBeforeDonations)\n\n\tconst total = nonDonationTotal + don.allowedDonationsTotal\n\n\tconst breakdown: ThAllowancesBreakdown = {\n\t\tpersonal: fam.personal,\n\t\tspouse: fam.spouse,\n\t\tchildren: fam.children,\n\t\tparents: fam.parents,\n\t\tdisabled: fam.disabled,\n\t\tsocialSecurity: ins.socialSecurity,\n\t\tlifeInsurance: ins.lifeInsurance,\n\t\tparentHealthInsurance: ins.parentHealthInsurance,\n\t\tspouseLifeInsurance: ins.spouseLifeInsurance,\n\t\tmortgageInterest: ins.mortgageInterest,\n\t\tretirementCluster: ret.retirementCluster,\n\t\tthaiEsg: ret.thaiEsg,\n\t\tdonations: don.breakdown,\n\t\ttotal,\n\t}\n\n\treturn {\n\t\tbreakdown,\n\t\tnetTaxableIncome: don.netTaxableIncome,\n\t}\n}\n","import { validateMonetaryAmount } from \"../../core/money\"\nimport {\n\tThAssessableField,\n\ttype ThAssessableIncomeBreakdown,\n\ttype ThAssessableIncomeInput,\n\ttype ThExpenseElectableIncome,\n\ttype ThSection40_5Input,\n\ttype ThSection40_6Input,\n\ttype ThSection40_8Input,\n} from \"../types\"\n\nexport function extractElectableAmount(\n\tval: number | ThExpenseElectableIncome | undefined,\n\tfield: string,\n): number {\n\tif (val === undefined) return 0\n\tconst amount = typeof val === \"number\" ? val : val.amount\n\tvalidateMonetaryAmount(amount, field)\n\tif (typeof val === \"object\" && val.actualExpense !== undefined) {\n\t\tvalidateMonetaryAmount(val.actualExpense, `${field}.actualExpense`)\n\t}\n\treturn amount\n}\n\nfunction sumSection40_5(input: ThSection40_5Input | undefined): number {\n\tif (!input) return 0\n\treturn (\n\t\textractElectableAmount(input.building, ThAssessableField.Section40_5_Building) +\n\t\textractElectableAmount(input.agriculturalLand, ThAssessableField.Section40_5_AgriculturalLand) +\n\t\textractElectableAmount(\n\t\t\tinput.nonAgriculturalLand,\n\t\t\tThAssessableField.Section40_5_NonAgriculturalLand,\n\t\t) +\n\t\textractElectableAmount(input.vehicles, ThAssessableField.Section40_5_Vehicles) +\n\t\textractElectableAmount(input.other, ThAssessableField.Section40_5_Other)\n\t)\n}\n\nfunction sumSection40_6(input: ThSection40_6Input | undefined): number {\n\tif (!input) return 0\n\treturn (\n\t\textractElectableAmount(input.medical, ThAssessableField.Section40_6_Medical) +\n\t\textractElectableAmount(input.law, ThAssessableField.Section40_6_Law) +\n\t\textractElectableAmount(input.engineering, ThAssessableField.Section40_6_Engineering) +\n\t\textractElectableAmount(input.architecture, ThAssessableField.Section40_6_Architecture) +\n\t\textractElectableAmount(input.accounting, ThAssessableField.Section40_6_Accounting) +\n\t\textractElectableAmount(input.fineArts, ThAssessableField.Section40_6_FineArts) +\n\t\textractElectableAmount(input.other, ThAssessableField.Section40_6_Other)\n\t)\n}\n\nfunction sumSection40_8(\n\tinput: number | ThExpenseElectableIncome | ThSection40_8Input | undefined,\n): number {\n\tif (input === undefined) return 0\n\tif (typeof input === \"number\" || \"amount\" in input) {\n\t\treturn extractElectableAmount(\n\t\t\tinput as number | ThExpenseElectableIncome,\n\t\t\tThAssessableField.Section40_8,\n\t\t)\n\t}\n\treturn (\n\t\textractElectableAmount(input.general, ThAssessableField.Section40_8_General) +\n\t\textractElectableAmount(input.entertainer, ThAssessableField.Section40_8_Entertainer)\n\t)\n}\n\n/**\n * Normalizes and validates assessable income across Section 40(1)–40(8).\n *\n * @statute Thailand Revenue Code Section 40(1)–(8).\n */\nexport function calculateAssessableIncome(\n\tinput: ThAssessableIncomeInput = {},\n): ThAssessableIncomeBreakdown {\n\tconst s1 = extractElectableAmount(input.section40_1, ThAssessableField.Section40_1)\n\tconst s2 = extractElectableAmount(input.section40_2, ThAssessableField.Section40_2)\n\tconst s3 = extractElectableAmount(input.section40_3, ThAssessableField.Section40_3)\n\tconst s4 = extractElectableAmount(\n\t\ttypeof input.section40_4 === \"number\" ? input.section40_4 : input.section40_4?.amount,\n\t\tThAssessableField.Section40_4,\n\t)\n\tconst s5 = sumSection40_5(input.section40_5)\n\tconst s6 = sumSection40_6(input.section40_6)\n\tconst s7 = extractElectableAmount(input.section40_7, ThAssessableField.Section40_7)\n\tconst s8 = sumSection40_8(input.section40_8)\n\n\tconst total = s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8\n\tconst grossNonEmploymentIncome = s2 + s3 + s4 + s5 + s6 + s7 + s8\n\n\treturn {\n\t\tsection40_1: s1,\n\t\tsection40_2: s2,\n\t\tsection40_3: s3,\n\t\tsection40_4: s4,\n\t\tsection40_5: s5,\n\t\tsection40_6: s6,\n\t\tsection40_7: s7,\n\t\tsection40_8: s8,\n\t\ttotal,\n\t\tgrossNonEmploymentIncome,\n\t}\n}\n","import {\n\ttype BracketEvaluationResult,\n\tevaluateBrackets,\n\ttype TaxBracket,\n} from \"../../core/brackets\"\nimport { fromMinorUnits, toMinorUnits } from \"../../core/money\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport { ThBracketField } from \"../types\"\n\n/**\n * Evaluates an arbitrary Thai statutory bracket schedule (major-unit bounds) in minor units.\n *\n * Math: Converts bounds and incomes to satang, slices through `evaluateBrackets`, converts back.\n * Units: Inputs and outputs in THB major units; arithmetic in integer satang.\n * Throws: TaxValidationError if incomes are negative or non-finite.\n *\n * @statute Thailand Revenue Code Sections 48(1), 48(4), 48(5).\n */\nexport function evaluateThBracketSet(\n\ttaxableIncome: number,\n\tgrossIncome: number,\n\tbrackets: readonly TaxBracket[],\n): {\n\ttotalTax: number\n\tbrackets: BracketEvaluationResult\n} {\n\tconst currency = TH_2026_PARAMETERS.currency\n\tconst minorBrackets = brackets.map((b) => ({\n\t\tupTo: b.upTo === null ? null : toMinorUnits(b.upTo, currency),\n\t\trate: b.rate,\n\t}))\n\tconst taxableMinor = toMinorUnits(taxableIncome, currency, ThBracketField.TaxableIncome)\n\tconst grossMinor = toMinorUnits(grossIncome, currency, ThBracketField.GrossIncome)\n\tconst res = evaluateBrackets(taxableMinor, minorBrackets, grossMinor)\n\n\tconst slices = res.slices.map((s) => ({\n\t\tbracketIndex: s.bracketIndex,\n\t\tlowerBound: fromMinorUnits(s.lowerBound, currency),\n\t\tupperBound: s.upperBound !== null ? fromMinorUnits(s.upperBound, currency) : null,\n\t\trate: s.rate,\n\t\ttaxableAmount: fromMinorUnits(s.taxableAmount, currency),\n\t\ttax: fromMinorUnits(s.tax, currency),\n\t}))\n\n\tconst totalTax = fromMinorUnits(res.totalTax, currency)\n\treturn {\n\t\ttotalTax,\n\t\tbrackets: {\n\t\t\ttotalTax,\n\t\t\tslices,\n\t\t\teffectiveRate: res.effectiveRate,\n\t\t\tmarginalRate: res.marginalRate,\n\t\t},\n\t}\n}\n\n/**\n * Evaluates the regular progressive tax brackets for Thailand PIT.\n *\n * Math: Slices taxable income through `TH_2026_PARAMETERS.brackets`.\n * Units: THB major units.\n * Throws: TaxValidationError if incomes are negative or non-finite.\n *\n * @statute Thailand Revenue Code Section 48(1).\n */\nexport function evaluateThBrackets(\n\ttaxableIncome: number,\n\tgrossIncome: number,\n): {\n\ttotalTax: number\n\tbrackets: BracketEvaluationResult\n} {\n\treturn evaluateThBracketSet(taxableIncome, grossIncome, TH_2026_PARAMETERS.brackets)\n}\n","import { type ThCombinedTaxBreakdown, ThCombineField, ThTaxField } from \"../types\"\nimport { fromSatang, toSatang } from \"../units\"\n\n/** Component taxes, credits, and payments to combine. Units: THB major units. */\nexport interface ThCombineInput {\n\t/** Section 48(1)/48(2) tax on the regular schedule. */\n\treadonly regularTax: number\n\t/** Section 48(5) separate sheet tax. */\n\treadonly separateSheetTax: number\n\t/** Section 48(4) real estate transfer tax. */\n\treadonly realEstateTax: number\n\t/** Section 40(4) tax withheld at source and elected final. */\n\treadonly finalWithholdingTax: number\n\t/** Section 47 bis dividend tax credit. */\n\treadonly dividendTaxCredit: number\n\t/** Other tax already withheld/paid, credited against the liability. */\n\treadonly withholdingTax: number\n}\n\n/**\n * Combines regular, separate sheet, real estate, and final withholding taxes into one liability.\n *\n * Purpose: Produces the single audited total and the amount still payable (or refundable).\n * Math: totalCombinedTax = regular + separate sheet + real estate + final withholding (before credits);\n * netTaxPayable = totalCombinedTax - final withholding (already withheld at source)\n * - Section 47 bis dividend credit - withholding tax paid. May be negative (refund).\n * Units: Inputs and outputs in THB major units; arithmetic in integer satang.\n * Throws: TaxValidationError if any component is negative or non-finite.\n *\n * @param input Component taxes, credits, and payments in THB.\n * @statute Thailand Revenue Code Sections 40(4), 47 bis, 48(1)–(5).\n */\nexport function combineThLiability(input: ThCombineInput): ThCombinedTaxBreakdown {\n\tconst regular = toSatang(input.regularTax, ThCombineField.RegularTax)\n\tconst separateSheet = toSatang(input.separateSheetTax, ThCombineField.SeparateSheetTax)\n\tconst realEstate = toSatang(input.realEstateTax, ThCombineField.RealEstateTax)\n\tconst finalWithholding = toSatang(input.finalWithholdingTax, ThCombineField.FinalWithholdingTax)\n\tconst dividendCredit = toSatang(input.dividendTaxCredit, ThCombineField.DividendTaxCredit)\n\tconst withheld = toSatang(input.withholdingTax, ThTaxField.WithholdingTax)\n\n\tconst total = regular + separateSheet + realEstate + finalWithholding\n\tconst credits = finalWithholding + dividendCredit + withheld\n\treturn {\n\t\ttotalCombinedTax: fromSatang(total, ThTaxField.TotalTax),\n\t\tnetTaxPayable: fromSatang(total - credits, ThTaxField.NetTaxPayable, { allowNegative: true }),\n\t}\n}\n","import { type ClampedAmount, clampCap } from \"../../core/caps\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport { ThExpenseField } from \"../types\"\n\n/**\n * Calculates shared Section 40(1) and Section 40(2) expense deduction.\n *\n * Math: 50% of combined 40(1) + 40(2) income, clamped to 100,000 THB ceiling.\n * Units: THB major units.\n *\n * @statute Thailand Revenue Code Section 42 bis.\n */\nexport function calculateExpenses40_1_2(s1: number, s2: number): ClampedAmount {\n\tconst combined = s1 + s2\n\tconst requested = Math.floor(combined * TH_2026_PARAMETERS.expenses.section40_1_2.rate)\n\treturn clampCap(\n\t\trequested,\n\t\tTH_2026_PARAMETERS.expenses.section40_1_2.ceiling,\n\t\tThExpenseField.Section40_1_2,\n\t)\n}\n","import { type ClampedAmount, clampCap } from \"../../core/caps\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport { type ThExpenseElectableIncome, ThExpenseField } from \"../types\"\n\n/**\n * Calculates Section 40(3) royalties/goodwill/copyright expense deduction.\n *\n * Math: Standard 50% capped at 100,000 THB or actual expenses capped at income.\n * Units: THB major units.\n *\n * @statute Thailand Revenue Code Section 42 ter; Royal Decree No. 11 Section 4.\n */\nexport function calculateExpenses40_3(\n\tinput: number | ThExpenseElectableIncome | undefined,\n\tincomeAmount: number,\n): ClampedAmount {\n\tif (incomeAmount <= 0) {\n\t\treturn { requested: 0, allowed: 0, cap: 0, wasClamped: false }\n\t}\n\tconst isActual = typeof input === \"object\" && input.expenseElection === \"actual\"\n\tif (isActual) {\n\t\tconst requested = input.actualExpense ?? 0\n\t\treturn clampCap(requested, incomeAmount, ThExpenseField.Section40_3)\n\t}\n\tconst requested = Math.floor(incomeAmount * TH_2026_PARAMETERS.expenses.section40_3.rate)\n\treturn clampCap(\n\t\trequested,\n\t\tTH_2026_PARAMETERS.expenses.section40_3.ceiling,\n\t\tThExpenseField.Section40_3,\n\t)\n}\n","import type { ClampedAmount } from \"../../core/caps\"\n\n/**\n * Calculates Section 40(4) expense deduction (0% statutory).\n *\n * Math: Thailand Revenue Code permits 0% expense deductions for interest, dividends, crypto.\n * Units: THB major units.\n *\n * @statute Thailand Revenue Code Section 40(4).\n */\nexport function calculateExpenses40_4(_incomeAmount: number): ClampedAmount {\n\treturn {\n\t\trequested: 0,\n\t\tallowed: 0,\n\t\tcap: 0,\n\t\twasClamped: false,\n\t}\n}\n","import { type ClampedAmount, clampCap } from \"../../core/caps\"\nimport type { ThExpenseElectableIncome } from \"../types\"\n\n/**\n * Computes expense deduction for an electable income item (standard % vs actual).\n *\n * Math: If actual, clamp to item income. If standard, apply percentage of item income.\n * Units: THB major units.\n *\n * @param item Income item number or configuration object.\n * @param standardRate Statutory percentage rate for standard deduction.\n * @param field Diagnostic field name.\n */\nexport function calculateElectableItemExpense(\n\titem: number | ThExpenseElectableIncome | undefined,\n\tstandardRate: number,\n\tfield: string,\n): ClampedAmount {\n\tif (!item) {\n\t\treturn { requested: 0, allowed: 0, cap: 0, wasClamped: false }\n\t}\n\tconst amount = typeof item === \"number\" ? item : item.amount\n\tif (amount <= 0) {\n\t\treturn { requested: 0, allowed: 0, cap: 0, wasClamped: false }\n\t}\n\tconst isActual = typeof item === \"object\" && item.expenseElection === \"actual\"\n\tif (isActual) {\n\t\tconst req = item.actualExpense ?? 0\n\t\treturn clampCap(req, amount, field)\n\t}\n\tconst req = Math.floor(amount * standardRate)\n\treturn { requested: req, allowed: req, cap: amount, wasClamped: false }\n}\n","import type { ClampedAmount } from \"../../core/caps\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport { ThExpenseField, type ThSection40_5Input } from \"../types\"\nimport { calculateElectableItemExpense } from \"./electable-expense\"\n\n/**\n * Calculates Section 40(5) rental expense deductions across property sub-categories.\n *\n * Math: Standard percentage per property class or actual expenses capped at income.\n * Units: THB major units.\n *\n * @statute Thailand Royal Decree No. 11 Section 5.\n */\nexport function calculateExpenses40_5(input: ThSection40_5Input | undefined): ClampedAmount {\n\tif (!input) {\n\t\treturn { requested: 0, allowed: 0, cap: 0, wasClamped: false }\n\t}\n\tconst rates = TH_2026_PARAMETERS.expenses.section40_5\n\tconst b = calculateElectableItemExpense(\n\t\tinput.building,\n\t\trates.building,\n\t\tThExpenseField.Section40_5_Building,\n\t)\n\tconst a = calculateElectableItemExpense(\n\t\tinput.agriculturalLand,\n\t\trates.agriculturalLand,\n\t\tThExpenseField.Section40_5_AgriculturalLand,\n\t)\n\tconst na = calculateElectableItemExpense(\n\t\tinput.nonAgriculturalLand,\n\t\trates.nonAgriculturalLand,\n\t\tThExpenseField.Section40_5_NonAgriculturalLand,\n\t)\n\tconst v = calculateElectableItemExpense(\n\t\tinput.vehicles,\n\t\trates.vehicles,\n\t\tThExpenseField.Section40_5_Vehicles,\n\t)\n\tconst o = calculateElectableItemExpense(\n\t\tinput.other,\n\t\trates.other,\n\t\tThExpenseField.Section40_5_Other,\n\t)\n\n\tconst requested = b.requested + a.requested + na.requested + v.requested + o.requested\n\tconst allowed = b.allowed + a.allowed + na.allowed + v.allowed + o.allowed\n\tconst cap = b.cap + a.cap + na.cap + v.cap + o.cap\n\n\treturn {\n\t\trequested,\n\t\tallowed,\n\t\tcap,\n\t\twasClamped: requested > allowed,\n\t}\n}\n","import type { ClampedAmount } from \"../../core/caps\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport { ThExpenseField, type ThSection40_6Input } from \"../types\"\nimport { calculateElectableItemExpense } from \"./electable-expense\"\n\n/**\n * Calculates Section 40(6) liberal professions expense deduction.\n *\n * Math: Medical 60%, law/engineering/architecture/accounting/fine arts/other 30%, or actual.\n * Units: THB major units.\n *\n * @statute Thailand Royal Decree No. 11 Section 6.\n */\nexport function calculateExpenses40_6(input: ThSection40_6Input | undefined): ClampedAmount {\n\tif (!input) {\n\t\treturn { requested: 0, allowed: 0, cap: 0, wasClamped: false }\n\t}\n\tconst rates = TH_2026_PARAMETERS.expenses.section40_6\n\tconst med = calculateElectableItemExpense(\n\t\tinput.medical,\n\t\trates.medical,\n\t\tThExpenseField.Section40_6_Medical,\n\t)\n\tconst law = calculateElectableItemExpense(input.law, rates.other, ThExpenseField.Section40_6_Law)\n\tconst eng = calculateElectableItemExpense(\n\t\tinput.engineering,\n\t\trates.other,\n\t\tThExpenseField.Section40_6_Engineering,\n\t)\n\tconst arch = calculateElectableItemExpense(\n\t\tinput.architecture,\n\t\trates.other,\n\t\tThExpenseField.Section40_6_Architecture,\n\t)\n\tconst acc = calculateElectableItemExpense(\n\t\tinput.accounting,\n\t\trates.other,\n\t\tThExpenseField.Section40_6_Accounting,\n\t)\n\tconst art = calculateElectableItemExpense(\n\t\tinput.fineArts,\n\t\trates.other,\n\t\tThExpenseField.Section40_6_FineArts,\n\t)\n\tconst oth = calculateElectableItemExpense(\n\t\tinput.other,\n\t\trates.other,\n\t\tThExpenseField.Section40_6_Other,\n\t)\n\n\tconst requested =\n\t\tmed.requested +\n\t\tlaw.requested +\n\t\teng.requested +\n\t\tarch.requested +\n\t\tacc.requested +\n\t\tart.requested +\n\t\toth.requested\n\tconst allowed =\n\t\tmed.allowed + law.allowed + eng.allowed + arch.allowed + acc.allowed + art.allowed + oth.allowed\n\tconst cap = med.cap + law.cap + eng.cap + arch.cap + acc.cap + art.cap + oth.cap\n\n\treturn {\n\t\trequested,\n\t\tallowed,\n\t\tcap,\n\t\twasClamped: requested > allowed,\n\t}\n}\n","import type { ClampedAmount } from \"../../core/caps\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport { type ThExpenseElectableIncome, ThExpenseField } from \"../types\"\nimport { calculateElectableItemExpense } from \"./electable-expense\"\n\n/**\n * Calculates Section 40(7) contracting expense deduction.\n *\n * Math: Standard 60% or actual expenses capped at income.\n * Units: THB major units.\n *\n * @statute Thailand Royal Decree No. 11 Section 7.\n */\nexport function calculateExpenses40_7(\n\tinput: number | ThExpenseElectableIncome | undefined,\n): ClampedAmount {\n\treturn calculateElectableItemExpense(\n\t\tinput,\n\t\tTH_2026_PARAMETERS.expenses.section40_7.rate,\n\t\tThExpenseField.Section40_7,\n\t)\n}\n","import { type ClampedAmount, clampCap } from \"../../core/caps\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport { type ThExpenseElectableIncome, ThExpenseField, type ThSection40_8Input } from \"../types\"\nimport { calculateElectableItemExpense } from \"./electable-expense\"\n\nfunction calculateEntertainerExpense(\n\titem: number | ThExpenseElectableIncome | undefined,\n): ClampedAmount {\n\tif (!item) {\n\t\treturn { requested: 0, allowed: 0, cap: 0, wasClamped: false }\n\t}\n\tconst amount = typeof item === \"number\" ? item : item.amount\n\tif (amount <= 0) {\n\t\treturn { requested: 0, allowed: 0, cap: 0, wasClamped: false }\n\t}\n\tif (typeof item === \"object\" && item.expenseElection === \"actual\") {\n\t\treturn clampCap(item.actualExpense ?? 0, amount, ThExpenseField.Section40_8_Entertainer)\n\t}\n\tconst p = TH_2026_PARAMETERS.expenses.section40_8\n\tconst tier1 = Math.min(amount, p.entertainerFirstTierThreshold)\n\tconst tier2 = Math.max(0, amount - p.entertainerFirstTierThreshold)\n\tconst computed =\n\t\tMath.floor(tier1 * p.entertainerFirstTierRate) + Math.floor(tier2 * p.entertainerSecondTierRate)\n\treturn clampCap(computed, p.entertainerCeiling, ThExpenseField.Section40_8_Entertainer)\n}\n\n/**\n * Calculates Section 40(8) business, commerce, and entertainer expense deduction.\n *\n * Math: General 60% or actual; entertainer tiered 60%/40% up to 600,000 THB or actual.\n * Units: THB major units.\n *\n * @statute Thailand Royal Decree No. 11 Section 8.\n */\nexport function calculateExpenses40_8(\n\tinput: number | ThExpenseElectableIncome | ThSection40_8Input | undefined,\n): ClampedAmount {\n\tif (!input) {\n\t\treturn { requested: 0, allowed: 0, cap: 0, wasClamped: false }\n\t}\n\tif (typeof input === \"number\" || \"amount\" in input) {\n\t\treturn calculateElectableItemExpense(\n\t\t\tinput as number | ThExpenseElectableIncome,\n\t\t\tTH_2026_PARAMETERS.expenses.section40_8.generalRate,\n\t\t\tThExpenseField.Section40_8_General,\n\t\t)\n\t}\n\tconst gen = calculateElectableItemExpense(\n\t\tinput.general,\n\t\tTH_2026_PARAMETERS.expenses.section40_8.generalRate,\n\t\tThExpenseField.Section40_8_General,\n\t)\n\tconst ent = calculateEntertainerExpense(input.entertainer)\n\tconst requested = gen.requested + ent.requested\n\tconst allowed = gen.allowed + ent.allowed\n\tconst cap = gen.cap + ent.cap\n\treturn {\n\t\trequested,\n\t\tallowed,\n\t\tcap,\n\t\twasClamped: requested > allowed,\n\t}\n}\n","import type {\n\tThAssessableIncomeBreakdown,\n\tThAssessableIncomeInput,\n\tThExpenseDeductionsBreakdown,\n} from \"../types\"\nimport { calculateExpenses40_1_2 } from \"./expenses-40-1-2\"\nimport { calculateExpenses40_3 } from \"./expenses-40-3\"\nimport { calculateExpenses40_4 } from \"./expenses-40-4\"\nimport { calculateExpenses40_5 } from \"./expenses-40-5\"\nimport { calculateExpenses40_6 } from \"./expenses-40-6\"\nimport { calculateExpenses40_7 } from \"./expenses-40-7\"\nimport { calculateExpenses40_8 } from \"./expenses-40-8\"\n\n/**\n * Aggregates all Section 40(1)–40(8) statutory expense deductions.\n *\n * @statute Thailand Revenue Code Sections 42 bis, 42 ter, 43–46; Royal Decree No. 11.\n */\nexport function calculateExpenseDeductions(\n\tincome: ThAssessableIncomeInput = {},\n\tassessable: ThAssessableIncomeBreakdown,\n): ThExpenseDeductionsBreakdown {\n\tconst e1_2 = calculateExpenses40_1_2(assessable.section40_1, assessable.section40_2)\n\tconst e3 = calculateExpenses40_3(income.section40_3, assessable.section40_3)\n\tconst e4 = calculateExpenses40_4(assessable.section40_4)\n\tconst e5 = calculateExpenses40_5(income.section40_5)\n\tconst e6 = calculateExpenses40_6(income.section40_6)\n\tconst e7 = calculateExpenses40_7(income.section40_7)\n\tconst e8 = calculateExpenses40_8(income.section40_8)\n\n\tconst total =\n\t\te1_2.allowed + e3.allowed + e4.allowed + e5.allowed + e6.allowed + e7.allowed + e8.allowed\n\n\treturn {\n\t\tsection40_1_2: e1_2,\n\t\tsection40_3: e3,\n\t\tsection40_4: e4,\n\t\tsection40_5: e5,\n\t\tsection40_6: e6,\n\t\tsection40_7: e7,\n\t\tsection40_8: e8,\n\t\ttotal,\n\t}\n}\n","import { applyPercentage, fromMinorUnits, toMinorUnits } from \"../../core/money\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport { type ThMinimumGrossTaxBreakdown, ThTaxField } from \"../types\"\n\n/**\n * Calculates Section 48(2) Minimum Gross Tax on Section 40(2)–40(8) income.\n *\n * Math: 0.5% of gross Section 40(2)–(8) if gross >= 120,000 THB; payable only if the 0.5% tax exceeds 5,000 THB\n * and exceeds the progressive tax.\n * Units: THB major units.\n *\n * @statute Thailand Revenue Code Section 48(2).\n */\nexport function calculateMinimumGrossTax(\n\tgrossNonEmploymentIncome: number,\n\tprogressiveTax: number,\n): ThMinimumGrossTaxBreakdown {\n\tconst p = TH_2026_PARAMETERS.minimumGrossTax\n\tif (grossNonEmploymentIncome < p.incomeThreshold) {\n\t\treturn {\n\t\t\tgrossEligibleIncome: grossNonEmploymentIncome,\n\t\t\trate: p.rate,\n\t\t\tcalculatedTax: 0,\n\t\t\tthreshold: p.taxThreshold,\n\t\t\tisApplicable: false,\n\t\t\twasApplied: false,\n\t\t}\n\t}\n\n\tconst currency = TH_2026_PARAMETERS.currency\n\tconst minorIncome = toMinorUnits(grossNonEmploymentIncome, currency, ThTaxField.MinimumGrossTax)\n\tconst minorTax = applyPercentage(minorIncome, p.rate)\n\tconst calculatedTax = fromMinorUnits(minorTax, currency)\n\n\tconst isApplicable = calculatedTax > p.taxThreshold\n\tconst wasApplied = isApplicable && calculatedTax > progressiveTax\n\n\treturn {\n\t\tgrossEligibleIncome: grossNonEmploymentIncome,\n\t\trate: p.rate,\n\t\tcalculatedTax,\n\t\tthreshold: p.taxThreshold,\n\t\tisApplicable,\n\t\twasApplied,\n\t}\n}\n","import { TaxValidationError } from \"../core/money\"\nimport { ThSpecialRegimeField } from \"./types\"\n\n/**\n * Validates a Thai corporate income tax rate used for the Section 47 bis gross-up.\n *\n * Purpose: Keeps `rate / (1 - rate)` finite before any stage uses the rate.\n * Math: Requires `0 <= rate < 1`.\n * Units: Rate ratio (0.2 = 20%).\n * Throws: TaxValidationError if rate is NaN, non-finite, negative, or at least 1.\n *\n * @param rate Corporate income tax rate ratio.\n * @statute Thailand Revenue Code Section 47 bis.\n */\nexport function validateCorpTaxRate(rate: number): void {\n\tif (!Number.isFinite(rate) || rate < 0 || rate >= 1) {\n\t\tthrow new TaxValidationError(\n\t\t\tThSpecialRegimeField.CorpTaxRate,\n\t\t\trate,\n\t\t\t\"Corporate tax rate must be finite and satisfy 0 <= rate < 1\",\n\t\t)\n\t}\n}\n\n/**\n * Resolves an optional typed choice (election, payment type, acquisition type) at runtime.\n *\n * Purpose: TypeScript unions are erased at runtime, so untyped callers could otherwise pass an\n * unknown string and silently get the default behaviour.\n * Math: Returns `fallback` when `value` is `undefined`; otherwise requires membership in `choices`.\n * Units: Dimensionless string enum.\n * Throws: TaxValidationError (reported against `field`) if `value` is not one of `choices`.\n *\n * @param value Caller-supplied value, possibly outside the declared union.\n * @param choices Typed const object whose values are the permitted choices.\n * @param fallback Choice used when `value` is `undefined`.\n * @param field Diagnostic field path reported on failure.\n * @statute Jurisdiction-neutral input bound (AGENTS rule 8); no statutory citation.\n */\nexport function resolveChoice<T extends string>(\n\tvalue: string | undefined,\n\tchoices: Readonly<Record<string, T>>,\n\tfallback: T,\n\tfield: ThSpecialRegimeField,\n): T {\n\tif (value === undefined) return fallback\n\tconst match = Object.values(choices).find((choice) => choice === value)\n\tif (match === undefined) {\n\t\tthrow new TaxValidationError(field, value, \"Value is not a supported choice\")\n\t}\n\treturn match\n}\n","import { applyGrossUp, validateMonetaryAmount } from \"../../core/money\"\nimport { ThCombineField, type ThDividendTaxCreditBreakdown, ThSpecialRegimeField } from \"../types\"\nimport { fromSatang, toSatang } from \"../units\"\nimport { validateCorpTaxRate } from \"../validate\"\n\n/**\n * Calculates the Section 47 bis dividend tax credit for dividends included in regular income.\n *\n * Purpose: Recovers the corporate tax already paid on dividends taxed again in the regular schedule.\n * Math: credit = dividend * corpTaxRate / (1 - corpTaxRate), exact in satang and rounded half-up.\n * The grossed-up amount (dividend + credit) is assessable; the credit offsets tax payable.\n * Units: Inputs and outputs in THB major units; arithmetic in integer satang.\n * Throws: TaxValidationError if dividend is negative/non-finite or the rate is invalid.\n *\n * @param includedDividends Dividends elected `\"include\"`, in THB.\n * @param corpTaxRate Corporate tax rate in [0, 1); `undefined` means no credit.\n * @statute Thailand Revenue Code Section 47 bis.\n */\nexport function calculateDividendTaxCredit(\n\tincludedDividends: number,\n\tcorpTaxRate: number | undefined,\n): ThDividendTaxCreditBreakdown {\n\tvalidateMonetaryAmount(includedDividends, ThSpecialRegimeField.Dividends)\n\tif (corpTaxRate === undefined) {\n\t\treturn {\n\t\t\tisApplicable: false,\n\t\t\tdividendAmount: includedDividends,\n\t\t\tcorpTaxRate: 0,\n\t\t\tcredit: 0,\n\t\t\tgrossedUpAmount: includedDividends,\n\t\t}\n\t}\n\tvalidateCorpTaxRate(corpTaxRate)\n\tconst dividendSatang = toSatang(includedDividends, ThSpecialRegimeField.Dividends)\n\tconst creditSatang = applyGrossUp(dividendSatang, corpTaxRate, ThSpecialRegimeField.Dividends)\n\treturn {\n\t\tisApplicable: true,\n\t\tdividendAmount: includedDividends,\n\t\tcorpTaxRate,\n\t\tcredit: fromSatang(creditSatang, ThCombineField.DividendTaxCredit),\n\t\tgrossedUpAmount: fromSatang(dividendSatang + creditSatang, ThCombineField.DividendTaxCredit),\n\t}\n}\n","import { applyPercentage } from \"../../core/money\"\nimport {\n\tThFinalWithholdingElection,\n\ttype ThFinalWithholdingSourceBreakdown,\n\ttype ThSection40_4Dividend,\n\ttype ThSection40_4Source,\n\ttype ThSpecialRegimeField,\n} from \"../types\"\nimport { fromSatang, toSatang } from \"../units\"\nimport { resolveChoice } from \"../validate\"\nimport { extractElectableAmount } from \"./assessable\"\n\nexport type ThSourceInput = number | ThSection40_4Source | ThSection40_4Dividend | undefined\n\n/** One Section 40(4) source after its election is applied. Units: integer satang. */\nexport interface ResolvedSource {\n\treadonly breakdown: ThFinalWithholdingSourceBreakdown\n\treadonly satang: number\n\treadonly taxSatang: number\n}\n\nfunction electionOf(input: ThSourceInput, field: ThSpecialRegimeField): ThFinalWithholdingElection {\n\tconst election = typeof input === \"object\" ? input.election : undefined\n\treturn resolveChoice(\n\t\telection,\n\t\tThFinalWithholdingElection,\n\t\tThFinalWithholdingElection.Include,\n\t\tfield,\n\t)\n}\n\n/**\n * Resolves one Section 40(4) source: validates its amount and election, withholds if final.\n *\n * Purpose: Applies the per-source final withholding election.\n * Math: tax = amount * rate (satang, half-up) when the election is `\"final\"`, otherwise 0.\n * Units: Input in THB major units; `satang` and `taxSatang` in integer satang.\n * Throws: TaxValidationError for a negative/non-finite amount or an unknown election value.\n *\n * @param input Bare amount, source object, or undefined.\n * @param field Diagnostic path of the amount.\n * @param electionField Diagnostic path of the election.\n * @param rate Final withholding rate (ratio).\n * @statute Thailand Revenue Code Sections 40(4), 50(2)(g), 57 ter.\n */\nexport function resolveSource(\n\tinput: ThSourceInput,\n\tfield: ThSpecialRegimeField,\n\telectionField: ThSpecialRegimeField,\n\trate: number,\n): ResolvedSource {\n\tconst amount = extractElectableAmount(input, field)\n\tconst election = electionOf(input, electionField)\n\tconst satang = toSatang(amount, field)\n\tconst isFinal = election === ThFinalWithholdingElection.Final\n\tconst taxSatang = isFinal ? applyPercentage(satang, rate) : 0\n\treturn { breakdown: { election, amount, rate, tax: fromSatang(taxSatang) }, satang, taxSatang }\n}\n","import { TH_2026_PARAMETERS } from \"../parameters\"\nimport {\n\tThAssessableField,\n\ttype ThFinalWithholdingBreakdown,\n\tThFinalWithholdingElection,\n\ttype ThSection40_4Input,\n\tThSpecialRegimeField,\n} from \"../types\"\nimport { fromSatang, toSatang } from \"../units\"\nimport { validateCorpTaxRate } from \"../validate\"\nimport { extractElectableAmount } from \"./assessable\"\nimport { type ResolvedSource, resolveSource } from \"./final-withholding-source\"\n\n/**\n * Section 40(4) split into regular-bracket income and final-withheld income.\n * Units: THB major units.\n *\n * @statute Thailand Revenue Code Sections 40(4), 47 bis, 50(2)(g), 57 ter.\n */\nexport interface ThResolvedSection40_4 {\n\t/** Section 40(4) income included in regular brackets, before any dividend gross-up. */\n\treadonly includedAmount: number\n\t/** Dividends elected `\"include\"`. */\n\treadonly includedDividends: number\n\t/** Corporate tax rate (ratio) for included dividends, if given. */\n\treadonly corpTaxRate: number | undefined\n\t/** Audit trail of the sources elected `\"final\"` and their withheld tax. */\n\treadonly finalWithholding: ThFinalWithholdingBreakdown\n}\n\nfunction toStructuredInput(input: number | ThSection40_4Input | undefined): ThSection40_4Input {\n\treturn typeof input === \"number\" ? { amount: input } : (input ?? {})\n}\n\nfunction sumSatang(sources: readonly ResolvedSource[], seed = 0): number {\n\treturn sources.reduce((sum, source) => sum + source.satang, seed)\n}\n\n/**\n * Splits Section 40(4) income into regular-bracket income and final-withheld income.\n *\n * Purpose: Applies each source's election so only included income reaches the regular schedule.\n * Math: For each source elected `\"final\"`, tax = amount * rate (satang, half-up) and the income is\n * excluded from regular brackets. `\"include\"` (default) adds the income to Section 40(4)\n * assessable income at a 0% expense rate.\n * Units: Inputs and outputs in THB major units; arithmetic in integer satang.\n * Throws: TaxValidationError for negative/non-finite amounts or an invalid `corpTaxRate`.\n *\n * @param input Bare amount, structured sources, or undefined.\n * @statute Thailand Revenue Code Sections 40(4), 50(2)(g), 56, 57 ter.\n */\nexport function resolveSection40_4(\n\tinput: number | ThSection40_4Input | undefined,\n): ThResolvedSection40_4 {\n\tconst section = toStructuredInput(input)\n\tconst rates = TH_2026_PARAMETERS.finalWithholding\n\tconst base = extractElectableAmount(section.amount, ThAssessableField.Section40_4)\n\tconst interest = resolveSource(\n\t\tsection.interest,\n\t\tThSpecialRegimeField.Interest,\n\t\tThSpecialRegimeField.InterestElection,\n\t\trates.interestRate,\n\t)\n\tconst dividends = resolveSource(\n\t\tsection.dividends,\n\t\tThSpecialRegimeField.Dividends,\n\t\tThSpecialRegimeField.DividendsElection,\n\t\trates.dividendRate,\n\t)\n\tconst digital = resolveSource(\n\t\tsection.digitalAssets,\n\t\tThSpecialRegimeField.DigitalAssets,\n\t\tThSpecialRegimeField.DigitalAssetsElection,\n\t\trates.digitalAssetRate,\n\t)\n\tconst corpTaxRate =\n\t\ttypeof section.dividends === \"object\" ? section.dividends.corpTaxRate : undefined\n\tif (corpTaxRate !== undefined) validateCorpTaxRate(corpTaxRate)\n\n\tconst sources = [interest, dividends, digital]\n\tconst isFinal = (source: ResolvedSource): boolean =>\n\t\tsource.breakdown.election === ThFinalWithholdingElection.Final\n\tconst finalSources = sources.filter(isFinal)\n\tconst includedSatang = sumSatang(\n\t\tsources.filter((source) => !isFinal(source)),\n\t\ttoSatang(base, ThAssessableField.Section40_4),\n\t)\n\tconst isDividendIncluded = !isFinal(dividends)\n\treturn {\n\t\tincludedAmount: fromSatang(includedSatang),\n\t\tincludedDividends: isDividendIncluded ? dividends.breakdown.amount : 0,\n\t\tcorpTaxRate: isDividendIncluded ? corpTaxRate : undefined,\n\t\tfinalWithholding: {\n\t\t\tinterest: interest.breakdown,\n\t\t\tdividends: dividends.breakdown,\n\t\t\tdigitalAssets: digital.breakdown,\n\t\t\ttotalAmount: fromSatang(sumSatang(finalSources)),\n\t\t\ttotalTax: fromSatang(finalSources.reduce((sum, source) => sum + source.taxSatang, 0)),\n\t\t},\n\t}\n}\n","import { TH_2026_PARAMETERS } from \"../parameters\"\nimport { ThRealEstateAcquisitionType, ThRealEstateExpenseBasis } from \"../types\"\n\n/**\n * Caps the holding period used by Section 48(4) at the statutory maximum.\n *\n * Purpose: More than 10 years of holding counts as 10 years.\n * Math: min(requestedYears, `realEstate.maxHoldingYears`).\n * Units: Dimensionless year count (callers round part-years up).\n * Throws: Never; `requestedYears` is validated by the caller.\n *\n * @param requestedYears Validated positive safe integer holding period.\n * @statute Thailand Revenue Code Section 48(4); Ministerial Regulation No. 126.\n */\nexport function effectiveHoldingYears(requestedYears: number): number {\n\treturn Math.min(requestedYears, TH_2026_PARAMETERS.realEstate.maxHoldingYears)\n}\n\n/**\n * Selects the Section 48(4) expense rate for the acquisition type.\n *\n * Purpose: Inherited or gifted property takes a flat rate; commercial or other property takes the\n * holding-year table. The two are never combined.\n * Math: Flat `inheritanceExpenseRate`, or the last `expenseTable` row with `minYears <= years`.\n * Units: Rate ratio; `years` is the effective (capped) holding period.\n * Throws: Never; the acquisition type is validated by the caller.\n *\n * @param acquisitionType Validated acquisition type.\n * @param years Effective holding years.\n * @statute Thailand Revenue Code Section 48(4); Ministerial Regulation No. 126.\n */\nexport function resolveRealEstateExpense(\n\tacquisitionType: ThRealEstateAcquisitionType,\n\tyears: number,\n): { readonly basis: ThRealEstateExpenseBasis; readonly rate: number } {\n\tconst rules = TH_2026_PARAMETERS.realEstate\n\tif (acquisitionType === ThRealEstateAcquisitionType.InheritanceOrGift) {\n\t\treturn { basis: ThRealEstateExpenseBasis.FlatInheritance, rate: rules.inheritanceExpenseRate }\n\t}\n\tconst rate = rules.expenseTable.reduce((acc, row) => (years >= row.minYears ? row.rate : acc), 0)\n\treturn { basis: ThRealEstateExpenseBasis.HoldingPeriodTable, rate }\n}\n","import type { TaxBracket } from \"../../core/brackets\"\nimport {\n\tapplyPercentage,\n\tmultiplySafeInteger,\n\troundHalfUp,\n\tvalidateMonetaryAmount,\n\tvalidateSafeIntegerCount,\n} from \"../../core/money\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport {\n\tThRealEstateAcquisitionType,\n\ttype ThRealEstateBreakdown,\n\tThRealEstateExpenseBasis,\n\ttype ThRealEstateTransferInput,\n\tThSpecialRegimeField,\n} from \"../types\"\nimport { fromSatang, toSatang } from \"../units\"\nimport { resolveChoice } from \"../validate\"\nimport { evaluateThBracketSet, evaluateThBrackets } from \"./bracket-eval\"\nimport { effectiveHoldingYears, resolveRealEstateExpense } from \"./real-estate-expense\"\n\nconst EMPTY_BREAKDOWN: ThRealEstateBreakdown = {\n\tgrossTransferValue: 0,\n\tacquisitionType: ThRealEstateAcquisitionType.Commercial,\n\texpenseBasis: ThRealEstateExpenseBasis.HoldingPeriodTable,\n\trequestedHoldingYears: 0,\n\teffectiveHoldingYears: 0,\n\texpenseRate: 0,\n\texpense: 0,\n\tnetGain: 0,\n\tannualGain: 0,\n\tbrackets: evaluateThBrackets(0, 0).brackets,\n\tannualTax: 0,\n\ttaxBeforeCap: 0,\n\ttaxCap: 0,\n\twasClamped: false,\n\ttax: 0,\n}\n\n/**\n * Scales every finite annualised-schedule bracket bound by the holding period.\n *\n * Math: `years * tax(net / years)` equals the tax on `net` against bounds multiplied by `years`\n * (each slice `rate * clip(net/years - lo, 0, width) * years = rate * clip(net - lo*years, 0, width*years)`),\n * so the annualisation needs no intermediate rounding.\n * Units: THB major units; scaling performed in integer satang.\n * Throws: TaxValidationError if a scaled bound exceeds the safe integer range (unreachable\n * for the capped holding period).\n */\nfunction scaleBracketsByYears(years: number): readonly TaxBracket[] {\n\treturn TH_2026_PARAMETERS.realEstate.brackets.map((bracket) => {\n\t\tif (bracket.upTo === null) return bracket\n\t\tconst scaled = multiplySafeInteger(\n\t\t\ttoSatang(bracket.upTo),\n\t\t\tyears,\n\t\t\tThSpecialRegimeField.RealEstateHoldingYears,\n\t\t)\n\t\treturn { upTo: fromSatang(scaled), rate: bracket.rate }\n\t})\n}\n\n/**\n * Clamps real estate tax at the statutory share of the gross transfer value.\n *\n * Purpose: Applies the Section 48(4) ceiling on the separately computed tax.\n * Math: taxCap = grossMinor * `realEstate.taxCapRate` (20%, half-up); tax = min(taxBeforeCap, taxCap).\n * Units: Integer minor units (satang) in and out.\n * Throws: TaxValidationError if `grossMinor` is not a non-negative safe integer.\n * Note: Unreachable end to end (top rate 35% of at most 50% net is 17.5% < 20%); kept as the\n * statutory guard.\n *\n * @param taxBeforeCap Uncapped tax in satang.\n * @param grossMinor Gross transfer value in satang.\n * @statute Thailand Revenue Code Section 48(4).\n */\nexport function applyRealEstateCap(\n\ttaxBeforeCap: number,\n\tgrossMinor: number,\n): { taxCap: number; tax: number; wasClamped: boolean } {\n\tconst taxCap = applyPercentage(grossMinor, TH_2026_PARAMETERS.realEstate.taxCapRate)\n\treturn { taxCap, tax: Math.min(taxBeforeCap, taxCap), wasClamped: taxBeforeCap > taxCap }\n}\n\n/**\n * Calculates Section 48(4) tax on an inherited, gifted, or sold real estate transfer.\n *\n * Purpose: Taxes the gain as if earned evenly over the holding period, then scales back up.\n * `grossTransferValue` must be the official appraised value (ราคาประเมินทุนทรัพย์, Section 49 bis),\n * not the actual sale price. `holdingYears` is a positive safe integer; callers round part-years\n * up, and more than 10 years counts as 10.\n * Math: years = min(holdingYears, 10); expense = gross * 50% for inheritance/gift, otherwise\n * gross * table(years) (never combined); net = gross - expense; tax = years * bracketTax(net / years)\n * on the schedule without a 0% tier (5% from the first baht), evaluated exactly as bracketTax of\n * `net` against bounds multiplied by `years` (no per-year rounding); tax is clamped at 20% of gross\n * (which the 35% top rate on at most 50% net can never reach). `annualGain` and `annualTax` are\n * rounded for display.\n * Units: Inputs and outputs in THB major units; arithmetic in integer satang.\n * Throws: TaxValidationError for a negative/non-finite value, a non-positive/non-safe-integer\n * holding period, or an unknown `acquisitionType`.\n *\n * @statute Thailand Revenue Code Section 48(4), Section 42(5), Section 49 bis.\n */\nexport function calculateRealEstateTransfer(\n\tinput: ThRealEstateTransferInput | undefined,\n): ThRealEstateBreakdown {\n\tif (input === undefined) return EMPTY_BREAKDOWN\n\tvalidateMonetaryAmount(input.grossTransferValue, ThSpecialRegimeField.RealEstateGrossValue)\n\tvalidateSafeIntegerCount(input.holdingYears, ThSpecialRegimeField.RealEstateHoldingYears, 1)\n\tconst acquisitionType = resolveChoice(\n\t\tinput.acquisitionType,\n\t\tThRealEstateAcquisitionType,\n\t\tThRealEstateAcquisitionType.Commercial,\n\t\tThSpecialRegimeField.RealEstateAcquisitionType,\n\t)\n\n\tconst years = effectiveHoldingYears(input.holdingYears)\n\tconst { basis, rate: expenseRate } = resolveRealEstateExpense(acquisitionType, years)\n\tconst gross = toSatang(input.grossTransferValue, ThSpecialRegimeField.RealEstateGrossValue)\n\tconst expense = applyPercentage(gross, expenseRate)\n\tconst netGain = gross - expense\n\tconst evaluated = evaluateThBracketSet(\n\t\tfromSatang(netGain),\n\t\tfromSatang(netGain),\n\t\tscaleBracketsByYears(years),\n\t)\n\tconst taxBeforeCap = toSatang(evaluated.totalTax)\n\tconst capped = applyRealEstateCap(taxBeforeCap, gross)\n\treturn {\n\t\tgrossTransferValue: input.grossTransferValue,\n\t\tacquisitionType,\n\t\texpenseBasis: basis,\n\t\trequestedHoldingYears: input.holdingYears,\n\t\teffectiveHoldingYears: years,\n\t\texpenseRate,\n\t\texpense: fromSatang(expense),\n\t\tnetGain: fromSatang(netGain),\n\t\tannualGain: fromSatang(roundHalfUp(netGain / years)),\n\t\tbrackets: evaluated.brackets,\n\t\tannualTax: fromSatang(roundHalfUp(taxBeforeCap / years)),\n\t\ttaxBeforeCap: fromSatang(taxBeforeCap),\n\t\ttaxCap: fromSatang(capped.taxCap),\n\t\twasClamped: capped.wasClamped,\n\t\ttax: fromSatang(capped.tax),\n\t}\n}\n","import { validateMonetaryAmount } from \"../../core/money\"\nimport { ThAssessableField, type ThAssessableIncomeInput } from \"../types\"\nimport { fromSatang, toSatang } from \"../units\"\n\n/** Special-regime amounts folded into the regular schedule. Units: THB major units. */\nexport interface ThSpecialIncome {\n\t/** Ineligible Section 48(5) lump sum added to Section 40(1). */\n\treadonly lumpSum: number\n\t/** Section 40(4) income elected `\"include\"` (final-withheld sources excluded). */\n\treadonly section40_4: number\n\t/** Section 47 bis dividend tax credit, added as gross-up to Section 40(4). */\n\treadonly dividendCredit: number\n}\n\n/**\n * Assembles the income that flows through the regular progressive schedule.\n *\n * Math: Section 40(1) = declared Section 40(1) + ineligible Section 48(5) lump sum;\n * Section 40(4) = included Section 40(4) income (final-withheld sources excluded)\n * + Section 47 bis gross-up. Other categories pass through untouched.\n * Units: THB major units; the Section 40(1) sum is performed in integer satang.\n * Throws: TaxValidationError if the declared Section 40(1) is negative or non-finite.\n *\n * @param income Declared Section 40 income.\n * @param special Lump sum, included Section 40(4) income, and dividend gross-up.\n * @statute Thailand Revenue Code Sections 40(1), 40(4), 47 bis, 48(5).\n */\nexport function buildRegularIncome(\n\tincome: ThAssessableIncomeInput | undefined,\n\tspecial: ThSpecialIncome,\n): ThAssessableIncomeInput {\n\tconst declared = income?.section40_1 ?? 0\n\tvalidateMonetaryAmount(declared, ThAssessableField.Section40_1)\n\tconst section40_1 =\n\t\ttoSatang(declared, ThAssessableField.Section40_1) +\n\t\ttoSatang(special.lumpSum, ThAssessableField.Section40_1)\n\tconst section40_4 =\n\t\ttoSatang(special.section40_4, ThAssessableField.Section40_4) +\n\t\ttoSatang(special.dividendCredit, ThAssessableField.Section40_4)\n\treturn {\n\t\t...income,\n\t\tsection40_1: fromSatang(section40_1),\n\t\tsection40_4: fromSatang(section40_4),\n\t}\n}\n","import {\n\tapplyPercentage,\n\tmultiplySafeInteger,\n\tvalidateMonetaryAmount,\n\tvalidateSafeIntegerCount,\n} from \"../../core/money\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport {\n\ttype ThSeparateSheetBreakdown,\n\ttype ThSeparateSheetInput,\n\tThSeparateSheetPaymentType,\n\tThSpecialRegimeField,\n} from \"../types\"\nimport { fromSatang, toSatang } from \"../units\"\nimport { resolveChoice } from \"../validate\"\nimport { evaluateThBracketSet } from \"./bracket-eval\"\n\nconst EMPTY_INPUT: ThSeparateSheetInput = { amount: 0, yearsOfService: 0, lastDailyWage: 0 }\n\n/**\n * Calculates the Section 48(5) separate sheet for one-time severance/retirement lump sums.\n *\n * Purpose: Applies the severance exemption, then taxes an eligible remainder on its own schedule\n * or returns the ineligible remainder for regular treatment.\n * Math: For `paymentType` severance (the default) at ANY length of service:\n * exemption = min(amount, 400 * lastDailyWage, 600,000); `\"retirement\"` payments get none.\n * remainder = amount - exemption. With yearsOfService >= 5: fixed = min(7,000 * years, remainder);\n * percentage expense = 50% of (remainder - fixed), half-up; net = remainder - fixed - percentage,\n * taxed on the separate schedule (5%..35%, no 0% tier). With fewer than 5 years only the remainder\n * is returned in `addedToSection40_1` for the regular computation.\n * Units: Inputs and outputs in THB major units; arithmetic in integer satang.\n * Throws: TaxValidationError for negative/non-finite money, a non-safe-integer/negative `yearsOfService`,\n * an unknown `paymentType`, or a wage/years value whose scaled satang amount exceeds safe integers.\n *\n * @statute Thailand Revenue Code Section 48(5); Section 42(1) (severance exemption).\n */\nexport function calculateSeparateSheet(\n\tinput: ThSeparateSheetInput = EMPTY_INPUT,\n): ThSeparateSheetBreakdown {\n\tconst rules = TH_2026_PARAMETERS.separateSheet\n\tvalidateMonetaryAmount(input.amount, ThSpecialRegimeField.SeparateSheetAmount)\n\tvalidateMonetaryAmount(input.lastDailyWage, ThSpecialRegimeField.SeparateSheetLastDailyWage)\n\tvalidateSafeIntegerCount(input.yearsOfService, ThSpecialRegimeField.SeparateSheetYears)\n\tconst paymentType = resolveChoice(\n\t\tinput.paymentType,\n\t\tThSeparateSheetPaymentType,\n\t\tThSeparateSheetPaymentType.Severance,\n\t\tThSpecialRegimeField.SeparateSheetPaymentType,\n\t)\n\n\tconst years = input.yearsOfService\n\tconst isEligible = years >= rules.minYearsOfService\n\tconst amountSatang = toSatang(input.amount, ThSpecialRegimeField.SeparateSheetAmount)\n\tconst wageSatang = toSatang(input.lastDailyWage, ThSpecialRegimeField.SeparateSheetLastDailyWage)\n\tconst isSeverance = paymentType === ThSeparateSheetPaymentType.Severance\n\tconst wageCap = isSeverance\n\t\t? multiplySafeInteger(\n\t\t\t\twageSatang,\n\t\t\t\trules.exemptionWageDays,\n\t\t\t\tThSpecialRegimeField.SeparateSheetLastDailyWage,\n\t\t\t)\n\t\t: 0\n\tconst exemption = isSeverance\n\t\t? Math.min(amountSatang, wageCap, toSatang(rules.exemptionCeiling))\n\t\t: 0\n\tconst remainder = amountSatang - exemption\n\tconst fixedCap = multiplySafeInteger(\n\t\ttoSatang(rules.expensePerYearOfService),\n\t\tyears,\n\t\tThSpecialRegimeField.SeparateSheetYears,\n\t)\n\tconst fixed = isEligible ? Math.min(remainder, fixedCap) : 0\n\tconst percentage = isEligible ? applyPercentage(remainder - fixed, rules.remainderExpenseRate) : 0\n\tconst net = fromSatang(isEligible ? remainder - fixed - percentage : 0)\n\n\tconst evaluated = evaluateThBracketSet(net, net, rules.brackets)\n\treturn {\n\t\tamount: input.amount,\n\t\tyearsOfService: years,\n\t\tlastDailyWage: input.lastDailyWage,\n\t\tisEligible,\n\t\taddedToSection40_1: isEligible ? 0 : fromSatang(remainder),\n\t\twageBasedCap: fromSatang(wageCap),\n\t\texemptionCeiling: rules.exemptionCeiling,\n\t\tpaymentType,\n\t\texemption: fromSatang(exemption),\n\t\tremainder: fromSatang(remainder),\n\t\tfixedExpense: fromSatang(fixed),\n\t\tpercentageExpense: fromSatang(percentage),\n\t\tnetTaxable: net,\n\t\tbrackets: evaluated.brackets,\n\t\ttax: evaluated.totalTax,\n\t}\n}\n","import {\n\ttype ThAssessableIncomeInput,\n\tThBracketField,\n\ttype ThDividendTaxCreditBreakdown,\n\ttype ThFinalWithholdingBreakdown,\n\ttype ThRealEstateBreakdown,\n\ttype ThSeparateSheetBreakdown,\n\ttype ThTaxInput,\n} from \"../types\"\nimport { sumThAmounts } from \"../units\"\nimport { calculateDividendTaxCredit } from \"./dividend-credit\"\nimport { resolveSection40_4 } from \"./final-withholding\"\nimport { calculateRealEstateTransfer } from \"./real-estate\"\nimport { buildRegularIncome } from \"./regular-income\"\nimport { calculateSeparateSheet } from \"./separate-sheet\"\n\n/** Outcome of every special regime plus the income left for the regular schedule. */\nexport interface ThSpecialRegimes {\n\t/** Income flowing through the regular progressive schedule (Section 40(1)–(8)). */\n\treadonly regularIncome: ThAssessableIncomeInput\n\t/** Gross amounts taxed outside the regular schedule: final 40(4), eligible 48(5), 48(4). */\n\treadonly grossOutsideRegular: number\n\treadonly finalWithholding: ThFinalWithholdingBreakdown\n\treadonly dividendTaxCredit: ThDividendTaxCreditBreakdown\n\treadonly separateSheet: ThSeparateSheetBreakdown\n\treadonly realEstate: ThRealEstateBreakdown\n}\n\n/**\n * Resolves Section 40(4) elections, the Section 47 bis credit, the Section 48(5) separate sheet,\n * and the Section 48(4) real estate transfer, then assembles the remaining regular income.\n *\n * Math: Regular Section 40(4) = included sources + dividend gross-up; ineligible lump sum joins\n * Section 40(1). Gross outside the regular schedule = final-elected 40(4) + eligible lump sum (or, if\n * ineligible, its severance exemption) + real estate gross transfer value.\n * Units: THB major units; sums performed in integer satang.\n * Throws: TaxValidationError for any invalid special-regime input.\n *\n * @statute Thailand Revenue Code Sections 40(4), 47 bis, 48(4), 48(5).\n */\nexport function resolveSpecialRegimes(input: ThTaxInput): ThSpecialRegimes {\n\tconst section40_4 = resolveSection40_4(input.income?.section40_4)\n\tconst dividendTaxCredit = calculateDividendTaxCredit(\n\t\tsection40_4.includedDividends,\n\t\tsection40_4.corpTaxRate,\n\t)\n\tconst separateSheet = calculateSeparateSheet(input.separateSheet)\n\tconst realEstate = calculateRealEstateTransfer(input.realEstateTransfer)\n\treturn {\n\t\tregularIncome: buildRegularIncome(input.income, {\n\t\t\tlumpSum: separateSheet.addedToSection40_1,\n\t\t\tsection40_4: section40_4.includedAmount,\n\t\t\tdividendCredit: dividendTaxCredit.credit,\n\t\t}),\n\t\tgrossOutsideRegular: sumThAmounts(\n\t\t\t[\n\t\t\t\tsection40_4.finalWithholding.totalAmount,\n\t\t\t\tseparateSheet.isEligible ? separateSheet.amount : separateSheet.exemption,\n\t\t\t\trealEstate.grossTransferValue,\n\t\t\t],\n\t\t\tThBracketField.GrossIncome,\n\t\t),\n\t\tfinalWithholding: section40_4.finalWithholding,\n\t\tdividendTaxCredit,\n\t\tseparateSheet,\n\t\trealEstate,\n\t}\n}\n","import { TaxValidationError, thb, validateMonetaryAmount } from \"../../core/money\"\nimport type { TaxResult } from \"../../core/system\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport { ThBracketField, type ThBreakdown, ThTaxField, type ThTaxInput } from \"../types\"\nimport { sumThAmounts } from \"../units\"\nimport { calculateAllowances } from \"./allowances\"\nimport { calculateAssessableIncome } from \"./assessable\"\nimport { evaluateThBrackets } from \"./bracket-eval\"\nimport { combineThLiability } from \"./combine\"\nimport { calculateExpenseDeductions } from \"./expenses\"\nimport { calculateMinimumGrossTax } from \"./minimum-tax\"\nimport { resolveSpecialRegimes } from \"./special-regimes\"\n\nfunction validateYear(year?: number): void {\n\tif (year !== undefined && year !== TH_2026_PARAMETERS.taxYear) {\n\t\tthrow new TaxValidationError(\n\t\t\tThTaxField.TaxYear,\n\t\t\tyear,\n\t\t\t\"Unsupported tax year; only 2026 is currently supported\",\n\t\t)\n\t}\n}\n\n/**\n * Executes full Thailand PIT calculation pipeline for ภ.ง.ด.90/91.\n *\n * @statute Thailand Revenue Code Sections 40, 42 bis, 47, 47 bis, 48(1)–(5).\n */\nexport function runThTaxPipeline(input: ThTaxInput = {}): TaxResult<\"THB\", ThBreakdown> {\n\tvalidateYear(input.taxYear)\n\tconst filingStatus = input.filingStatus ?? \"single\"\n\tconst withholding = input.withholdingTax ?? 0\n\tvalidateMonetaryAmount(withholding, ThTaxField.WithholdingTax)\n\n\tconst special = resolveSpecialRegimes(input)\n\tconst income = special.regularIncome\n\n\tconst assessable = calculateAssessableIncome(income)\n\tconst expenses = calculateExpenseDeductions(income, assessable)\n\tconst netIncomeAfterExpenses = Math.max(0, assessable.total - expenses.total)\n\n\tconst allowancesRes = calculateAllowances(\n\t\tinput.allowances,\n\t\tfilingStatus,\n\t\tassessable.total,\n\t\texpenses.total,\n\t\tassessable.section40_1,\n\t)\n\n\tconst evalBrackets = evaluateThBrackets(allowancesRes.netTaxableIncome, assessable.total)\n\tconst progressiveTax = evalBrackets.totalTax\n\n\tconst minimumGrossTax = calculateMinimumGrossTax(\n\t\tassessable.grossNonEmploymentIncome,\n\t\tprogressiveTax,\n\t)\n\n\tconst regularTax = minimumGrossTax.wasApplied ? minimumGrossTax.calculatedTax : progressiveTax\n\tconst combined = combineThLiability({\n\t\tregularTax,\n\t\tseparateSheetTax: special.separateSheet.tax,\n\t\trealEstateTax: special.realEstate.tax,\n\t\tfinalWithholdingTax: special.finalWithholding.totalTax,\n\t\tdividendTaxCredit: special.dividendTaxCredit.credit,\n\t\twithholdingTax: withholding,\n\t})\n\tconst grossIncome = sumThAmounts(\n\t\t[assessable.total, special.grossOutsideRegular],\n\t\tThBracketField.GrossIncome,\n\t)\n\tconst effectiveRate = grossIncome > 0 ? combined.totalCombinedTax / grossIncome : 0\n\n\tconst breakdown: ThBreakdown = {\n\t\tassessableIncome: assessable,\n\t\texpenseDeductions: expenses,\n\t\tnetIncomeAfterExpenses,\n\t\tallowances: allowancesRes.breakdown,\n\t\tnetTaxableIncome: allowancesRes.netTaxableIncome,\n\t\tprogressiveTax,\n\t\tprogressiveBrackets: evalBrackets.brackets,\n\t\tminimumGrossTax,\n\t\twithholdingTax: withholding,\n\t\tnetTaxPayable: combined.netTaxPayable,\n\t\tregularTax,\n\t\tseparateSheet: special.separateSheet,\n\t\trealEstate: special.realEstate,\n\t\tfinalWithholding: special.finalWithholding,\n\t\tdividendTaxCredit: special.dividendTaxCredit,\n\t\ttotalCombinedTax: combined.totalCombinedTax,\n\t}\n\n\treturn {\n\t\tjurisdiction: \"TH\",\n\t\ttaxYear: TH_2026_PARAMETERS.taxYear,\n\t\tcurrency: TH_2026_PARAMETERS.currency,\n\t\tgrossIncome: thb(grossIncome, ThBracketField.GrossIncome),\n\t\ttaxableIncome: thb(allowancesRes.netTaxableIncome, ThBracketField.TaxableIncome),\n\t\ttotalTax: thb(combined.totalCombinedTax, ThTaxField.TotalTax),\n\t\teffectiveRate,\n\t\tmarginalRate: evalBrackets.brackets.marginalRate,\n\t\tnetTaxPayable: thb(combined.netTaxPayable, ThTaxField.NetTaxPayable, {\n\t\t\tallowNegative: true,\n\t\t}),\n\t\tbreakdown,\n\t}\n}\n","import type { TaxResult } from \"../core/system\"\nimport { runThTaxPipeline } from \"./stages/pipeline\"\nimport type { ThBreakdown, ThTaxInput } from \"./types\"\n\n/**\n * Calculates Thailand Personal Income Tax (ภ.ง.ด.90/91) under Revenue Code.\n *\n * Defaults to tax year 2026 and single filing status.\n *\n * @param input Taxpayer assessable income, allowances, filing status, and withholding.\n * @returns Fully typed, immutable TaxResult with statutory breakdown.\n *\n * @statute Thailand Revenue Code Sections 40, 42 bis, 47, 48(1)–(2).\n */\nexport function calculateThTax(input: ThTaxInput = {}): TaxResult<\"THB\", ThBreakdown> {\n\treturn runThTaxPipeline(input)\n}\n","import type { TaxResult, TaxSystem } from \"../core/system\"\nimport { calculateThTax } from \"./calculate\"\nimport type { ThBreakdown, ThTaxInput } from \"./types\"\n\n/**\n * Thailand Tax System implementation for 2026.\n *\n * Implements TaxSystem contract for `@trajectonomics/tax/th`.\n */\nexport const th: TaxSystem<ThTaxInput, \"THB\", ThBreakdown> = {\n\tjurisdiction: \"TH\",\n\ttaxYear: 2026,\n\tcurrency: \"THB\",\n\tcalculate(input: ThTaxInput = {}): TaxResult<\"THB\", ThBreakdown> {\n\t\treturn calculateThTax(input)\n\t},\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["toMinorUnits","fromMinorUnits","clampCap","clampCap","clampCap","toMinorUnits","evaluateBrackets","fromMinorUnits","clampCap","clampCap","clampCap","clampCap","toMinorUnits","applyPercentage","fromMinorUnits","TaxValidationError","applyGrossUp","applyPercentage","multiplySafeInteger","applyPercentage","roundHalfUp","multiplySafeInteger","applyPercentage","TaxValidationError","thb","rateFraction","multiplySafeInteger","rateFraction","TaxValidationError","rateFraction"],"sources":["../../src/th/parameters-brackets.ts","../../src/th/parameters-special.ts","../../src/th/parameters.ts","../../src/th/types/special-input.ts","../../src/th/units.ts","../../src/th/stages/allowances-donations.ts","../../src/th/stages/allowances-family.ts","../../src/th/stages/allowances-insurance.ts","../../src/th/stages/allowances-retirement.ts","../../src/th/stages/allowances.ts","../../src/th/stages/assessable.ts","../../src/th/stages/bracket-eval.ts","../../src/th/stages/combine.ts","../../src/th/stages/expenses-40-1-2.ts","../../src/th/stages/expenses-40-3.ts","../../src/th/stages/expenses-40-4.ts","../../src/th/stages/electable-expense.ts","../../src/th/stages/expenses-40-5.ts","../../src/th/stages/expenses-40-6.ts","../../src/th/stages/expenses-40-7.ts","../../src/th/stages/expenses-40-8.ts","../../src/th/stages/expenses.ts","../../src/th/stages/minimum-tax.ts","../../src/th/validate.ts","../../src/th/stages/dividend-credit.ts","../../src/th/stages/final-withholding-source.ts","../../src/th/stages/final-withholding.ts","../../src/th/stages/real-estate-expense.ts","../../src/th/stages/real-estate.ts","../../src/th/stages/regular-income.ts","../../src/th/stages/separate-sheet.ts","../../src/th/stages/special-regimes.ts","../../src/th/stages/pipeline.ts","../../src/th/calculate.ts","../../src/th/levy/graph-types.ts","../../src/th/levy/graph-rates.ts","../../src/th/levy/measure-ids.ts","../../src/th/levy/allowances-donations.ts","../../src/th/levy/allowances-fixed.ts","../../src/th/levy/labels.ts","../../src/th/levy/allowances-retirement.ts","../../src/th/levy/allowances.ts","../../src/th/levy/assessable.ts","../../src/th/levy/brackets.ts","../../src/th/levy/expense-item.ts","../../src/th/levy/expenses-business.ts","../../src/th/levy/expenses-contracting.ts","../../src/th/levy/expenses-income.ts","../../src/th/levy/expenses-professions.ts","../../src/th/levy/expenses-rental.ts","../../src/th/levy/expenses.ts","../../src/th/levy/graph.ts","../../src/th/levy/regimes.ts","../../src/th/levy/tallies.ts","../../src/th/levy/emit.ts","../../src/th/system.ts"],"sourcesContent":["import type { TaxBracket } from \"../core/brackets\"\n\n/**\n * Section 48(1) progressive net-income brackets for Tax Year 2026 (THB major units, rates as ratios).\n *\n * @statute Thailand Revenue Code Section 48(1); Royal Decree No. 650.\n */\nexport const TH_2026_BRACKETS: readonly TaxBracket[] = [\n\t{ upTo: 150_000, rate: 0.0 },\n\t{ upTo: 300_000, rate: 0.05 },\n\t{ upTo: 500_000, rate: 0.1 },\n\t{ upTo: 750_000, rate: 0.15 },\n\t{ upTo: 1_000_000, rate: 0.2 },\n\t{ upTo: 2_000_000, rate: 0.25 },\n\t{ upTo: 5_000_000, rate: 0.3 },\n\t{ upTo: null, rate: 0.35 },\n]\n","import { TH_2026_BRACKETS } from \"./parameters-brackets\"\nimport type { ThParameters } from \"./parameters-types\"\n\n/**\n * Section 40(4) final withholding rates for Tax Year 2026.\n *\n * @statute Thailand Revenue Code Sections 40(4), 50(2)(g), 57 ter.\n */\nexport const TH_2026_FINAL_WITHHOLDING: ThParameters[\"finalWithholding\"] = {\n\tinterestRate: 0.15,\n\tdividendRate: 0.1,\n\t// Assumption: the issue gives no digital asset rate; 15% mirrors interest.\n\tdigitalAssetRate: 0.15,\n}\n\n/**\n * Section 48(5) separate sheet parameters for Tax Year 2026: exemption, expenses, and schedule.\n * The schedule is the Section 48(1) ladder without its 0% tier (same upper bounds and rates).\n *\n * @statute Thailand Revenue Code Section 48(5), Section 42(1).\n */\nexport const TH_2026_SEPARATE_SHEET: ThParameters[\"separateSheet\"] = {\n\tminYearsOfService: 5,\n\texemptionWageDays: 400,\n\texemptionCeiling: 600_000,\n\texpensePerYearOfService: 7_000,\n\tremainderExpenseRate: 0.5,\n\tbrackets: TH_2026_BRACKETS.filter((bracket) => bracket.rate > 0),\n}\n\n/**\n * Section 48(4) real estate transfer parameters for Tax Year 2026: the commercial holding-year\n * expense table, the flat inheritance/gift rate, the 10-year holding cap, the annualised schedule\n * (no 0% tier, per the Revenue Department's worked examples), and the 20% tax cap.\n *\n * @statute Thailand Revenue Code Section 48(4), Ministerial Regulation No. 126.\n */\nexport const TH_2026_REAL_ESTATE: ThParameters[\"realEstate\"] = {\n\texpenseTable: [\n\t\t{ minYears: 1, rate: 0.92 },\n\t\t{ minYears: 2, rate: 0.84 },\n\t\t{ minYears: 3, rate: 0.77 },\n\t\t{ minYears: 4, rate: 0.71 },\n\t\t{ minYears: 5, rate: 0.65 },\n\t\t{ minYears: 6, rate: 0.6 },\n\t\t{ minYears: 7, rate: 0.55 },\n\t\t{ minYears: 8, rate: 0.5 },\n\t],\n\tinheritanceExpenseRate: 0.5,\n\tmaxHoldingYears: 10,\n\tbrackets: TH_2026_SEPARATE_SHEET.brackets,\n\ttaxCapRate: 0.2,\n}\n","import { TH_2026_BRACKETS } from \"./parameters-brackets\"\nimport {\n\tTH_2026_FINAL_WITHHOLDING,\n\tTH_2026_REAL_ESTATE,\n\tTH_2026_SEPARATE_SHEET,\n} from \"./parameters-special\"\nimport type { ThParameters } from \"./parameters-types\"\n\n/**\n * Thailand Statutory Tax Parameters for Tax Year 2026 (Revenue Code ภ.ง.ด.90/91).\n *\n * @statute Thailand Revenue Code Sections 40, 42 bis, 47, 47 bis, 48(1)–(5); Royal Decrees No. 11, 650.\n */\nexport const TH_2026_PARAMETERS: ThParameters = {\n\ttaxYear: 2026,\n\tcurrency: \"THB\",\n\texpenses: {\n\t\tsection40_1_2: { rate: 0.5, ceiling: 100_000 },\n\t\tsection40_3: { rate: 0.5, ceiling: 100_000 },\n\t\tsection40_4: { rate: 0.0 },\n\t\tsection40_5: {\n\t\t\tbuilding: 0.3,\n\t\t\tagriculturalLand: 0.2,\n\t\t\tnonAgriculturalLand: 0.15,\n\t\t\tvehicles: 0.3,\n\t\t\tother: 0.1,\n\t\t},\n\t\tsection40_6: {\n\t\t\tmedical: 0.6,\n\t\t\tother: 0.3,\n\t\t},\n\t\tsection40_7: { rate: 0.6 },\n\t\tsection40_8: {\n\t\t\tgeneralRate: 0.6,\n\t\t\tentertainerFirstTierRate: 0.6,\n\t\t\tentertainerFirstTierThreshold: 300_000,\n\t\t\tentertainerSecondTierRate: 0.4,\n\t\t\tentertainerCeiling: 600_000,\n\t\t},\n\t},\n\tallowances: {\n\t\tpersonal: 60_000,\n\t\tspouse: 60_000,\n\t\tchild: {\n\t\t\tbasePerChild: 30_000,\n\t\t\tbonusSecondPlusBorn2018OrLater: 30_000,\n\t\t\tbonusCutoffYear: 2018,\n\t\t},\n\t\tparent: {\n\t\t\tperPerson: 30_000,\n\t\t\tmaxCount: 4,\n\t\t},\n\t\tdisabledPerPerson: 60_000,\n\t\tsocialSecurityFundCeiling: 10_500,\n\t\tlifeInsurance: {\n\t\t\tcombinedCeiling: 100_000,\n\t\t\thealthCeiling: 25_000,\n\t\t},\n\t\tparentHealthInsuranceCeiling: 15_000,\n\t\tspouseLifeInsuranceCeiling: 10_000,\n\t\tmortgageInterestCeiling: 100_000,\n\t\tretirementCluster: {\n\t\t\tcombinedCeiling: 500_000,\n\t\t\trmf: { maxRate: 0.3, ceiling: 500_000 },\n\t\t\tssf: { maxRate: 0.3, ceiling: 200_000 },\n\t\t\tpvd: { maxRate: 0.15, ceiling: 500_000 },\n\t\t\tgpf: { maxRate: 0.3, ceiling: 500_000 },\n\t\t\tnsf: { ceiling: 30_000 },\n\t\t\tpensionLifeInsurance: { maxRate: 0.15, ceiling: 200_000 },\n\t\t},\n\t\tthaiEsg: { maxRate: 0.3, ceiling: 300_000 },\n\t\tdonations: {\n\t\t\tdonation2xMultiplier: 2,\n\t\t\tdonation2xMaxRate: 0.1,\n\t\t\tdonation1xMaxRate: 0.1,\n\t\t},\n\t},\n\tbrackets: TH_2026_BRACKETS,\n\tminimumGrossTax: {\n\t\trate: 0.005,\n\t\tincomeThreshold: 120_000,\n\t\ttaxThreshold: 5_000,\n\t},\n\tfinalWithholding: TH_2026_FINAL_WITHHOLDING,\n\tseparateSheet: TH_2026_SEPARATE_SHEET,\n\trealEstate: TH_2026_REAL_ESTATE,\n}\n","/**\n * Election values for a Section 40(4) source: keep source withholding as final tax, or include\n * the income in the regular progressive computation. Plain `\"final\"`/`\"include\"` strings are\n * accepted wherever {@link ThFinalWithholdingElection} is expected.\n *\n * @statute Thailand Revenue Code Sections 40(4), 50(2)(g), 57 ter.\n */\nexport const ThFinalWithholdingElection = {\n\tFinal: \"final\",\n\tInclude: \"include\",\n} as const\n\n/** Union of {@link ThFinalWithholdingElection} values. */\nexport type ThFinalWithholdingElection =\n\t(typeof ThFinalWithholdingElection)[keyof typeof ThFinalWithholdingElection]\n\n/**\n * Section 40(4) interest or digital asset income with an optional final withholding election.\n * Units: THB major units.\n *\n * @statute Thailand Revenue Code Sections 40(4), 50(2)(g), 57 ter.\n */\nexport interface ThSection40_4Source {\n\t/** Gross amount received, in THB (non-negative, finite). */\n\treadonly amount: number\n\t/** Defaults to `\"include\"`. */\n\treadonly election?: ThFinalWithholdingElection\n}\n\n/**\n * Section 40(4) dividend income with election and optional Thai corporate tax rate.\n * Units: THB major units; `corpTaxRate` is a ratio.\n *\n * @statute Thailand Revenue Code Sections 40(4), 47 bis.\n */\nexport interface ThSection40_4Dividend extends ThSection40_4Source {\n\t/** Corporate income tax rate in [0, 1) paid by the distributing company (Section 47 bis). */\n\treadonly corpTaxRate?: number\n}\n\n/**\n * Structured Section 40(4) input splitting interest, dividends, and digital assets.\n * Units: THB major units.\n *\n * @statute Thailand Revenue Code Section 40(4).\n */\nexport interface ThSection40_4Input {\n\t/** Other Section 40(4) income always included in regular brackets. */\n\treadonly amount?: number\n\t/** Interest; a bare number means included in regular brackets. */\n\treadonly interest?: number | ThSection40_4Source\n\t/** Dividends; a bare number means included, with no Section 47 bis credit. */\n\treadonly dividends?: number | ThSection40_4Dividend\n\t/** Digital asset income; a bare number means included in regular brackets. */\n\treadonly digitalAssets?: number | ThSection40_4Source\n}\n\n/**\n * Kind of Section 48(5) lump sum. Only labour-law `\"severance\"` carries the 400-day / 600,000 THB\n * exemption; other `\"retirement\"` payments (e.g. a pension fund lump sum) get none.\n * Plain `\"severance\"`/`\"retirement\"` strings are accepted wherever\n * {@link ThSeparateSheetPaymentType} is expected.\n *\n * @statute Thailand Revenue Code Section 48(5); Section 42(1) (severance exemption).\n */\nexport const ThSeparateSheetPaymentType = {\n\tSeverance: \"severance\",\n\tRetirement: \"retirement\",\n} as const\n\n/** Union of {@link ThSeparateSheetPaymentType} values. */\nexport type ThSeparateSheetPaymentType =\n\t(typeof ThSeparateSheetPaymentType)[keyof typeof ThSeparateSheetPaymentType]\n\n/**\n * Section 48(5) one-time severance or retirement lump sum.\n * Units: THB major units; `yearsOfService` is a count.\n *\n * @statute Thailand Revenue Code Section 48(5), Section 42(1).\n */\nexport interface ThSeparateSheetInput {\n\t/** Lump sum received, in THB. */\n\treadonly amount: number\n\t/** Completed years of service (non-negative safe integer; callers round part-years up). */\n\treadonly yearsOfService: number\n\t/** Last daily wage in THB, basis for the 400-day exemption. */\n\treadonly lastDailyWage: number\n\t/** Defaults to `\"severance\"`; `\"retirement\"` payments receive no exemption. */\n\treadonly paymentType?: ThSeparateSheetPaymentType\n}\n\n/**\n * How a Section 48(4) property was acquired. Inherited or gifted property takes a flat 50%\n * expense; commercially acquired (or other) property takes the holding-year table. The two are\n * never combined. Plain `\"commercial\"`/`\"inheritance_or_gift\"` strings are accepted wherever\n * {@link ThRealEstateAcquisitionType} is expected.\n *\n * @statute Thailand Revenue Code Section 48(4); Ministerial Regulation No. 126.\n */\nexport const ThRealEstateAcquisitionType = {\n\tCommercial: \"commercial\",\n\tInheritanceOrGift: \"inheritance_or_gift\",\n} as const\n\n/** Union of {@link ThRealEstateAcquisitionType} values. */\nexport type ThRealEstateAcquisitionType =\n\t(typeof ThRealEstateAcquisitionType)[keyof typeof ThRealEstateAcquisitionType]\n\n/**\n * Which expense deduction a Section 48(4) computation used, reported in the breakdown.\n *\n * @statute Thailand Revenue Code Section 48(4); Ministerial Regulation No. 126.\n */\nexport const ThRealEstateExpenseBasis = {\n\tHoldingPeriodTable: \"holding_period_table\",\n\tFlatInheritance: \"flat_inheritance\",\n} as const\n\n/** Union of {@link ThRealEstateExpenseBasis} values. */\nexport type ThRealEstateExpenseBasis =\n\t(typeof ThRealEstateExpenseBasis)[keyof typeof ThRealEstateExpenseBasis]\n\n/**\n * Section 48(4) real estate transfer subject to separate computation.\n * Units: THB major units; `holdingYears` is a count.\n *\n * @statute Thailand Revenue Code Section 48(4).\n */\nexport interface ThRealEstateTransferInput {\n\t/**\n\t * Official appraised value in THB (ราคาประเมินทุนทรัพย์, Section 49 bis), not the actual sale\n\t * price.\n\t */\n\treadonly grossTransferValue: number\n\t/**\n\t * Years the property was held (positive safe integer). Callers round part-years up; the\n\t * calculation counts at most 10 years.\n\t */\n\treadonly holdingYears: number\n\t/** Defaults to `\"commercial\"`; `\"inheritance_or_gift\"` uses a flat 50% expense. */\n\treadonly acquisitionType?: ThRealEstateAcquisitionType\n}\n","import { fromMinorUnits, toMinorUnits } from \"../core/money\"\nimport { TH_2026_PARAMETERS } from \"./parameters\"\n\n/**\n * Converts a THB major-unit amount to integer satang.\n *\n * Purpose: Single THB-bound entry point so stages never repeat the currency threading.\n * Math: Decimal-exact conversion via `toMinorUnits` (half away from zero beyond 2 decimals).\n * Units: THB major units in, integer satang out.\n * Throws: TaxValidationError if the amount is negative (unless allowed), non-finite, or unsafe.\n *\n * @param amount Amount in THB.\n * @param field Diagnostic field name reported on failure.\n * @statute Thailand Revenue Code Section 40 (amounts expressed in baht).\n */\nexport function toSatang(amount: number, field?: string): number {\n\treturn toMinorUnits(amount, TH_2026_PARAMETERS.currency, field)\n}\n\n/**\n * Converts integer satang back to THB major units.\n *\n * Purpose: Counterpart of {@link toSatang}.\n * Math: `satang / 100`, fixed to two decimals.\n * Units: Integer satang in, THB major units out.\n * Throws: TaxValidationError if the input is not a safe integer (or is negative without\n * `allowNegative`).\n *\n * @param satang Integer satang.\n * @param field Diagnostic field name reported on failure.\n * @param options Set `allowNegative` for refund balances.\n * @statute Thailand Revenue Code Section 40 (amounts expressed in baht).\n */\nexport function fromSatang(\n\tsatang: number,\n\tfield?: string,\n\toptions?: { readonly allowNegative?: boolean },\n): number {\n\treturn fromMinorUnits(satang, TH_2026_PARAMETERS.currency, field, options)\n}\n\n/**\n * Sums non-negative THB amounts exactly in integer satang.\n *\n * Purpose: Avoids binary float drift when aggregating gross amounts.\n * Math: Converts each amount to satang, adds, converts back to major units.\n * Units: THB major units in and out.\n * Throws: TaxValidationError if any amount is negative or non-finite, or the sum is not a safe integer.\n *\n * @param values Amounts in THB.\n * @param field Diagnostic field name reported on failure.\n * @statute Thailand Revenue Code Section 40 (gross income aggregation).\n */\nexport function sumThAmounts(values: readonly number[], field?: string): number {\n\treturn fromSatang(\n\t\tvalues.reduce((sum, value) => sum + toSatang(value, field), 0),\n\t\tfield,\n\t)\n}\n","import type { ClampedAmount } from \"../../core/caps\"\nimport { validateMonetaryAmount } from \"../../core/money\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport { ThAllowanceField, type ThDonationsBreakdown } from \"../types\"\n\nexport interface ThDonationAllowancesResult {\n\treadonly breakdown: ThDonationsBreakdown\n\treadonly allowedDonationsTotal: number\n\treadonly netTaxableIncome: number\n}\n\n/**\n * Calculates qualified 2x and general 1x donation allowances.\n *\n * Math: 2x donations capped at 10% of net before donations; 1x donations capped at 10% of remaining net.\n * Units: THB major units.\n *\n * @statute Thailand Revenue Code Section 47(7); Royal Decrees No. 428, 558.\n */\nexport function calculateDonationAllowances(\n\tdonation2x: number | undefined,\n\tdonation1x: number | undefined,\n\tnetBeforeDonations: number,\n): ThDonationAllowancesResult {\n\tconst p = TH_2026_PARAMETERS.allowances.donations\n\tconst net = Math.max(0, netBeforeDonations)\n\n\tconst raw2x = donation2x ?? 0\n\tvalidateMonetaryAmount(raw2x, ThAllowanceField.Donation2x)\n\tconst eligible2x = raw2x * p.donation2xMultiplier\n\tconst cap2x = Math.floor(net * p.donation2xMaxRate)\n\tconst allowed2x = Math.min(eligible2x, cap2x)\n\tconst clamped2x: ClampedAmount = {\n\t\trequested: raw2x,\n\t\tallowed: allowed2x,\n\t\tcap: cap2x,\n\t\twasClamped: eligible2x > cap2x,\n\t}\n\n\tconst remainingNet = Math.max(0, net - allowed2x)\n\tconst raw1x = donation1x ?? 0\n\tvalidateMonetaryAmount(raw1x, ThAllowanceField.Donation1x)\n\tconst cap1x = Math.floor(remainingNet * p.donation1xMaxRate)\n\tconst allowed1x = Math.min(raw1x, cap1x)\n\tconst clamped1x: ClampedAmount = {\n\t\trequested: raw1x,\n\t\tallowed: allowed1x,\n\t\tcap: cap1x,\n\t\twasClamped: raw1x > cap1x,\n\t}\n\n\tconst breakdown: ThDonationsBreakdown = {\n\t\tdonation2x: clamped2x,\n\t\tdonation1x: clamped1x,\n\t\ttotal: allowed2x + allowed1x,\n\t}\n\n\tconst netTaxableIncome = Math.max(0, remainingNet - allowed1x)\n\n\treturn {\n\t\tbreakdown,\n\t\tallowedDonationsTotal: breakdown.total,\n\t\tnetTaxableIncome,\n\t}\n}\n","import { type ClampedAmount, clampCap } from \"../../core/caps\"\nimport { validateMonetaryAmount } from \"../../core/money\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport {\n\tThAllowanceField,\n\ttype ThAllowancesInput,\n\ttype ThChild,\n\ttype ThChildrenInput,\n\ttype ThFilingStatus,\n\ttype ThParentsInput,\n} from \"../types\"\n\nexport interface ThFamilyAllowancesResult {\n\treadonly personal: ClampedAmount\n\treadonly spouse: ClampedAmount\n\treadonly children: ClampedAmount\n\treadonly parents: ClampedAmount\n\treadonly disabled: ClampedAmount\n}\n\nconst unclamped = (n: number): ClampedAmount => ({\n\trequested: n,\n\tallowed: n,\n\tcap: n,\n\twasClamped: false,\n})\n\nfunction countBonusChildren(children: readonly ThChild[], cutoff: number): number {\n\tconst sorted = [...children].sort((a, b) => (a.birthYear ?? 0) - (b.birthYear ?? 0))\n\tlet bonus = 0\n\tfor (let i = 1; i < sorted.length; i++) {\n\t\tif ((sorted[i]?.birthYear ?? 0) >= cutoff) bonus++\n\t}\n\treturn bonus\n}\n\nfunction computeChildAllowance(children: ThAllowancesInput[\"children\"]): ClampedAmount {\n\tif (!children) return unclamped(0)\n\tconst p = TH_2026_PARAMETERS.allowances.child\n\tif (Array.isArray(children)) {\n\t\tconst bonus = countBonusChildren(children, p.bonusCutoffYear)\n\t\treturn unclamped(children.length * p.basePerChild + bonus * p.bonusSecondPlusBorn2018OrLater)\n\t}\n\tconst counts = children as ThChildrenInput\n\tvalidateMonetaryAmount(counts.totalCount ?? 0, ThAllowanceField.Children)\n\tconst count = Math.max(0, Math.floor(counts.totalCount ?? 0))\n\tconst bonusReq = Math.max(0, Math.floor(counts.secondPlusBorn2018OrLaterCount ?? 0))\n\tconst bonus = Math.min(bonusReq, Math.max(0, count - 1))\n\treturn unclamped(count * p.basePerChild + bonus * p.bonusSecondPlusBorn2018OrLater)\n}\n\nfunction computeParentCount(\n\tparents: ThParentsInput | number | undefined,\n\tstatus: ThFilingStatus,\n): number {\n\tif (parents === undefined) return 0\n\tif (typeof parents === \"number\") {\n\t\tvalidateMonetaryAmount(parents, ThAllowanceField.Parents)\n\t\treturn Math.floor(parents)\n\t}\n\tlet count = (parents.taxpayerFather ? 1 : 0) + (parents.taxpayerMother ? 1 : 0)\n\tif (status === \"married_no_income_spouse\") {\n\t\tcount += (parents.spouseFather ? 1 : 0) + (parents.spouseMother ? 1 : 0)\n\t}\n\treturn count\n}\n\n/**\n * Calculates family allowances (personal, spouse, children, parents, disabled).\n *\n * @statute Thailand Revenue Code Section 47(1)(a)–(f).\n */\nexport function calculateFamilyAllowances(\n\tinput: ThAllowancesInput = {},\n\tfilingStatus: ThFilingStatus = \"single\",\n): ThFamilyAllowancesResult {\n\tconst p = TH_2026_PARAMETERS.allowances\n\tconst personalReq = input.personal ?? p.personal\n\tconst personal = clampCap(personalReq, p.personal, ThAllowanceField.Personal)\n\n\tconst isSpouseEligible =\n\t\tfilingStatus === \"married_no_income_spouse\" || filingStatus === \"married_joint\"\n\tconst spouseRequested = input.spouse ?? (isSpouseEligible ? p.spouse : 0)\n\tconst spouse = isSpouseEligible\n\t\t? clampCap(spouseRequested, p.spouse, ThAllowanceField.Spouse)\n\t\t: {\n\t\t\t\trequested: spouseRequested,\n\t\t\t\tallowed: 0,\n\t\t\t\tcap: 0,\n\t\t\t\twasClamped: spouseRequested > 0,\n\t\t\t}\n\n\tconst children = computeChildAllowance(input.children)\n\tconst parentCount = computeParentCount(input.parents, filingStatus)\n\tconst parentsRequested = parentCount * p.parent.perPerson\n\tconst parents = clampCap(\n\t\tparentsRequested,\n\t\tp.parent.perPerson * p.parent.maxCount,\n\t\tThAllowanceField.Parents,\n\t)\n\n\tconst disabledRequested = (input.disabledCareCount ?? 0) * p.disabledPerPerson\n\tvalidateMonetaryAmount(disabledRequested, ThAllowanceField.Disabled)\n\tconst disabled = unclamped(disabledRequested)\n\n\treturn { personal, spouse, children, parents, disabled }\n}\n","import { type ClampedAmount, clampCap } from \"../../core/caps\"\nimport { validateMonetaryAmount } from \"../../core/money\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport { ThAllowanceField, type ThAllowancesInput, type ThFilingStatus } from \"../types\"\n\nexport interface ThInsuranceAllowancesResult {\n\treadonly socialSecurity: ClampedAmount\n\treadonly lifeInsurance: ClampedAmount\n\treadonly parentHealthInsurance: ClampedAmount\n\treadonly spouseLifeInsurance: ClampedAmount\n\treadonly mortgageInterest: ClampedAmount\n}\n\n/**\n * Calculates insurance, social security, and housing mortgage interest allowances.\n *\n * @statute Thailand Revenue Code Section 47(1)(d), (g), (h), (j).\n */\nexport function calculateInsuranceAllowances(\n\tinput: ThAllowancesInput = {},\n\tfilingStatus: ThFilingStatus = \"single\",\n): ThInsuranceAllowancesResult {\n\tconst p = TH_2026_PARAMETERS.allowances\n\n\tconst socialSecurity = clampCap(\n\t\tinput.socialSecurity ?? 0,\n\t\tp.socialSecurityFundCeiling,\n\t\tThAllowanceField.SocialSecurity,\n\t)\n\n\tconst healthClamped = clampCap(\n\t\tinput.healthInsurance ?? 0,\n\t\tp.lifeInsurance.healthCeiling,\n\t\tThAllowanceField.HealthInsurance,\n\t)\n\tconst rawLife = input.lifeInsurance ?? 0\n\tif (input.lifeInsurance !== undefined) {\n\t\tvalidateMonetaryAmount(rawLife, ThAllowanceField.LifeHealthCombined)\n\t}\n\tconst lifeCombinedRequested = rawLife + (input.healthInsurance ?? 0)\n\tconst lifeEffectiveRequested = rawLife + healthClamped.allowed\n\tconst lifeCombined = clampCap(\n\t\tlifeEffectiveRequested,\n\t\tp.lifeInsurance.combinedCeiling,\n\t\tThAllowanceField.LifeHealthCombined,\n\t)\n\tconst lifeInsurance: ClampedAmount = {\n\t\trequested: lifeCombinedRequested,\n\t\tallowed: lifeCombined.allowed,\n\t\tcap: p.lifeInsurance.combinedCeiling,\n\t\twasClamped: lifeCombinedRequested > lifeCombined.allowed,\n\t}\n\n\tconst parentHealthInsurance = clampCap(\n\t\tinput.parentHealthInsurance ?? 0,\n\t\tp.parentHealthInsuranceCeiling,\n\t\tThAllowanceField.ParentHealthInsurance,\n\t)\n\n\tconst isSpouseEligible = filingStatus === \"married_no_income_spouse\"\n\tconst spouseLifeReq = input.spouseLifeInsurance ?? 0\n\tconst spouseLifeInsurance: ClampedAmount = isSpouseEligible\n\t\t? clampCap(spouseLifeReq, p.spouseLifeInsuranceCeiling, ThAllowanceField.SpouseLifeInsurance)\n\t\t: {\n\t\t\t\trequested: spouseLifeReq,\n\t\t\t\tallowed: 0,\n\t\t\t\tcap: 0,\n\t\t\t\twasClamped: spouseLifeReq > 0,\n\t\t\t}\n\n\tconst mortgageInterest = clampCap(\n\t\tinput.mortgageInterest ?? 0,\n\t\tp.mortgageInterestCeiling,\n\t\tThAllowanceField.MortgageInterest,\n\t)\n\n\treturn {\n\t\tsocialSecurity,\n\t\tlifeInsurance,\n\t\tparentHealthInsurance,\n\t\tspouseLifeInsurance,\n\t\tmortgageInterest,\n\t}\n}\n","import { type ClampedAmount, clampCap } from \"../../core/caps\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport {\n\tThAllowanceField,\n\ttype ThAllowancesInput,\n\ttype ThRetirementClusterBreakdown,\n} from \"../types\"\n\nexport interface ThRetirementAllowancesResult {\n\treadonly retirementCluster: ThRetirementClusterBreakdown\n\treadonly thaiEsg: ClampedAmount\n\treadonly allowedClusterTotal: number\n\treadonly allowedThaiEsgTotal: number\n}\n\n/**\n * Calculates Retirement Cluster and ThaiESG allowances.\n *\n * @statute Thailand Revenue Code Section 47; Ministerial Regulations No. 126, 390.\n */\nexport function calculateRetirementAllowances(\n\tinput: ThAllowancesInput = {},\n\tassessableIncome: number,\n\twageBase: number,\n): ThRetirementAllowancesResult {\n\tconst p = TH_2026_PARAMETERS.allowances.retirementCluster\n\tconst currency = TH_2026_PARAMETERS.currency\n\tconst wage = Math.max(wageBase, 0)\n\n\tconst rmf = clampCap(\n\t\tinput.rmf ?? 0,\n\t\t{\n\t\t\tbase: assessableIncome,\n\t\t\trate: p.rmf.maxRate,\n\t\t\tceiling: p.rmf.ceiling,\n\t\t\tcurrency,\n\t\t},\n\t\tThAllowanceField.Rmf,\n\t)\n\tconst ssf = clampCap(\n\t\tinput.ssf ?? 0,\n\t\t{\n\t\t\tbase: assessableIncome,\n\t\t\trate: p.ssf.maxRate,\n\t\t\tceiling: p.ssf.ceiling,\n\t\t\tcurrency,\n\t\t},\n\t\tThAllowanceField.Ssf,\n\t)\n\tconst pvd = clampCap(\n\t\tinput.pvd ?? 0,\n\t\t{ base: wage, rate: p.pvd.maxRate, ceiling: p.pvd.ceiling, currency },\n\t\tThAllowanceField.Pvd,\n\t)\n\tconst gpf = clampCap(\n\t\tinput.gpf ?? 0,\n\t\t{ base: wage, rate: p.gpf.maxRate, ceiling: p.gpf.ceiling, currency },\n\t\tThAllowanceField.Gpf,\n\t)\n\tconst nsf = clampCap(input.nsf ?? 0, p.nsf.ceiling, ThAllowanceField.Nsf)\n\tconst pensionLifeInsurance = clampCap(\n\t\tinput.pensionLifeInsurance ?? 0,\n\t\t{\n\t\t\tbase: assessableIncome,\n\t\t\trate: p.pensionLifeInsurance.maxRate,\n\t\t\tceiling: p.pensionLifeInsurance.ceiling,\n\t\t\tcurrency,\n\t\t},\n\t\tThAllowanceField.PensionLife,\n\t)\n\n\tconst individualSum =\n\t\trmf.allowed +\n\t\tssf.allowed +\n\t\tpvd.allowed +\n\t\tgpf.allowed +\n\t\tnsf.allowed +\n\t\tpensionLifeInsurance.allowed\n\tconst combined = clampCap(individualSum, p.combinedCeiling, ThAllowanceField.RetirementCluster)\n\n\tconst retirementCluster: ThRetirementClusterBreakdown = {\n\t\trmf,\n\t\tssf,\n\t\tpvd,\n\t\tgpf,\n\t\tnsf,\n\t\tpensionLifeInsurance,\n\t\tcombined,\n\t}\n\n\tconst thaiEsgParam = TH_2026_PARAMETERS.allowances.thaiEsg\n\tconst thaiEsg = clampCap(\n\t\tinput.thaiEsg ?? 0,\n\t\t{\n\t\t\tbase: assessableIncome,\n\t\t\trate: thaiEsgParam.maxRate,\n\t\t\tceiling: thaiEsgParam.ceiling,\n\t\t\tcurrency,\n\t\t},\n\t\tThAllowanceField.ThaiEsg,\n\t)\n\n\treturn {\n\t\tretirementCluster,\n\t\tthaiEsg,\n\t\tallowedClusterTotal: combined.allowed,\n\t\tallowedThaiEsgTotal: thaiEsg.allowed,\n\t}\n}\n","import type { ThAllowancesBreakdown, ThAllowancesInput, ThFilingStatus } from \"../types\"\nimport { calculateDonationAllowances } from \"./allowances-donations\"\nimport { calculateFamilyAllowances } from \"./allowances-family\"\nimport { calculateInsuranceAllowances } from \"./allowances-insurance\"\nimport { calculateRetirementAllowances } from \"./allowances-retirement\"\n\nexport interface ThAllowancesResult {\n\treadonly breakdown: ThAllowancesBreakdown\n\treadonly netTaxableIncome: number\n}\n\n/**\n * Aggregates all personal, family, insurance, retirement, and donation allowances.\n *\n * @statute Thailand Revenue Code Section 47.\n */\nexport function calculateAllowances(\n\tinput: ThAllowancesInput = {},\n\tfilingStatus: ThFilingStatus = \"single\",\n\tassessableTotal: number,\n\texpensesTotal: number,\n\twageBase: number,\n): ThAllowancesResult {\n\tconst fam = calculateFamilyAllowances(input, filingStatus)\n\tconst ins = calculateInsuranceAllowances(input, filingStatus)\n\tconst ret = calculateRetirementAllowances(input, assessableTotal, wageBase)\n\n\tconst nonDonationTotal =\n\t\tfam.personal.allowed +\n\t\tfam.spouse.allowed +\n\t\tfam.children.allowed +\n\t\tfam.parents.allowed +\n\t\tfam.disabled.allowed +\n\t\tins.socialSecurity.allowed +\n\t\tins.lifeInsurance.allowed +\n\t\tins.parentHealthInsurance.allowed +\n\t\tins.spouseLifeInsurance.allowed +\n\t\tins.mortgageInterest.allowed +\n\t\tret.allowedClusterTotal +\n\t\tret.allowedThaiEsgTotal\n\n\tconst netBeforeDonations = Math.max(0, assessableTotal - expensesTotal - nonDonationTotal)\n\tconst don = calculateDonationAllowances(input.donation2x, input.donation1x, netBeforeDonations)\n\n\tconst total = nonDonationTotal + don.allowedDonationsTotal\n\n\tconst breakdown: ThAllowancesBreakdown = {\n\t\tpersonal: fam.personal,\n\t\tspouse: fam.spouse,\n\t\tchildren: fam.children,\n\t\tparents: fam.parents,\n\t\tdisabled: fam.disabled,\n\t\tsocialSecurity: ins.socialSecurity,\n\t\tlifeInsurance: ins.lifeInsurance,\n\t\tparentHealthInsurance: ins.parentHealthInsurance,\n\t\tspouseLifeInsurance: ins.spouseLifeInsurance,\n\t\tmortgageInterest: ins.mortgageInterest,\n\t\tretirementCluster: ret.retirementCluster,\n\t\tthaiEsg: ret.thaiEsg,\n\t\tdonations: don.breakdown,\n\t\ttotal,\n\t}\n\n\treturn {\n\t\tbreakdown,\n\t\tnetTaxableIncome: don.netTaxableIncome,\n\t}\n}\n","import { validateMonetaryAmount } from \"../../core/money\"\nimport {\n\tThAssessableField,\n\ttype ThAssessableIncomeBreakdown,\n\ttype ThAssessableIncomeInput,\n\ttype ThExpenseElectableIncome,\n\ttype ThSection40_5Input,\n\ttype ThSection40_6Input,\n\ttype ThSection40_8Input,\n} from \"../types\"\n\nexport function extractElectableAmount(\n\tval: number | ThExpenseElectableIncome | undefined,\n\tfield: string,\n): number {\n\tif (val === undefined) return 0\n\tconst amount = typeof val === \"number\" ? val : val.amount\n\tvalidateMonetaryAmount(amount, field)\n\tif (typeof val === \"object\" && val.actualExpense !== undefined) {\n\t\tvalidateMonetaryAmount(val.actualExpense, `${field}.actualExpense`)\n\t}\n\treturn amount\n}\n\nfunction sumSection40_5(input: ThSection40_5Input | undefined): number {\n\tif (!input) return 0\n\treturn (\n\t\textractElectableAmount(input.building, ThAssessableField.Section40_5_Building) +\n\t\textractElectableAmount(input.agriculturalLand, ThAssessableField.Section40_5_AgriculturalLand) +\n\t\textractElectableAmount(\n\t\t\tinput.nonAgriculturalLand,\n\t\t\tThAssessableField.Section40_5_NonAgriculturalLand,\n\t\t) +\n\t\textractElectableAmount(input.vehicles, ThAssessableField.Section40_5_Vehicles) +\n\t\textractElectableAmount(input.other, ThAssessableField.Section40_5_Other)\n\t)\n}\n\nfunction sumSection40_6(input: ThSection40_6Input | undefined): number {\n\tif (!input) return 0\n\treturn (\n\t\textractElectableAmount(input.medical, ThAssessableField.Section40_6_Medical) +\n\t\textractElectableAmount(input.law, ThAssessableField.Section40_6_Law) +\n\t\textractElectableAmount(input.engineering, ThAssessableField.Section40_6_Engineering) +\n\t\textractElectableAmount(input.architecture, ThAssessableField.Section40_6_Architecture) +\n\t\textractElectableAmount(input.accounting, ThAssessableField.Section40_6_Accounting) +\n\t\textractElectableAmount(input.fineArts, ThAssessableField.Section40_6_FineArts) +\n\t\textractElectableAmount(input.other, ThAssessableField.Section40_6_Other)\n\t)\n}\n\nfunction sumSection40_8(\n\tinput: number | ThExpenseElectableIncome | ThSection40_8Input | undefined,\n): number {\n\tif (input === undefined) return 0\n\tif (typeof input === \"number\" || \"amount\" in input) {\n\t\treturn extractElectableAmount(\n\t\t\tinput as number | ThExpenseElectableIncome,\n\t\t\tThAssessableField.Section40_8,\n\t\t)\n\t}\n\treturn (\n\t\textractElectableAmount(input.general, ThAssessableField.Section40_8_General) +\n\t\textractElectableAmount(input.entertainer, ThAssessableField.Section40_8_Entertainer)\n\t)\n}\n\n/**\n * Normalizes and validates assessable income across Section 40(1)–40(8).\n *\n * @statute Thailand Revenue Code Section 40(1)–(8).\n */\nexport function calculateAssessableIncome(\n\tinput: ThAssessableIncomeInput = {},\n): ThAssessableIncomeBreakdown {\n\tconst s1 = extractElectableAmount(input.section40_1, ThAssessableField.Section40_1)\n\tconst s2 = extractElectableAmount(input.section40_2, ThAssessableField.Section40_2)\n\tconst s3 = extractElectableAmount(input.section40_3, ThAssessableField.Section40_3)\n\tconst s4 = extractElectableAmount(\n\t\ttypeof input.section40_4 === \"number\" ? input.section40_4 : input.section40_4?.amount,\n\t\tThAssessableField.Section40_4,\n\t)\n\tconst s5 = sumSection40_5(input.section40_5)\n\tconst s6 = sumSection40_6(input.section40_6)\n\tconst s7 = extractElectableAmount(input.section40_7, ThAssessableField.Section40_7)\n\tconst s8 = sumSection40_8(input.section40_8)\n\n\tconst total = s1 + s2 + s3 + s4 + s5 + s6 + s7 + s8\n\tconst grossNonEmploymentIncome = s2 + s3 + s4 + s5 + s6 + s7 + s8\n\n\treturn {\n\t\tsection40_1: s1,\n\t\tsection40_2: s2,\n\t\tsection40_3: s3,\n\t\tsection40_4: s4,\n\t\tsection40_5: s5,\n\t\tsection40_6: s6,\n\t\tsection40_7: s7,\n\t\tsection40_8: s8,\n\t\ttotal,\n\t\tgrossNonEmploymentIncome,\n\t}\n}\n","import {\n\ttype BracketEvaluationResult,\n\tevaluateBrackets,\n\ttype TaxBracket,\n} from \"../../core/brackets\"\nimport { fromMinorUnits, toMinorUnits } from \"../../core/money\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport { ThBracketField } from \"../types\"\n\n/**\n * Evaluates an arbitrary Thai statutory bracket schedule (major-unit bounds) in minor units.\n *\n * Math: Converts bounds and incomes to satang, slices through `evaluateBrackets`, converts back.\n * Units: Inputs and outputs in THB major units; arithmetic in integer satang.\n * Throws: TaxValidationError if incomes are negative or non-finite.\n *\n * @statute Thailand Revenue Code Sections 48(1), 48(4), 48(5).\n */\nexport function evaluateThBracketSet(\n\ttaxableIncome: number,\n\tgrossIncome: number,\n\tbrackets: readonly TaxBracket[],\n): {\n\ttotalTax: number\n\tbrackets: BracketEvaluationResult\n} {\n\tconst currency = TH_2026_PARAMETERS.currency\n\tconst minorBrackets = brackets.map((b) => ({\n\t\tupTo: b.upTo === null ? null : toMinorUnits(b.upTo, currency),\n\t\trate: b.rate,\n\t}))\n\tconst taxableMinor = toMinorUnits(taxableIncome, currency, ThBracketField.TaxableIncome)\n\tconst grossMinor = toMinorUnits(grossIncome, currency, ThBracketField.GrossIncome)\n\tconst res = evaluateBrackets(taxableMinor, minorBrackets, grossMinor)\n\n\tconst slices = res.slices.map((s) => ({\n\t\tbracketIndex: s.bracketIndex,\n\t\tlowerBound: fromMinorUnits(s.lowerBound, currency),\n\t\tupperBound: s.upperBound !== null ? fromMinorUnits(s.upperBound, currency) : null,\n\t\trate: s.rate,\n\t\ttaxableAmount: fromMinorUnits(s.taxableAmount, currency),\n\t\ttax: fromMinorUnits(s.tax, currency),\n\t}))\n\n\tconst totalTax = fromMinorUnits(res.totalTax, currency)\n\treturn {\n\t\ttotalTax,\n\t\tbrackets: {\n\t\t\ttotalTax,\n\t\t\tslices,\n\t\t\teffectiveRate: res.effectiveRate,\n\t\t\tmarginalRate: res.marginalRate,\n\t\t},\n\t}\n}\n\n/**\n * Evaluates the regular progressive tax brackets for Thailand PIT.\n *\n * Math: Slices taxable income through `TH_2026_PARAMETERS.brackets`.\n * Units: THB major units.\n * Throws: TaxValidationError if incomes are negative or non-finite.\n *\n * @statute Thailand Revenue Code Section 48(1).\n */\nexport function evaluateThBrackets(\n\ttaxableIncome: number,\n\tgrossIncome: number,\n): {\n\ttotalTax: number\n\tbrackets: BracketEvaluationResult\n} {\n\treturn evaluateThBracketSet(taxableIncome, grossIncome, TH_2026_PARAMETERS.brackets)\n}\n","import { type ThCombinedTaxBreakdown, ThCombineField, ThTaxField } from \"../types\"\nimport { fromSatang, toSatang } from \"../units\"\n\n/** Component taxes, credits, and payments to combine. Units: THB major units. */\nexport interface ThCombineInput {\n\t/** Section 48(1)/48(2) tax on the regular schedule. */\n\treadonly regularTax: number\n\t/** Section 48(5) separate sheet tax. */\n\treadonly separateSheetTax: number\n\t/** Section 48(4) real estate transfer tax. */\n\treadonly realEstateTax: number\n\t/** Section 40(4) tax withheld at source and elected final. */\n\treadonly finalWithholdingTax: number\n\t/** Section 47 bis dividend tax credit. */\n\treadonly dividendTaxCredit: number\n\t/** Other tax already withheld/paid, credited against the liability. */\n\treadonly withholdingTax: number\n}\n\n/**\n * Combines regular, separate sheet, real estate, and final withholding taxes into one liability.\n *\n * Purpose: Produces the single audited total and the amount still payable (or refundable).\n * Math: totalCombinedTax = regular + separate sheet + real estate + final withholding (before credits);\n * netTaxPayable = totalCombinedTax - final withholding (already withheld at source)\n * - Section 47 bis dividend credit - withholding tax paid. May be negative (refund).\n * Units: Inputs and outputs in THB major units; arithmetic in integer satang.\n * Throws: TaxValidationError if any component is negative or non-finite.\n *\n * @param input Component taxes, credits, and payments in THB.\n * @statute Thailand Revenue Code Sections 40(4), 47 bis, 48(1)–(5).\n */\nexport function combineThLiability(input: ThCombineInput): ThCombinedTaxBreakdown {\n\tconst regular = toSatang(input.regularTax, ThCombineField.RegularTax)\n\tconst separateSheet = toSatang(input.separateSheetTax, ThCombineField.SeparateSheetTax)\n\tconst realEstate = toSatang(input.realEstateTax, ThCombineField.RealEstateTax)\n\tconst finalWithholding = toSatang(input.finalWithholdingTax, ThCombineField.FinalWithholdingTax)\n\tconst dividendCredit = toSatang(input.dividendTaxCredit, ThCombineField.DividendTaxCredit)\n\tconst withheld = toSatang(input.withholdingTax, ThTaxField.WithholdingTax)\n\n\tconst total = regular + separateSheet + realEstate + finalWithholding\n\tconst credits = finalWithholding + dividendCredit + withheld\n\treturn {\n\t\ttotalCombinedTax: fromSatang(total, ThTaxField.TotalTax),\n\t\tnetTaxPayable: fromSatang(total - credits, ThTaxField.NetTaxPayable, { allowNegative: true }),\n\t}\n}\n","import { type ClampedAmount, clampCap } from \"../../core/caps\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport { ThExpenseField } from \"../types\"\n\n/**\n * Calculates shared Section 40(1) and Section 40(2) expense deduction.\n *\n * Math: 50% of combined 40(1) + 40(2) income, clamped to 100,000 THB ceiling.\n * Units: THB major units.\n *\n * @statute Thailand Revenue Code Section 42 bis.\n */\nexport function calculateExpenses40_1_2(s1: number, s2: number): ClampedAmount {\n\tconst combined = s1 + s2\n\tconst requested = Math.floor(combined * TH_2026_PARAMETERS.expenses.section40_1_2.rate)\n\treturn clampCap(\n\t\trequested,\n\t\tTH_2026_PARAMETERS.expenses.section40_1_2.ceiling,\n\t\tThExpenseField.Section40_1_2,\n\t)\n}\n","import { type ClampedAmount, clampCap } from \"../../core/caps\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport { type ThExpenseElectableIncome, ThExpenseField } from \"../types\"\n\n/**\n * Calculates Section 40(3) royalties/goodwill/copyright expense deduction.\n *\n * Math: Standard 50% capped at 100,000 THB or actual expenses capped at income.\n * Units: THB major units.\n *\n * @statute Thailand Revenue Code Section 42 ter; Royal Decree No. 11 Section 4.\n */\nexport function calculateExpenses40_3(\n\tinput: number | ThExpenseElectableIncome | undefined,\n\tincomeAmount: number,\n): ClampedAmount {\n\tif (incomeAmount <= 0) {\n\t\treturn { requested: 0, allowed: 0, cap: 0, wasClamped: false }\n\t}\n\tconst isActual = typeof input === \"object\" && input.expenseElection === \"actual\"\n\tif (isActual) {\n\t\tconst requested = input.actualExpense ?? 0\n\t\treturn clampCap(requested, incomeAmount, ThExpenseField.Section40_3)\n\t}\n\tconst requested = Math.floor(incomeAmount * TH_2026_PARAMETERS.expenses.section40_3.rate)\n\treturn clampCap(\n\t\trequested,\n\t\tTH_2026_PARAMETERS.expenses.section40_3.ceiling,\n\t\tThExpenseField.Section40_3,\n\t)\n}\n","import type { ClampedAmount } from \"../../core/caps\"\n\n/**\n * Calculates Section 40(4) expense deduction (0% statutory).\n *\n * Math: Thailand Revenue Code permits 0% expense deductions for interest, dividends, crypto.\n * Units: THB major units.\n *\n * @statute Thailand Revenue Code Section 40(4).\n */\nexport function calculateExpenses40_4(_incomeAmount: number): ClampedAmount {\n\treturn {\n\t\trequested: 0,\n\t\tallowed: 0,\n\t\tcap: 0,\n\t\twasClamped: false,\n\t}\n}\n","import { type ClampedAmount, clampCap } from \"../../core/caps\"\nimport type { ThExpenseElectableIncome } from \"../types\"\n\n/**\n * Computes expense deduction for an electable income item (standard % vs actual).\n *\n * Math: If actual, clamp to item income. If standard, apply percentage of item income.\n * Units: THB major units.\n *\n * @param item Income item number or configuration object.\n * @param standardRate Statutory percentage rate for standard deduction.\n * @param field Diagnostic field name.\n */\nexport function calculateElectableItemExpense(\n\titem: number | ThExpenseElectableIncome | undefined,\n\tstandardRate: number,\n\tfield: string,\n): ClampedAmount {\n\tif (!item) {\n\t\treturn { requested: 0, allowed: 0, cap: 0, wasClamped: false }\n\t}\n\tconst amount = typeof item === \"number\" ? item : item.amount\n\tif (amount <= 0) {\n\t\treturn { requested: 0, allowed: 0, cap: 0, wasClamped: false }\n\t}\n\tconst isActual = typeof item === \"object\" && item.expenseElection === \"actual\"\n\tif (isActual) {\n\t\tconst req = item.actualExpense ?? 0\n\t\treturn clampCap(req, amount, field)\n\t}\n\tconst req = Math.floor(amount * standardRate)\n\treturn { requested: req, allowed: req, cap: amount, wasClamped: false }\n}\n","import type { ClampedAmount } from \"../../core/caps\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport { ThExpenseField, type ThSection40_5Input } from \"../types\"\nimport { calculateElectableItemExpense } from \"./electable-expense\"\n\n/**\n * Calculates Section 40(5) rental expense deductions across property sub-categories.\n *\n * Math: Standard percentage per property class or actual expenses capped at income.\n * Units: THB major units.\n *\n * @statute Thailand Royal Decree No. 11 Section 5.\n */\nexport function calculateExpenses40_5(input: ThSection40_5Input | undefined): ClampedAmount {\n\tif (!input) {\n\t\treturn { requested: 0, allowed: 0, cap: 0, wasClamped: false }\n\t}\n\tconst rates = TH_2026_PARAMETERS.expenses.section40_5\n\tconst b = calculateElectableItemExpense(\n\t\tinput.building,\n\t\trates.building,\n\t\tThExpenseField.Section40_5_Building,\n\t)\n\tconst a = calculateElectableItemExpense(\n\t\tinput.agriculturalLand,\n\t\trates.agriculturalLand,\n\t\tThExpenseField.Section40_5_AgriculturalLand,\n\t)\n\tconst na = calculateElectableItemExpense(\n\t\tinput.nonAgriculturalLand,\n\t\trates.nonAgriculturalLand,\n\t\tThExpenseField.Section40_5_NonAgriculturalLand,\n\t)\n\tconst v = calculateElectableItemExpense(\n\t\tinput.vehicles,\n\t\trates.vehicles,\n\t\tThExpenseField.Section40_5_Vehicles,\n\t)\n\tconst o = calculateElectableItemExpense(\n\t\tinput.other,\n\t\trates.other,\n\t\tThExpenseField.Section40_5_Other,\n\t)\n\n\tconst requested = b.requested + a.requested + na.requested + v.requested + o.requested\n\tconst allowed = b.allowed + a.allowed + na.allowed + v.allowed + o.allowed\n\tconst cap = b.cap + a.cap + na.cap + v.cap + o.cap\n\n\treturn {\n\t\trequested,\n\t\tallowed,\n\t\tcap,\n\t\twasClamped: requested > allowed,\n\t}\n}\n","import type { ClampedAmount } from \"../../core/caps\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport { ThExpenseField, type ThSection40_6Input } from \"../types\"\nimport { calculateElectableItemExpense } from \"./electable-expense\"\n\n/**\n * Calculates Section 40(6) liberal professions expense deduction.\n *\n * Math: Medical 60%, law/engineering/architecture/accounting/fine arts/other 30%, or actual.\n * Units: THB major units.\n *\n * @statute Thailand Royal Decree No. 11 Section 6.\n */\nexport function calculateExpenses40_6(input: ThSection40_6Input | undefined): ClampedAmount {\n\tif (!input) {\n\t\treturn { requested: 0, allowed: 0, cap: 0, wasClamped: false }\n\t}\n\tconst rates = TH_2026_PARAMETERS.expenses.section40_6\n\tconst med = calculateElectableItemExpense(\n\t\tinput.medical,\n\t\trates.medical,\n\t\tThExpenseField.Section40_6_Medical,\n\t)\n\tconst law = calculateElectableItemExpense(input.law, rates.other, ThExpenseField.Section40_6_Law)\n\tconst eng = calculateElectableItemExpense(\n\t\tinput.engineering,\n\t\trates.other,\n\t\tThExpenseField.Section40_6_Engineering,\n\t)\n\tconst arch = calculateElectableItemExpense(\n\t\tinput.architecture,\n\t\trates.other,\n\t\tThExpenseField.Section40_6_Architecture,\n\t)\n\tconst acc = calculateElectableItemExpense(\n\t\tinput.accounting,\n\t\trates.other,\n\t\tThExpenseField.Section40_6_Accounting,\n\t)\n\tconst art = calculateElectableItemExpense(\n\t\tinput.fineArts,\n\t\trates.other,\n\t\tThExpenseField.Section40_6_FineArts,\n\t)\n\tconst oth = calculateElectableItemExpense(\n\t\tinput.other,\n\t\trates.other,\n\t\tThExpenseField.Section40_6_Other,\n\t)\n\n\tconst requested =\n\t\tmed.requested +\n\t\tlaw.requested +\n\t\teng.requested +\n\t\tarch.requested +\n\t\tacc.requested +\n\t\tart.requested +\n\t\toth.requested\n\tconst allowed =\n\t\tmed.allowed + law.allowed + eng.allowed + arch.allowed + acc.allowed + art.allowed + oth.allowed\n\tconst cap = med.cap + law.cap + eng.cap + arch.cap + acc.cap + art.cap + oth.cap\n\n\treturn {\n\t\trequested,\n\t\tallowed,\n\t\tcap,\n\t\twasClamped: requested > allowed,\n\t}\n}\n","import type { ClampedAmount } from \"../../core/caps\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport { type ThExpenseElectableIncome, ThExpenseField } from \"../types\"\nimport { calculateElectableItemExpense } from \"./electable-expense\"\n\n/**\n * Calculates Section 40(7) contracting expense deduction.\n *\n * Math: Standard 60% or actual expenses capped at income.\n * Units: THB major units.\n *\n * @statute Thailand Royal Decree No. 11 Section 7.\n */\nexport function calculateExpenses40_7(\n\tinput: number | ThExpenseElectableIncome | undefined,\n): ClampedAmount {\n\treturn calculateElectableItemExpense(\n\t\tinput,\n\t\tTH_2026_PARAMETERS.expenses.section40_7.rate,\n\t\tThExpenseField.Section40_7,\n\t)\n}\n","import { type ClampedAmount, clampCap } from \"../../core/caps\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport { type ThExpenseElectableIncome, ThExpenseField, type ThSection40_8Input } from \"../types\"\nimport { calculateElectableItemExpense } from \"./electable-expense\"\n\nfunction calculateEntertainerExpense(\n\titem: number | ThExpenseElectableIncome | undefined,\n): ClampedAmount {\n\tif (!item) {\n\t\treturn { requested: 0, allowed: 0, cap: 0, wasClamped: false }\n\t}\n\tconst amount = typeof item === \"number\" ? item : item.amount\n\tif (amount <= 0) {\n\t\treturn { requested: 0, allowed: 0, cap: 0, wasClamped: false }\n\t}\n\tif (typeof item === \"object\" && item.expenseElection === \"actual\") {\n\t\treturn clampCap(item.actualExpense ?? 0, amount, ThExpenseField.Section40_8_Entertainer)\n\t}\n\tconst p = TH_2026_PARAMETERS.expenses.section40_8\n\tconst tier1 = Math.min(amount, p.entertainerFirstTierThreshold)\n\tconst tier2 = Math.max(0, amount - p.entertainerFirstTierThreshold)\n\tconst computed =\n\t\tMath.floor(tier1 * p.entertainerFirstTierRate) + Math.floor(tier2 * p.entertainerSecondTierRate)\n\treturn clampCap(computed, p.entertainerCeiling, ThExpenseField.Section40_8_Entertainer)\n}\n\n/**\n * Calculates Section 40(8) business, commerce, and entertainer expense deduction.\n *\n * Math: General 60% or actual; entertainer tiered 60%/40% up to 600,000 THB or actual.\n * Units: THB major units.\n *\n * @statute Thailand Royal Decree No. 11 Section 8.\n */\nexport function calculateExpenses40_8(\n\tinput: number | ThExpenseElectableIncome | ThSection40_8Input | undefined,\n): ClampedAmount {\n\tif (!input) {\n\t\treturn { requested: 0, allowed: 0, cap: 0, wasClamped: false }\n\t}\n\tif (typeof input === \"number\" || \"amount\" in input) {\n\t\treturn calculateElectableItemExpense(\n\t\t\tinput as number | ThExpenseElectableIncome,\n\t\t\tTH_2026_PARAMETERS.expenses.section40_8.generalRate,\n\t\t\tThExpenseField.Section40_8_General,\n\t\t)\n\t}\n\tconst gen = calculateElectableItemExpense(\n\t\tinput.general,\n\t\tTH_2026_PARAMETERS.expenses.section40_8.generalRate,\n\t\tThExpenseField.Section40_8_General,\n\t)\n\tconst ent = calculateEntertainerExpense(input.entertainer)\n\tconst requested = gen.requested + ent.requested\n\tconst allowed = gen.allowed + ent.allowed\n\tconst cap = gen.cap + ent.cap\n\treturn {\n\t\trequested,\n\t\tallowed,\n\t\tcap,\n\t\twasClamped: requested > allowed,\n\t}\n}\n","import type {\n\tThAssessableIncomeBreakdown,\n\tThAssessableIncomeInput,\n\tThExpenseDeductionsBreakdown,\n} from \"../types\"\nimport { calculateExpenses40_1_2 } from \"./expenses-40-1-2\"\nimport { calculateExpenses40_3 } from \"./expenses-40-3\"\nimport { calculateExpenses40_4 } from \"./expenses-40-4\"\nimport { calculateExpenses40_5 } from \"./expenses-40-5\"\nimport { calculateExpenses40_6 } from \"./expenses-40-6\"\nimport { calculateExpenses40_7 } from \"./expenses-40-7\"\nimport { calculateExpenses40_8 } from \"./expenses-40-8\"\n\n/**\n * Aggregates all Section 40(1)–40(8) statutory expense deductions.\n *\n * @statute Thailand Revenue Code Sections 42 bis, 42 ter, 43–46; Royal Decree No. 11.\n */\nexport function calculateExpenseDeductions(\n\tincome: ThAssessableIncomeInput = {},\n\tassessable: ThAssessableIncomeBreakdown,\n): ThExpenseDeductionsBreakdown {\n\tconst e1_2 = calculateExpenses40_1_2(assessable.section40_1, assessable.section40_2)\n\tconst e3 = calculateExpenses40_3(income.section40_3, assessable.section40_3)\n\tconst e4 = calculateExpenses40_4(assessable.section40_4)\n\tconst e5 = calculateExpenses40_5(income.section40_5)\n\tconst e6 = calculateExpenses40_6(income.section40_6)\n\tconst e7 = calculateExpenses40_7(income.section40_7)\n\tconst e8 = calculateExpenses40_8(income.section40_8)\n\n\tconst total =\n\t\te1_2.allowed + e3.allowed + e4.allowed + e5.allowed + e6.allowed + e7.allowed + e8.allowed\n\n\treturn {\n\t\tsection40_1_2: e1_2,\n\t\tsection40_3: e3,\n\t\tsection40_4: e4,\n\t\tsection40_5: e5,\n\t\tsection40_6: e6,\n\t\tsection40_7: e7,\n\t\tsection40_8: e8,\n\t\ttotal,\n\t}\n}\n","import { applyPercentage, fromMinorUnits, toMinorUnits } from \"../../core/money\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport { type ThMinimumGrossTaxBreakdown, ThTaxField } from \"../types\"\n\n/**\n * Calculates Section 48(2) Minimum Gross Tax on Section 40(2)–40(8) income.\n *\n * Math: 0.5% of gross Section 40(2)–(8) if gross >= 120,000 THB; payable only if the 0.5% tax exceeds 5,000 THB\n * and exceeds the progressive tax.\n * Units: THB major units.\n *\n * @statute Thailand Revenue Code Section 48(2).\n */\nexport function calculateMinimumGrossTax(\n\tgrossNonEmploymentIncome: number,\n\tprogressiveTax: number,\n): ThMinimumGrossTaxBreakdown {\n\tconst p = TH_2026_PARAMETERS.minimumGrossTax\n\tif (grossNonEmploymentIncome < p.incomeThreshold) {\n\t\treturn {\n\t\t\tgrossEligibleIncome: grossNonEmploymentIncome,\n\t\t\trate: p.rate,\n\t\t\tcalculatedTax: 0,\n\t\t\tthreshold: p.taxThreshold,\n\t\t\tisApplicable: false,\n\t\t\twasApplied: false,\n\t\t}\n\t}\n\n\tconst currency = TH_2026_PARAMETERS.currency\n\tconst minorIncome = toMinorUnits(grossNonEmploymentIncome, currency, ThTaxField.MinimumGrossTax)\n\tconst minorTax = applyPercentage(minorIncome, p.rate)\n\tconst calculatedTax = fromMinorUnits(minorTax, currency)\n\n\tconst isApplicable = calculatedTax > p.taxThreshold\n\tconst wasApplied = isApplicable && calculatedTax > progressiveTax\n\n\treturn {\n\t\tgrossEligibleIncome: grossNonEmploymentIncome,\n\t\trate: p.rate,\n\t\tcalculatedTax,\n\t\tthreshold: p.taxThreshold,\n\t\tisApplicable,\n\t\twasApplied,\n\t}\n}\n","import { TaxValidationError } from \"../core/money\"\nimport { ThSpecialRegimeField } from \"./types\"\n\n/**\n * Validates a Thai corporate income tax rate used for the Section 47 bis gross-up.\n *\n * Purpose: Keeps `rate / (1 - rate)` finite before any stage uses the rate.\n * Math: Requires `0 <= rate < 1`.\n * Units: Rate ratio (0.2 = 20%).\n * Throws: TaxValidationError if rate is NaN, non-finite, negative, or at least 1.\n *\n * @param rate Corporate income tax rate ratio.\n * @statute Thailand Revenue Code Section 47 bis.\n */\nexport function validateCorpTaxRate(rate: number): void {\n\tif (!Number.isFinite(rate) || rate < 0 || rate >= 1) {\n\t\tthrow new TaxValidationError(\n\t\t\tThSpecialRegimeField.CorpTaxRate,\n\t\t\trate,\n\t\t\t\"Corporate tax rate must be finite and satisfy 0 <= rate < 1\",\n\t\t)\n\t}\n}\n\n/**\n * Resolves an optional typed choice (election, payment type, acquisition type) at runtime.\n *\n * Purpose: TypeScript unions are erased at runtime, so untyped callers could otherwise pass an\n * unknown string and silently get the default behaviour.\n * Math: Returns `fallback` when `value` is `undefined`; otherwise requires membership in `choices`.\n * Units: Dimensionless string enum.\n * Throws: TaxValidationError (reported against `field`) if `value` is not one of `choices`.\n *\n * @param value Caller-supplied value, possibly outside the declared union.\n * @param choices Typed const object whose values are the permitted choices.\n * @param fallback Choice used when `value` is `undefined`.\n * @param field Diagnostic field path reported on failure.\n * @statute Jurisdiction-neutral input bound (AGENTS rule 8); no statutory citation.\n */\nexport function resolveChoice<T extends string>(\n\tvalue: string | undefined,\n\tchoices: Readonly<Record<string, T>>,\n\tfallback: T,\n\tfield: ThSpecialRegimeField,\n): T {\n\tif (value === undefined) return fallback\n\tconst match = Object.values(choices).find((choice) => choice === value)\n\tif (match === undefined) {\n\t\tthrow new TaxValidationError(field, value, \"Value is not a supported choice\")\n\t}\n\treturn match\n}\n","import { applyGrossUp, validateMonetaryAmount } from \"../../core/money\"\nimport { ThCombineField, type ThDividendTaxCreditBreakdown, ThSpecialRegimeField } from \"../types\"\nimport { fromSatang, toSatang } from \"../units\"\nimport { validateCorpTaxRate } from \"../validate\"\n\n/**\n * Calculates the Section 47 bis dividend tax credit for dividends included in regular income.\n *\n * Purpose: Recovers the corporate tax already paid on dividends taxed again in the regular schedule.\n * Math: credit = dividend * corpTaxRate / (1 - corpTaxRate), exact in satang and rounded half-up.\n * The grossed-up amount (dividend + credit) is assessable; the credit offsets tax payable.\n * Units: Inputs and outputs in THB major units; arithmetic in integer satang.\n * Throws: TaxValidationError if dividend is negative/non-finite or the rate is invalid.\n *\n * @param includedDividends Dividends elected `\"include\"`, in THB.\n * @param corpTaxRate Corporate tax rate in [0, 1); `undefined` means no credit.\n * @statute Thailand Revenue Code Section 47 bis.\n */\nexport function calculateDividendTaxCredit(\n\tincludedDividends: number,\n\tcorpTaxRate: number | undefined,\n): ThDividendTaxCreditBreakdown {\n\tvalidateMonetaryAmount(includedDividends, ThSpecialRegimeField.Dividends)\n\tif (corpTaxRate === undefined) {\n\t\treturn {\n\t\t\tisApplicable: false,\n\t\t\tdividendAmount: includedDividends,\n\t\t\tcorpTaxRate: 0,\n\t\t\tcredit: 0,\n\t\t\tgrossedUpAmount: includedDividends,\n\t\t}\n\t}\n\tvalidateCorpTaxRate(corpTaxRate)\n\tconst dividendSatang = toSatang(includedDividends, ThSpecialRegimeField.Dividends)\n\tconst creditSatang = applyGrossUp(dividendSatang, corpTaxRate, ThSpecialRegimeField.Dividends)\n\treturn {\n\t\tisApplicable: true,\n\t\tdividendAmount: includedDividends,\n\t\tcorpTaxRate,\n\t\tcredit: fromSatang(creditSatang, ThCombineField.DividendTaxCredit),\n\t\tgrossedUpAmount: fromSatang(dividendSatang + creditSatang, ThCombineField.DividendTaxCredit),\n\t}\n}\n","import { applyPercentage } from \"../../core/money\"\nimport {\n\tThFinalWithholdingElection,\n\ttype ThFinalWithholdingSourceBreakdown,\n\ttype ThSection40_4Dividend,\n\ttype ThSection40_4Source,\n\ttype ThSpecialRegimeField,\n} from \"../types\"\nimport { fromSatang, toSatang } from \"../units\"\nimport { resolveChoice } from \"../validate\"\nimport { extractElectableAmount } from \"./assessable\"\n\nexport type ThSourceInput = number | ThSection40_4Source | ThSection40_4Dividend | undefined\n\n/** One Section 40(4) source after its election is applied. Units: integer satang. */\nexport interface ResolvedSource {\n\treadonly breakdown: ThFinalWithholdingSourceBreakdown\n\treadonly satang: number\n\treadonly taxSatang: number\n}\n\nfunction electionOf(input: ThSourceInput, field: ThSpecialRegimeField): ThFinalWithholdingElection {\n\tconst election = typeof input === \"object\" ? input.election : undefined\n\treturn resolveChoice(\n\t\telection,\n\t\tThFinalWithholdingElection,\n\t\tThFinalWithholdingElection.Include,\n\t\tfield,\n\t)\n}\n\n/**\n * Resolves one Section 40(4) source: validates its amount and election, withholds if final.\n *\n * Purpose: Applies the per-source final withholding election.\n * Math: tax = amount * rate (satang, half-up) when the election is `\"final\"`, otherwise 0.\n * Units: Input in THB major units; `satang` and `taxSatang` in integer satang.\n * Throws: TaxValidationError for a negative/non-finite amount or an unknown election value.\n *\n * @param input Bare amount, source object, or undefined.\n * @param field Diagnostic path of the amount.\n * @param electionField Diagnostic path of the election.\n * @param rate Final withholding rate (ratio).\n * @statute Thailand Revenue Code Sections 40(4), 50(2)(g), 57 ter.\n */\nexport function resolveSource(\n\tinput: ThSourceInput,\n\tfield: ThSpecialRegimeField,\n\telectionField: ThSpecialRegimeField,\n\trate: number,\n): ResolvedSource {\n\tconst amount = extractElectableAmount(input, field)\n\tconst election = electionOf(input, electionField)\n\tconst satang = toSatang(amount, field)\n\tconst isFinal = election === ThFinalWithholdingElection.Final\n\tconst taxSatang = isFinal ? applyPercentage(satang, rate) : 0\n\treturn { breakdown: { election, amount, rate, tax: fromSatang(taxSatang) }, satang, taxSatang }\n}\n","import { TH_2026_PARAMETERS } from \"../parameters\"\nimport {\n\tThAssessableField,\n\ttype ThFinalWithholdingBreakdown,\n\tThFinalWithholdingElection,\n\ttype ThSection40_4Input,\n\tThSpecialRegimeField,\n} from \"../types\"\nimport { fromSatang, toSatang } from \"../units\"\nimport { validateCorpTaxRate } from \"../validate\"\nimport { extractElectableAmount } from \"./assessable\"\nimport { type ResolvedSource, resolveSource } from \"./final-withholding-source\"\n\n/**\n * Section 40(4) split into regular-bracket income and final-withheld income.\n * Units: THB major units.\n *\n * @statute Thailand Revenue Code Sections 40(4), 47 bis, 50(2)(g), 57 ter.\n */\nexport interface ThResolvedSection40_4 {\n\t/** Section 40(4) income included in regular brackets, before any dividend gross-up. */\n\treadonly includedAmount: number\n\t/** Dividends elected `\"include\"`. */\n\treadonly includedDividends: number\n\t/** Corporate tax rate (ratio) for included dividends, if given. */\n\treadonly corpTaxRate: number | undefined\n\t/** Audit trail of the sources elected `\"final\"` and their withheld tax. */\n\treadonly finalWithholding: ThFinalWithholdingBreakdown\n}\n\nfunction toStructuredInput(input: number | ThSection40_4Input | undefined): ThSection40_4Input {\n\treturn typeof input === \"number\" ? { amount: input } : (input ?? {})\n}\n\nfunction sumSatang(sources: readonly ResolvedSource[], seed = 0): number {\n\treturn sources.reduce((sum, source) => sum + source.satang, seed)\n}\n\n/**\n * Splits Section 40(4) income into regular-bracket income and final-withheld income.\n *\n * Purpose: Applies each source's election so only included income reaches the regular schedule.\n * Math: For each source elected `\"final\"`, tax = amount * rate (satang, half-up) and the income is\n * excluded from regular brackets. `\"include\"` (default) adds the income to Section 40(4)\n * assessable income at a 0% expense rate.\n * Units: Inputs and outputs in THB major units; arithmetic in integer satang.\n * Throws: TaxValidationError for negative/non-finite amounts or an invalid `corpTaxRate`.\n *\n * @param input Bare amount, structured sources, or undefined.\n * @statute Thailand Revenue Code Sections 40(4), 50(2)(g), 56, 57 ter.\n */\nexport function resolveSection40_4(\n\tinput: number | ThSection40_4Input | undefined,\n): ThResolvedSection40_4 {\n\tconst section = toStructuredInput(input)\n\tconst rates = TH_2026_PARAMETERS.finalWithholding\n\tconst base = extractElectableAmount(section.amount, ThAssessableField.Section40_4)\n\tconst interest = resolveSource(\n\t\tsection.interest,\n\t\tThSpecialRegimeField.Interest,\n\t\tThSpecialRegimeField.InterestElection,\n\t\trates.interestRate,\n\t)\n\tconst dividends = resolveSource(\n\t\tsection.dividends,\n\t\tThSpecialRegimeField.Dividends,\n\t\tThSpecialRegimeField.DividendsElection,\n\t\trates.dividendRate,\n\t)\n\tconst digital = resolveSource(\n\t\tsection.digitalAssets,\n\t\tThSpecialRegimeField.DigitalAssets,\n\t\tThSpecialRegimeField.DigitalAssetsElection,\n\t\trates.digitalAssetRate,\n\t)\n\tconst corpTaxRate =\n\t\ttypeof section.dividends === \"object\" ? section.dividends.corpTaxRate : undefined\n\tif (corpTaxRate !== undefined) validateCorpTaxRate(corpTaxRate)\n\n\tconst sources = [interest, dividends, digital]\n\tconst isFinal = (source: ResolvedSource): boolean =>\n\t\tsource.breakdown.election === ThFinalWithholdingElection.Final\n\tconst finalSources = sources.filter(isFinal)\n\tconst includedSatang = sumSatang(\n\t\tsources.filter((source) => !isFinal(source)),\n\t\ttoSatang(base, ThAssessableField.Section40_4),\n\t)\n\tconst isDividendIncluded = !isFinal(dividends)\n\treturn {\n\t\tincludedAmount: fromSatang(includedSatang),\n\t\tincludedDividends: isDividendIncluded ? dividends.breakdown.amount : 0,\n\t\tcorpTaxRate: isDividendIncluded ? corpTaxRate : undefined,\n\t\tfinalWithholding: {\n\t\t\tinterest: interest.breakdown,\n\t\t\tdividends: dividends.breakdown,\n\t\t\tdigitalAssets: digital.breakdown,\n\t\t\ttotalAmount: fromSatang(sumSatang(finalSources)),\n\t\t\ttotalTax: fromSatang(finalSources.reduce((sum, source) => sum + source.taxSatang, 0)),\n\t\t},\n\t}\n}\n","import { TH_2026_PARAMETERS } from \"../parameters\"\nimport { ThRealEstateAcquisitionType, ThRealEstateExpenseBasis } from \"../types\"\n\n/**\n * Caps the holding period used by Section 48(4) at the statutory maximum.\n *\n * Purpose: More than 10 years of holding counts as 10 years.\n * Math: min(requestedYears, `realEstate.maxHoldingYears`).\n * Units: Dimensionless year count (callers round part-years up).\n * Throws: Never; `requestedYears` is validated by the caller.\n *\n * @param requestedYears Validated positive safe integer holding period.\n * @statute Thailand Revenue Code Section 48(4); Ministerial Regulation No. 126.\n */\nexport function effectiveHoldingYears(requestedYears: number): number {\n\treturn Math.min(requestedYears, TH_2026_PARAMETERS.realEstate.maxHoldingYears)\n}\n\n/**\n * Selects the Section 48(4) expense rate for the acquisition type.\n *\n * Purpose: Inherited or gifted property takes a flat rate; commercial or other property takes the\n * holding-year table. The two are never combined.\n * Math: Flat `inheritanceExpenseRate`, or the last `expenseTable` row with `minYears <= years`.\n * Units: Rate ratio; `years` is the effective (capped) holding period.\n * Throws: Never; the acquisition type is validated by the caller.\n *\n * @param acquisitionType Validated acquisition type.\n * @param years Effective holding years.\n * @statute Thailand Revenue Code Section 48(4); Ministerial Regulation No. 126.\n */\nexport function resolveRealEstateExpense(\n\tacquisitionType: ThRealEstateAcquisitionType,\n\tyears: number,\n): { readonly basis: ThRealEstateExpenseBasis; readonly rate: number } {\n\tconst rules = TH_2026_PARAMETERS.realEstate\n\tif (acquisitionType === ThRealEstateAcquisitionType.InheritanceOrGift) {\n\t\treturn { basis: ThRealEstateExpenseBasis.FlatInheritance, rate: rules.inheritanceExpenseRate }\n\t}\n\tconst rate = rules.expenseTable.reduce((acc, row) => (years >= row.minYears ? row.rate : acc), 0)\n\treturn { basis: ThRealEstateExpenseBasis.HoldingPeriodTable, rate }\n}\n","import type { TaxBracket } from \"../../core/brackets\"\nimport {\n\tapplyPercentage,\n\tmultiplySafeInteger,\n\troundHalfUp,\n\tvalidateMonetaryAmount,\n\tvalidateSafeIntegerCount,\n} from \"../../core/money\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport {\n\tThRealEstateAcquisitionType,\n\ttype ThRealEstateBreakdown,\n\tThRealEstateExpenseBasis,\n\ttype ThRealEstateTransferInput,\n\tThSpecialRegimeField,\n} from \"../types\"\nimport { fromSatang, toSatang } from \"../units\"\nimport { resolveChoice } from \"../validate\"\nimport { evaluateThBracketSet, evaluateThBrackets } from \"./bracket-eval\"\nimport { effectiveHoldingYears, resolveRealEstateExpense } from \"./real-estate-expense\"\n\nconst EMPTY_BREAKDOWN: ThRealEstateBreakdown = {\n\tgrossTransferValue: 0,\n\tacquisitionType: ThRealEstateAcquisitionType.Commercial,\n\texpenseBasis: ThRealEstateExpenseBasis.HoldingPeriodTable,\n\trequestedHoldingYears: 0,\n\teffectiveHoldingYears: 0,\n\texpenseRate: 0,\n\texpense: 0,\n\tnetGain: 0,\n\tannualGain: 0,\n\tbrackets: evaluateThBrackets(0, 0).brackets,\n\tannualTax: 0,\n\ttaxBeforeCap: 0,\n\ttaxCap: 0,\n\twasClamped: false,\n\ttax: 0,\n}\n\n/**\n * Scales every finite annualised-schedule bracket bound by the holding period.\n *\n * Math: `years * tax(net / years)` equals the tax on `net` against bounds multiplied by `years`\n * (each slice `rate * clip(net/years - lo, 0, width) * years = rate * clip(net - lo*years, 0, width*years)`),\n * so the annualisation needs no intermediate rounding.\n * Units: THB major units; scaling performed in integer satang.\n * Throws: TaxValidationError if a scaled bound exceeds the safe integer range (unreachable\n * for the capped holding period).\n */\nfunction scaleBracketsByYears(years: number): readonly TaxBracket[] {\n\treturn TH_2026_PARAMETERS.realEstate.brackets.map((bracket) => {\n\t\tif (bracket.upTo === null) return bracket\n\t\tconst scaled = multiplySafeInteger(\n\t\t\ttoSatang(bracket.upTo),\n\t\t\tyears,\n\t\t\tThSpecialRegimeField.RealEstateHoldingYears,\n\t\t)\n\t\treturn { upTo: fromSatang(scaled), rate: bracket.rate }\n\t})\n}\n\n/**\n * Clamps real estate tax at the statutory share of the gross transfer value.\n *\n * Purpose: Applies the Section 48(4) ceiling on the separately computed tax.\n * Math: taxCap = grossMinor * `realEstate.taxCapRate` (20%, half-up); tax = min(taxBeforeCap, taxCap).\n * Units: Integer minor units (satang) in and out.\n * Throws: TaxValidationError if `grossMinor` is not a non-negative safe integer.\n * Note: Unreachable end to end (top rate 35% of at most 50% net is 17.5% < 20%); kept as the\n * statutory guard.\n *\n * @param taxBeforeCap Uncapped tax in satang.\n * @param grossMinor Gross transfer value in satang.\n * @statute Thailand Revenue Code Section 48(4).\n */\nexport function applyRealEstateCap(\n\ttaxBeforeCap: number,\n\tgrossMinor: number,\n): { taxCap: number; tax: number; wasClamped: boolean } {\n\tconst taxCap = applyPercentage(grossMinor, TH_2026_PARAMETERS.realEstate.taxCapRate)\n\treturn { taxCap, tax: Math.min(taxBeforeCap, taxCap), wasClamped: taxBeforeCap > taxCap }\n}\n\n/**\n * Calculates Section 48(4) tax on an inherited, gifted, or sold real estate transfer.\n *\n * Purpose: Taxes the gain as if earned evenly over the holding period, then scales back up.\n * `grossTransferValue` must be the official appraised value (ราคาประเมินทุนทรัพย์, Section 49 bis),\n * not the actual sale price. `holdingYears` is a positive safe integer; callers round part-years\n * up, and more than 10 years counts as 10.\n * Math: years = min(holdingYears, 10); expense = gross * 50% for inheritance/gift, otherwise\n * gross * table(years) (never combined); net = gross - expense; tax = years * bracketTax(net / years)\n * on the schedule without a 0% tier (5% from the first baht), evaluated exactly as bracketTax of\n * `net` against bounds multiplied by `years` (no per-year rounding); tax is clamped at 20% of gross\n * (which the 35% top rate on at most 50% net can never reach). `annualGain` and `annualTax` are\n * rounded for display.\n * Units: Inputs and outputs in THB major units; arithmetic in integer satang.\n * Throws: TaxValidationError for a negative/non-finite value, a non-positive/non-safe-integer\n * holding period, or an unknown `acquisitionType`.\n *\n * @statute Thailand Revenue Code Section 48(4), Section 42(5), Section 49 bis.\n */\nexport function calculateRealEstateTransfer(\n\tinput: ThRealEstateTransferInput | undefined,\n): ThRealEstateBreakdown {\n\tif (input === undefined) return EMPTY_BREAKDOWN\n\tvalidateMonetaryAmount(input.grossTransferValue, ThSpecialRegimeField.RealEstateGrossValue)\n\tvalidateSafeIntegerCount(input.holdingYears, ThSpecialRegimeField.RealEstateHoldingYears, 1)\n\tconst acquisitionType = resolveChoice(\n\t\tinput.acquisitionType,\n\t\tThRealEstateAcquisitionType,\n\t\tThRealEstateAcquisitionType.Commercial,\n\t\tThSpecialRegimeField.RealEstateAcquisitionType,\n\t)\n\n\tconst years = effectiveHoldingYears(input.holdingYears)\n\tconst { basis, rate: expenseRate } = resolveRealEstateExpense(acquisitionType, years)\n\tconst gross = toSatang(input.grossTransferValue, ThSpecialRegimeField.RealEstateGrossValue)\n\tconst expense = applyPercentage(gross, expenseRate)\n\tconst netGain = gross - expense\n\tconst evaluated = evaluateThBracketSet(\n\t\tfromSatang(netGain),\n\t\tfromSatang(netGain),\n\t\tscaleBracketsByYears(years),\n\t)\n\tconst taxBeforeCap = toSatang(evaluated.totalTax)\n\tconst capped = applyRealEstateCap(taxBeforeCap, gross)\n\treturn {\n\t\tgrossTransferValue: input.grossTransferValue,\n\t\tacquisitionType,\n\t\texpenseBasis: basis,\n\t\trequestedHoldingYears: input.holdingYears,\n\t\teffectiveHoldingYears: years,\n\t\texpenseRate,\n\t\texpense: fromSatang(expense),\n\t\tnetGain: fromSatang(netGain),\n\t\tannualGain: fromSatang(roundHalfUp(netGain / years)),\n\t\tbrackets: evaluated.brackets,\n\t\tannualTax: fromSatang(roundHalfUp(taxBeforeCap / years)),\n\t\ttaxBeforeCap: fromSatang(taxBeforeCap),\n\t\ttaxCap: fromSatang(capped.taxCap),\n\t\twasClamped: capped.wasClamped,\n\t\ttax: fromSatang(capped.tax),\n\t}\n}\n","import { validateMonetaryAmount } from \"../../core/money\"\nimport { ThAssessableField, type ThAssessableIncomeInput } from \"../types\"\nimport { fromSatang, toSatang } from \"../units\"\n\n/** Special-regime amounts folded into the regular schedule. Units: THB major units. */\nexport interface ThSpecialIncome {\n\t/** Ineligible Section 48(5) lump sum added to Section 40(1). */\n\treadonly lumpSum: number\n\t/** Section 40(4) income elected `\"include\"` (final-withheld sources excluded). */\n\treadonly section40_4: number\n\t/** Section 47 bis dividend tax credit, added as gross-up to Section 40(4). */\n\treadonly dividendCredit: number\n}\n\n/**\n * Assembles the income that flows through the regular progressive schedule.\n *\n * Math: Section 40(1) = declared Section 40(1) + ineligible Section 48(5) lump sum;\n * Section 40(4) = included Section 40(4) income (final-withheld sources excluded)\n * + Section 47 bis gross-up. Other categories pass through untouched.\n * Units: THB major units; the Section 40(1) sum is performed in integer satang.\n * Throws: TaxValidationError if the declared Section 40(1) is negative or non-finite.\n *\n * @param income Declared Section 40 income.\n * @param special Lump sum, included Section 40(4) income, and dividend gross-up.\n * @statute Thailand Revenue Code Sections 40(1), 40(4), 47 bis, 48(5).\n */\nexport function buildRegularIncome(\n\tincome: ThAssessableIncomeInput | undefined,\n\tspecial: ThSpecialIncome,\n): ThAssessableIncomeInput {\n\tconst declared = income?.section40_1 ?? 0\n\tvalidateMonetaryAmount(declared, ThAssessableField.Section40_1)\n\tconst section40_1 =\n\t\ttoSatang(declared, ThAssessableField.Section40_1) +\n\t\ttoSatang(special.lumpSum, ThAssessableField.Section40_1)\n\tconst section40_4 =\n\t\ttoSatang(special.section40_4, ThAssessableField.Section40_4) +\n\t\ttoSatang(special.dividendCredit, ThAssessableField.Section40_4)\n\treturn {\n\t\t...income,\n\t\tsection40_1: fromSatang(section40_1),\n\t\tsection40_4: fromSatang(section40_4),\n\t}\n}\n","import {\n\tapplyPercentage,\n\tmultiplySafeInteger,\n\tvalidateMonetaryAmount,\n\tvalidateSafeIntegerCount,\n} from \"../../core/money\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport {\n\ttype ThSeparateSheetBreakdown,\n\ttype ThSeparateSheetInput,\n\tThSeparateSheetPaymentType,\n\tThSpecialRegimeField,\n} from \"../types\"\nimport { fromSatang, toSatang } from \"../units\"\nimport { resolveChoice } from \"../validate\"\nimport { evaluateThBracketSet } from \"./bracket-eval\"\n\nconst EMPTY_INPUT: ThSeparateSheetInput = { amount: 0, yearsOfService: 0, lastDailyWage: 0 }\n\n/**\n * Calculates the Section 48(5) separate sheet for one-time severance/retirement lump sums.\n *\n * Purpose: Applies the severance exemption, then taxes an eligible remainder on its own schedule\n * or returns the ineligible remainder for regular treatment.\n * Math: For `paymentType` severance (the default) at ANY length of service:\n * exemption = min(amount, 400 * lastDailyWage, 600,000); `\"retirement\"` payments get none.\n * remainder = amount - exemption. With yearsOfService >= 5: fixed = min(7,000 * years, remainder);\n * percentage expense = 50% of (remainder - fixed), half-up; net = remainder - fixed - percentage,\n * taxed on the separate schedule (5%..35%, no 0% tier). With fewer than 5 years only the remainder\n * is returned in `addedToSection40_1` for the regular computation.\n * Units: Inputs and outputs in THB major units; arithmetic in integer satang.\n * Throws: TaxValidationError for negative/non-finite money, a non-safe-integer/negative `yearsOfService`,\n * an unknown `paymentType`, or a wage/years value whose scaled satang amount exceeds safe integers.\n *\n * @statute Thailand Revenue Code Section 48(5); Section 42(1) (severance exemption).\n */\nexport function calculateSeparateSheet(\n\tinput: ThSeparateSheetInput = EMPTY_INPUT,\n): ThSeparateSheetBreakdown {\n\tconst rules = TH_2026_PARAMETERS.separateSheet\n\tvalidateMonetaryAmount(input.amount, ThSpecialRegimeField.SeparateSheetAmount)\n\tvalidateMonetaryAmount(input.lastDailyWage, ThSpecialRegimeField.SeparateSheetLastDailyWage)\n\tvalidateSafeIntegerCount(input.yearsOfService, ThSpecialRegimeField.SeparateSheetYears)\n\tconst paymentType = resolveChoice(\n\t\tinput.paymentType,\n\t\tThSeparateSheetPaymentType,\n\t\tThSeparateSheetPaymentType.Severance,\n\t\tThSpecialRegimeField.SeparateSheetPaymentType,\n\t)\n\n\tconst years = input.yearsOfService\n\tconst isEligible = years >= rules.minYearsOfService\n\tconst amountSatang = toSatang(input.amount, ThSpecialRegimeField.SeparateSheetAmount)\n\tconst wageSatang = toSatang(input.lastDailyWage, ThSpecialRegimeField.SeparateSheetLastDailyWage)\n\tconst isSeverance = paymentType === ThSeparateSheetPaymentType.Severance\n\tconst wageCap = isSeverance\n\t\t? multiplySafeInteger(\n\t\t\t\twageSatang,\n\t\t\t\trules.exemptionWageDays,\n\t\t\t\tThSpecialRegimeField.SeparateSheetLastDailyWage,\n\t\t\t)\n\t\t: 0\n\tconst exemption = isSeverance\n\t\t? Math.min(amountSatang, wageCap, toSatang(rules.exemptionCeiling))\n\t\t: 0\n\tconst remainder = amountSatang - exemption\n\tconst fixedCap = multiplySafeInteger(\n\t\ttoSatang(rules.expensePerYearOfService),\n\t\tyears,\n\t\tThSpecialRegimeField.SeparateSheetYears,\n\t)\n\tconst fixed = isEligible ? Math.min(remainder, fixedCap) : 0\n\tconst percentage = isEligible ? applyPercentage(remainder - fixed, rules.remainderExpenseRate) : 0\n\tconst net = fromSatang(isEligible ? remainder - fixed - percentage : 0)\n\n\tconst evaluated = evaluateThBracketSet(net, net, rules.brackets)\n\treturn {\n\t\tamount: input.amount,\n\t\tyearsOfService: years,\n\t\tlastDailyWage: input.lastDailyWage,\n\t\tisEligible,\n\t\taddedToSection40_1: isEligible ? 0 : fromSatang(remainder),\n\t\twageBasedCap: fromSatang(wageCap),\n\t\texemptionCeiling: rules.exemptionCeiling,\n\t\tpaymentType,\n\t\texemption: fromSatang(exemption),\n\t\tremainder: fromSatang(remainder),\n\t\tfixedExpense: fromSatang(fixed),\n\t\tpercentageExpense: fromSatang(percentage),\n\t\tnetTaxable: net,\n\t\tbrackets: evaluated.brackets,\n\t\ttax: evaluated.totalTax,\n\t}\n}\n","import {\n\ttype ThAssessableIncomeInput,\n\tThBracketField,\n\ttype ThDividendTaxCreditBreakdown,\n\ttype ThFinalWithholdingBreakdown,\n\ttype ThRealEstateBreakdown,\n\ttype ThSeparateSheetBreakdown,\n\ttype ThTaxInput,\n} from \"../types\"\nimport { sumThAmounts } from \"../units\"\nimport { calculateDividendTaxCredit } from \"./dividend-credit\"\nimport { resolveSection40_4 } from \"./final-withholding\"\nimport { calculateRealEstateTransfer } from \"./real-estate\"\nimport { buildRegularIncome } from \"./regular-income\"\nimport { calculateSeparateSheet } from \"./separate-sheet\"\n\n/** Outcome of every special regime plus the income left for the regular schedule. */\nexport interface ThSpecialRegimes {\n\t/** Income flowing through the regular progressive schedule (Section 40(1)–(8)). */\n\treadonly regularIncome: ThAssessableIncomeInput\n\t/** Gross amounts taxed outside the regular schedule: final 40(4), eligible 48(5), 48(4). */\n\treadonly grossOutsideRegular: number\n\treadonly finalWithholding: ThFinalWithholdingBreakdown\n\treadonly dividendTaxCredit: ThDividendTaxCreditBreakdown\n\treadonly separateSheet: ThSeparateSheetBreakdown\n\treadonly realEstate: ThRealEstateBreakdown\n}\n\n/**\n * Resolves Section 40(4) elections, the Section 47 bis credit, the Section 48(5) separate sheet,\n * and the Section 48(4) real estate transfer, then assembles the remaining regular income.\n *\n * Math: Regular Section 40(4) = included sources + dividend gross-up; ineligible lump sum joins\n * Section 40(1). Gross outside the regular schedule = final-elected 40(4) + eligible lump sum (or, if\n * ineligible, its severance exemption) + real estate gross transfer value.\n * Units: THB major units; sums performed in integer satang.\n * Throws: TaxValidationError for any invalid special-regime input.\n *\n * @statute Thailand Revenue Code Sections 40(4), 47 bis, 48(4), 48(5).\n */\nexport function resolveSpecialRegimes(input: ThTaxInput): ThSpecialRegimes {\n\tconst section40_4 = resolveSection40_4(input.income?.section40_4)\n\tconst dividendTaxCredit = calculateDividendTaxCredit(\n\t\tsection40_4.includedDividends,\n\t\tsection40_4.corpTaxRate,\n\t)\n\tconst separateSheet = calculateSeparateSheet(input.separateSheet)\n\tconst realEstate = calculateRealEstateTransfer(input.realEstateTransfer)\n\treturn {\n\t\tregularIncome: buildRegularIncome(input.income, {\n\t\t\tlumpSum: separateSheet.addedToSection40_1,\n\t\t\tsection40_4: section40_4.includedAmount,\n\t\t\tdividendCredit: dividendTaxCredit.credit,\n\t\t}),\n\t\tgrossOutsideRegular: sumThAmounts(\n\t\t\t[\n\t\t\t\tsection40_4.finalWithholding.totalAmount,\n\t\t\t\tseparateSheet.isEligible ? separateSheet.amount : separateSheet.exemption,\n\t\t\t\trealEstate.grossTransferValue,\n\t\t\t],\n\t\t\tThBracketField.GrossIncome,\n\t\t),\n\t\tfinalWithholding: section40_4.finalWithholding,\n\t\tdividendTaxCredit,\n\t\tseparateSheet,\n\t\trealEstate,\n\t}\n}\n","import { TaxValidationError, thb, validateMonetaryAmount } from \"../../core/money\"\nimport type { TaxResult } from \"../../core/system\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport { ThBracketField, type ThBreakdown, ThTaxField, type ThTaxInput } from \"../types\"\nimport { sumThAmounts } from \"../units\"\nimport { calculateAllowances } from \"./allowances\"\nimport { calculateAssessableIncome } from \"./assessable\"\nimport { evaluateThBrackets } from \"./bracket-eval\"\nimport { combineThLiability } from \"./combine\"\nimport { calculateExpenseDeductions } from \"./expenses\"\nimport { calculateMinimumGrossTax } from \"./minimum-tax\"\nimport { resolveSpecialRegimes } from \"./special-regimes\"\n\nfunction validateYear(year?: number): void {\n\tif (year !== undefined && year !== TH_2026_PARAMETERS.taxYear) {\n\t\tthrow new TaxValidationError(\n\t\t\tThTaxField.TaxYear,\n\t\t\tyear,\n\t\t\t\"Unsupported tax year; only 2026 is currently supported\",\n\t\t)\n\t}\n}\n\n/**\n * Executes full Thailand PIT calculation pipeline for ภ.ง.ด.90/91.\n *\n * @statute Thailand Revenue Code Sections 40, 42 bis, 47, 47 bis, 48(1)–(5).\n */\nexport function runThTaxPipeline(input: ThTaxInput = {}): TaxResult<\"THB\", ThBreakdown> {\n\tvalidateYear(input.taxYear)\n\tconst filingStatus = input.filingStatus ?? \"single\"\n\tconst withholding = input.withholdingTax ?? 0\n\tvalidateMonetaryAmount(withholding, ThTaxField.WithholdingTax)\n\n\tconst special = resolveSpecialRegimes(input)\n\tconst income = special.regularIncome\n\n\tconst assessable = calculateAssessableIncome(income)\n\tconst expenses = calculateExpenseDeductions(income, assessable)\n\tconst netIncomeAfterExpenses = Math.max(0, assessable.total - expenses.total)\n\n\tconst allowancesRes = calculateAllowances(\n\t\tinput.allowances,\n\t\tfilingStatus,\n\t\tassessable.total,\n\t\texpenses.total,\n\t\tassessable.section40_1,\n\t)\n\n\tconst evalBrackets = evaluateThBrackets(allowancesRes.netTaxableIncome, assessable.total)\n\tconst progressiveTax = evalBrackets.totalTax\n\n\tconst minimumGrossTax = calculateMinimumGrossTax(\n\t\tassessable.grossNonEmploymentIncome,\n\t\tprogressiveTax,\n\t)\n\n\tconst regularTax = minimumGrossTax.wasApplied ? minimumGrossTax.calculatedTax : progressiveTax\n\tconst combined = combineThLiability({\n\t\tregularTax,\n\t\tseparateSheetTax: special.separateSheet.tax,\n\t\trealEstateTax: special.realEstate.tax,\n\t\tfinalWithholdingTax: special.finalWithholding.totalTax,\n\t\tdividendTaxCredit: special.dividendTaxCredit.credit,\n\t\twithholdingTax: withholding,\n\t})\n\tconst grossIncome = sumThAmounts(\n\t\t[assessable.total, special.grossOutsideRegular],\n\t\tThBracketField.GrossIncome,\n\t)\n\tconst effectiveRate = grossIncome > 0 ? combined.totalCombinedTax / grossIncome : 0\n\n\tconst breakdown: ThBreakdown = {\n\t\tassessableIncome: assessable,\n\t\texpenseDeductions: expenses,\n\t\tnetIncomeAfterExpenses,\n\t\tallowances: allowancesRes.breakdown,\n\t\tnetTaxableIncome: allowancesRes.netTaxableIncome,\n\t\tprogressiveTax,\n\t\tprogressiveBrackets: evalBrackets.brackets,\n\t\tminimumGrossTax,\n\t\twithholdingTax: withholding,\n\t\tnetTaxPayable: combined.netTaxPayable,\n\t\tregularTax,\n\t\tseparateSheet: special.separateSheet,\n\t\trealEstate: special.realEstate,\n\t\tfinalWithholding: special.finalWithholding,\n\t\tdividendTaxCredit: special.dividendTaxCredit,\n\t\ttotalCombinedTax: combined.totalCombinedTax,\n\t}\n\n\treturn {\n\t\tjurisdiction: \"TH\",\n\t\ttaxYear: TH_2026_PARAMETERS.taxYear,\n\t\tcurrency: TH_2026_PARAMETERS.currency,\n\t\tgrossIncome: thb(grossIncome, ThBracketField.GrossIncome),\n\t\ttaxableIncome: thb(allowancesRes.netTaxableIncome, ThBracketField.TaxableIncome),\n\t\ttotalTax: thb(combined.totalCombinedTax, ThTaxField.TotalTax),\n\t\teffectiveRate,\n\t\tmarginalRate: evalBrackets.brackets.marginalRate,\n\t\tnetTaxPayable: thb(combined.netTaxPayable, ThTaxField.NetTaxPayable, {\n\t\t\tallowNegative: true,\n\t\t}),\n\t\tbreakdown,\n\t}\n}\n","import type { TaxResult } from \"../core/system\"\nimport { runThTaxPipeline } from \"./stages/pipeline\"\nimport type { ThBreakdown, ThTaxInput } from \"./types\"\n\n/**\n * Calculates Thailand Personal Income Tax (ภ.ง.ด.90/91) under Revenue Code.\n *\n * Defaults to tax year 2026 and single filing status.\n *\n * @param input Taxpayer assessable income, allowances, filing status, and withholding.\n * @returns Fully typed, immutable TaxResult with statutory breakdown.\n *\n * @statute Thailand Revenue Code Sections 40, 42 bis, 47, 48(1)–(2).\n */\nexport function calculateThTax(input: ThTaxInput = {}): TaxResult<\"THB\", ThBreakdown> {\n\treturn runThTaxPipeline(input)\n}\n","/**\n * Tally bases sim-core's `tally` Measure reads.\n *\n * Purpose: Distinguishes income Flows from contribution Flows at the tally.\n * Math: No arithmetic; a discriminator only.\n * Units: Dimensionless.\n * Throws: Nothing.\n * @statute Thailand Revenue Code Sections 40(1)–(8) (gross) and 47 (funded).\n */\nexport const ThLevyTallyBasis = {\n\t/** Sums income Flows. */\n\tGross: \"gross\",\n\t/** Sums contribution Flows. */\n\tFunded: \"funded\",\n} as const\n\n/**\n * Union of {@link ThLevyTallyBasis} values.\n *\n * Purpose: Narrow type for the tally basis of an emitted Measure.\n * Math: No arithmetic; a discriminator only.\n * Units: Dimensionless.\n * Throws: Nothing.\n * @statute Thailand Revenue Code Sections 40(1)–(8) (gross) and 47 (funded).\n */\nexport type ThLevyTallyBasis = (typeof ThLevyTallyBasis)[keyof typeof ThLevyTallyBasis]\n\n/**\n * Rounding modes sim-core's `scale` Measure accepts.\n *\n * Purpose: Names the three rounding policies embedded in emitted Measures.\n * Math: `Floor` rounds down, `Ceil` up, and `HalfUp` sends ties away from zero.\n * Units: Dimensionless.\n * Throws: Nothing.\n * @statute Thailand Revenue Code Sections 47 bis, 48(1), 48(2).\n */\nexport const ThLevyRounding = {\n\tFloor: \"floor\",\n\tHalfUp: \"halfUp\",\n\tCeil: \"ceil\",\n} as const\n\n/**\n * Union of {@link ThLevyRounding} values.\n *\n * Purpose: Narrow type for a `scale` Measure's rounding policy.\n * Math: No arithmetic; names `floor`, `ceil`, or ties-away-from-zero `halfUp`.\n * Units: Dimensionless.\n * Throws: Nothing.\n * @statute Thailand Revenue Code Sections 47 bis, 48(1), 48(2).\n */\nexport type ThLevyRounding = (typeof ThLevyRounding)[keyof typeof ThLevyRounding]\n\n/**\n * Recurring calendar date without a year, as sim-core's `MonthDay`.\n *\n * Purpose: Fixes the Levy period and settlement dates at emit time.\n * Math: No arithmetic; the date recurs every year and is never February 29.\n * Units: Month number 1–12 and day number; not a timestamp.\n * Throws: Nothing; sim-core's `check()` validates the pair.\n * @statute Thailand Revenue Code Section 56 (annual return filing period).\n */\nexport interface ThLevyMonthDay {\n\t/** Month number, 1 to 12. */\n\treadonly month: number\n\t/** Day number within the month. */\n\treadonly day: number\n}\n\n/**\n * One node of the emitted rule graph, restricted to the operation set sim-core evaluates.\n *\n * Purpose: Closed union that makes any operation outside sim-core's set a type error.\n * Math: `const`, `tally`, binary arithmetic, exact `scale`, and `pick` — nothing else.\n * Units: `value` and Measure totals are integer satang; `num`/`den` are dimensionless.\n * Throws: Nothing at construction; {@link ThLevyGraph.build} checks operands.\n * @statute Thailand Revenue Code Sections 40, 42 bis, 47, 47 bis, 48(1)–(2).\n */\nexport type ThLevyMeasure =\n\t| { readonly id: string; readonly op: \"const\"; readonly value: number }\n\t| {\n\t\t\treadonly id: string\n\t\t\treadonly op: \"tally\"\n\t\t\treadonly label: string\n\t\t\treadonly basis: ThLevyTallyBasis\n\t }\n\t| {\n\t\t\treadonly id: string\n\t\t\treadonly op: \"add\" | \"sub\" | \"min\" | \"max\"\n\t\t\treadonly a: string\n\t\t\treadonly b: string\n\t }\n\t| {\n\t\t\treadonly id: string\n\t\t\treadonly op: \"scale\"\n\t\t\treadonly of: string\n\t\t\treadonly num: number\n\t\t\treadonly den: number\n\t\t\treadonly rounding: ThLevyRounding\n\t }\n\t| {\n\t\t\treadonly id: string\n\t\t\treadonly op: \"pick\"\n\t\t\treadonly a: string\n\t\t\treadonly b: string\n\t\t\treadonly then: string\n\t\t\treadonly else: string\n\t }\n\n/**\n * A Thailand Personal Income Tax rule set in sim-core's published `Levy` shape.\n *\n * Purpose: The caller hands this to sim-core, which evaluates it from Flow tallies without\n * importing this package. The taxpayer profile, elections, and 2026 statutory parameters are\n * frozen at emit time, so one Levy describes one taxpayer.\n * Math: Every Measure is exact integer satang arithmetic over the closed sim-core operation set.\n * Units: All monetary Measure values are integer satang (THB minor units).\n * Throws: Nothing at construction; {@link ThLevyGraph.build} validates the graph.\n * @statute Thailand Revenue Code Sections 40, 42 bis, 47, 47 bis, 48(1)–(2).\n */\nexport interface ThLevy {\n\t/** Stable Levy id echoed by sim-core's projection report. */\n\treadonly id: string\n\t/** Calendar date on which each Settlement period starts. */\n\treadonly period: ThLevyMonthDay\n\t/** First such date strictly after the period's last day. */\n\treadonly settlement: ThLevyMonthDay\n\t/** Rule graph, in dependency-friendly emit order. */\n\treadonly measures: readonly ThLevyMeasure[]\n\t/** Id of the Measure holding the amount owed before Prepayment. */\n\treadonly due: string\n}\n","import { rateFraction } from \"../../core/money/ratio\"\nimport type { ThLevyGraph } from \"./graph\"\nimport { ThLevyRounding } from \"./graph-types\"\n\n/**\n * Emits a satang sum of Measure ids; an empty list becomes a zero const.\n *\n * Purpose: Keeps statutory totals readable without a fold at every call site.\n * Math: Left-associative `add`; empty input yields `const 0`. When `name` is given, a final\n * zero addition exposes the running total under an audited id (sim-core has no rename op).\n * Units: Integer satang in and out.\n * Throws: TaxValidationError if the graph rejects an id or constant.\n * @statute Jurisdiction-neutral graph helper; no statutory citation.\n */\nexport function sumMeasures(graph: ThLevyGraph, ids: readonly string[], name?: string): string {\n\tconst [first, ...rest] = ids\n\tlet total = first ?? graph.constant(0)\n\tfor (const id of rest) total = graph.add(total, id)\n\treturn name === undefined ? total : graph.add(total, graph.constant(0), name)\n}\n\n/**\n * Emits `of` rounded down to a whole baht while keeping satang units.\n *\n * Purpose: Reproduces the calculator's whole-baht `Math.floor` steps exactly.\n * Math: `100 * floor(x / 100)` via two integer scales.\n * Units: Integer satang in and out.\n * Throws: Nothing; `of` must name an emitted Measure.\n * @statute Thailand Revenue Code Section 42 bis (whole-baht expense rounding).\n */\nexport function flooredToBaht(graph: ThLevyGraph, of: string): string {\n\treturn graph.scale(graph.scale(of, 1, 100, ThLevyRounding.Floor), 100, 1, ThLevyRounding.Floor)\n}\n\n/**\n * Emits `floor(income * rate)`, rounded down to a whole baht.\n *\n * Purpose: Mirrors the calculator's `Math.floor(major * rate)` expense deduction.\n * Math: `100 * floor(x * num / (den * 100))`.\n * Units: Integer satang in and out; `rate` is dimensionless.\n * Throws: TaxValidationError if `rate` is outside [0, 1].\n * @statute Thailand Revenue Code Sections 42 bis, 42 ter, 43–46.\n */\nexport function rateFlooredToBaht(graph: ThLevyGraph, of: string, rate: number): string {\n\tconst { num, den } = rateFraction(rate)\n\treturn flooredToBaht(graph, graph.scale(of, num, den, ThLevyRounding.Floor))\n}\n\n/**\n * Emits `min(of, ceiling)`, the shape `clampCap` uses for an absolute statutory ceiling.\n *\n * Purpose: Reproduces the absolute-ceiling branch of `clampCap`.\n * Math: `min(x, ceiling)`.\n * Units: Integer satang in and out.\n * Throws: TaxValidationError if the ceiling is not safe integer satang.\n * @statute Thailand Revenue Code Section 47 (allowance ceilings).\n */\nexport function capAt(graph: ThLevyGraph, of: string, ceilingSatang: number): string {\n\treturn graph.min(of, graph.constant(ceilingSatang))\n}\n\n/**\n * Emits `min(of * rate, ceiling)` with half-up satang rounding, the shape `resolveCap` computes.\n *\n * Purpose: Percentage-of-income retirement caps round to the nearest satang, not to a baht.\n * Math: `min(round_half_up(x * num / den), ceiling)`.\n * Units: Integer satang in and out; `rate` is dimensionless.\n * Throws: TaxValidationError if `rate` is outside [0, 1] or the ceiling is invalid.\n * @statute Thailand Revenue Code Section 47; Ministerial Regulations No. 126, 390.\n */\nexport function rateCapAt(\n\tgraph: ThLevyGraph,\n\tof: string,\n\trate: number,\n\tceilingSatang: number,\n): string {\n\tconst { num, den } = rateFraction(rate)\n\treturn capAt(graph, graph.scale(of, num, den, ThLevyRounding.HalfUp), ceilingSatang)\n}\n","/**\n * Diagnostic field identifiers for values passed to the Levy measure builder.\n *\n * Purpose: Names the invalid input on every emit-time validation error.\n * Math: No arithmetic; diagnostic paths only.\n * Units: Dimensionless field paths.\n * Throws: Nothing; consumed by `TaxValidationError`.\n * @statute Jurisdiction-neutral input bound (AGENTS rule 8); no statutory citation.\n */\nexport enum ThLevyField {\n\tMeasureId = \"levy.measure.id\",\n\tMeasureDue = \"levy.measure.due\",\n\tConstValue = \"levy.measure.const.value\",\n\tScaleNum = \"levy.measure.scale.num\",\n\tScaleDen = \"levy.measure.scale.den\",\n}\n\n/**\n * Audited Measure ids a TH 2026 Levy exposes by name.\n *\n * Purpose: Lets callers and tests compare each intermediate statutory figure without knowing\n * the emit order.\n * Math: No arithmetic; Measure ids only.\n * Units: The referenced Measures are integer satang.\n * Throws: Nothing.\n * @statute Thailand Revenue Code Sections 40, 42 bis, 47, 47 bis, 48(1)–(2).\n */\nexport enum ThLevyMeasureId {\n\tAssessableTotal = \"assessable.total\",\n\tGrossNonEmployment = \"assessable.non_employment\",\n\tExpensesTotal = \"expenses.total\",\n\tAllowancesFixed = \"allowances.fixed\",\n\tRetirementCluster = \"allowances.retirement_cluster\",\n\tThaiEsg = \"allowances.thai_esg\",\n\tDonations = \"allowances.donations\",\n\tAllowancesTotal = \"allowances.total\",\n\tNetTaxableIncome = \"taxable.income\",\n\tProgressiveTax = \"tax.progressive\",\n\tMinimumGrossTax = \"tax.minimum\",\n\tRegularTax = \"tax.regular\",\n\tFinalWithholdingTax = \"tax.final_withholding\",\n\tDividendCredit = \"credit.dividend\",\n\tTotalTax = \"tax.total\",\n\tNetTaxPayable = \"tax.net_payable\",\n}\n","import { multiplySafeInteger, validateMonetaryAmount } from \"../../core/money\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport { ThAllowanceField } from \"../types\"\nimport { toSatang } from \"../units\"\nimport type { ThLevyGraph } from \"./graph\"\nimport { rateFlooredToBaht } from \"./graph-rates\"\nimport { ThLevyMeasureId } from \"./measure-ids\"\n\n/**\n * Donation allowance total and the net taxable income it leaves.\n *\n * Purpose: Names the donation total and the bracket schedule's input.\n * Math: No arithmetic; ids only.\n * Units: The referenced Measures are integer satang.\n * Throws: Nothing.\n * @statute Thailand Revenue Code Section 47(7); Royal Decrees No. 428, 558.\n */\nexport interface ThDonationAllowanceMeasures {\n\t/** Allowed 2x plus 1x donations. */\n\treadonly total: string\n\t/** `max(0, net after donations)`, the bracket schedule's input. */\n\treadonly netTaxable: string\n}\n\n/**\n * Emits the Section 47(7) donation allowances against the net income before donations.\n *\n * Purpose: Reproduces the 2x-then-1x donation caps as Measures.\n * Math: 2x donations are doubled, then `min(doubled, floor(net * 10%))`; 1x donations are\n * `min(amount, floor(remaining net * 10%))`. Taxable income is `max(0, remaining - 1x allowed)`.\n * Units: Profile amounts are THB major units; Measures are integer satang.\n * Throws: TaxValidationError if a donation amount is invalid or the doubled amount overflows.\n *\n * @param graph Measure graph being built.\n * @param netBefore `max(0, assessable - expenses - non-donation allowances)` Measure.\n * @param requested2x Requested 2x-qualified donation in THB.\n * @param requested1x Requested general donation in THB.\n * @statute Thailand Revenue Code Section 47(7); Royal Decrees No. 428, 558.\n */\nexport function addDonationAllowances(\n\tgraph: ThLevyGraph,\n\tnetBefore: string,\n\trequested2x: number | undefined,\n\trequested1x: number | undefined,\n): ThDonationAllowanceMeasures {\n\tconst p = TH_2026_PARAMETERS.allowances.donations\n\tconst raw2x = requested2x ?? 0\n\tconst raw1x = requested1x ?? 0\n\tvalidateMonetaryAmount(raw2x, ThAllowanceField.Donation2x)\n\tvalidateMonetaryAmount(raw1x, ThAllowanceField.Donation1x)\n\tconst doubled = multiplySafeInteger(\n\t\ttoSatang(raw2x, ThAllowanceField.Donation2x),\n\t\tp.donation2xMultiplier,\n\t\tThAllowanceField.Donation2x,\n\t)\n\tconst net = graph.max(netBefore, graph.constant(0))\n\tconst cap2x = rateFlooredToBaht(graph, net, p.donation2xMaxRate)\n\tconst allowed2x = graph.min(graph.constant(doubled), cap2x)\n\tconst remaining = graph.max(graph.sub(net, allowed2x), graph.constant(0))\n\tconst cap1x = rateFlooredToBaht(graph, remaining, p.donation1xMaxRate)\n\tconst allowed1x = graph.min(graph.constant(toSatang(raw1x, ThAllowanceField.Donation1x)), cap1x)\n\tconst total = graph.add(allowed2x, allowed1x, ThLevyMeasureId.Donations)\n\tconst netTaxable = graph.max(\n\t\tgraph.sub(remaining, allowed1x),\n\t\tgraph.constant(0),\n\t\tThLevyMeasureId.NetTaxableIncome,\n\t)\n\treturn { total, netTaxable }\n}\n","import { calculateFamilyAllowances } from \"../stages/allowances-family\"\nimport { calculateInsuranceAllowances } from \"../stages/allowances-insurance\"\nimport type { ThFilingStatus } from \"../types\"\nimport { toSatang } from \"../units\"\nimport type { ThLevyGraph } from \"./graph\"\nimport { ThLevyMeasureId } from \"./measure-ids\"\nimport type { ThLevyAllowances } from \"./profile-types\"\n\n/**\n * Computes the fixed allowance total frozen into the Levy.\n *\n * Purpose: Reuses the calculator's own family and insurance stages, so the const Measure cannot\n * drift from the rules it mirrors.\n * Math: Sum of the allowed personal, spouse, children, parents, disabled, social security,\n * life/health insurance, parent health, spouse life, and mortgage interest amounts.\n * Units: THB major units in, integer satang out.\n * Throws: TaxValidationError if any profile allowance amount is invalid.\n *\n * @param allowances Fixed profile allowances.\n * @param status Filing status governing spouse and parent eligibility.\n * @statute Thailand Revenue Code Section 47(1)(a)–(j).\n */\nexport function fixedAllowanceSatang(\n\tallowances: ThLevyAllowances | undefined,\n\tstatus: ThFilingStatus,\n): number {\n\tconst family = calculateFamilyAllowances(allowances, status)\n\tconst insurance = calculateInsuranceAllowances(allowances, status)\n\tconst allowed = [\n\t\tfamily.personal.allowed,\n\t\tfamily.spouse.allowed,\n\t\tfamily.children.allowed,\n\t\tfamily.parents.allowed,\n\t\tfamily.disabled.allowed,\n\t\tinsurance.socialSecurity.allowed,\n\t\tinsurance.lifeInsurance.allowed,\n\t\tinsurance.parentHealthInsurance.allowed,\n\t\tinsurance.spouseLifeInsurance.allowed,\n\t\tinsurance.mortgageInterest.allowed,\n\t]\n\treturn allowed.reduce((sum, value) => sum + toSatang(value), 0)\n}\n\n/**\n * Emits the Section 47 fixed allowance total as one const Measure.\n *\n * Purpose: Freezes the profile-only allowances into a single named Measure.\n * Math: `const(fixedAllowanceSatang(...))`.\n * Units: Integer satang.\n * Throws: TaxValidationError if a profile allowance amount is invalid.\n *\n * @param graph Measure graph being built.\n * @param allowances Fixed profile allowances.\n * @param status Filing status governing eligibility.\n * @statute Thailand Revenue Code Section 47(1)(a)–(j).\n */\nexport function addFixedAllowances(\n\tgraph: ThLevyGraph,\n\tallowances: ThLevyAllowances | undefined,\n\tstatus: ThFilingStatus,\n): string {\n\treturn graph.constant(fixedAllowanceSatang(allowances, status), ThLevyMeasureId.AllowancesFixed)\n}\n","/**\n * Tally labels a TH 2026 Levy reads from income Flows (`gross` basis).\n *\n * Purpose: One label per assessable income class under Section 40(1)–(8), with Section 40(4)\n * split per source because each source carries its own final-withholding election at emit time.\n * Math: No arithmetic; the label selects which tally a Measure reads.\n * Units: Dimensionless label string; the tallied amount is integer satang.\n * Throws: Nothing.\n * @statute Thailand Revenue Code Sections 40(1)–(8), 40(4), 50(2)(g).\n */\nexport const ThLevyIncomeLabel = {\n\t/** Section 40(1) employment income. */\n\tSection40_1: \"income.section40_1\",\n\t/** Section 40(2) service and hire-of-work income. */\n\tSection40_2: \"income.section40_2\",\n\t/** Section 40(3) royalties, goodwill, and copyright income. */\n\tSection40_3: \"income.section40_3\",\n\t/** Section 40(4) income with no source-specific election. */\n\tSection40_4Other: \"income.section40_4\",\n\t/** Section 40(4) interest, elected final or included at emit time. */\n\tSection40_4Interest: \"income.section40_4.interest\",\n\t/** Section 40(4) dividends, elected final or included at emit time. */\n\tSection40_4Dividends: \"income.section40_4.dividends\",\n\t/** Section 40(4) digital asset income, elected final or included at emit time. */\n\tSection40_4DigitalAssets: \"income.section40_4.digitalAssets\",\n\t/** Section 40(5) building rental income. */\n\tSection40_5Building: \"income.section40_5.building\",\n\t/** Section 40(5) agricultural land rental income. */\n\tSection40_5AgriculturalLand: \"income.section40_5.agriculturalLand\",\n\t/** Section 40(5) non-agricultural land rental income. */\n\tSection40_5NonAgriculturalLand: \"income.section40_5.nonAgriculturalLand\",\n\t/** Section 40(5) vehicle rental income. */\n\tSection40_5Vehicles: \"income.section40_5.vehicles\",\n\t/** Section 40(5) other rental income. */\n\tSection40_5Other: \"income.section40_5.other\",\n\t/** Section 40(6) medical profession income. */\n\tSection40_6Medical: \"income.section40_6.medical\",\n\t/** Section 40(6) law profession income. */\n\tSection40_6Law: \"income.section40_6.law\",\n\t/** Section 40(6) engineering profession income. */\n\tSection40_6Engineering: \"income.section40_6.engineering\",\n\t/** Section 40(6) architecture profession income. */\n\tSection40_6Architecture: \"income.section40_6.architecture\",\n\t/** Section 40(6) accounting profession income. */\n\tSection40_6Accounting: \"income.section40_6.accounting\",\n\t/** Section 40(6) fine arts profession income. */\n\tSection40_6FineArts: \"income.section40_6.fineArts\",\n\t/** Section 40(6) other liberal profession income. */\n\tSection40_6Other: \"income.section40_6.other\",\n\t/** Section 40(7) contracting income. */\n\tSection40_7: \"income.section40_7\",\n\t/** Section 40(8) general business and commerce income. */\n\tSection40_8General: \"income.section40_8.general\",\n\t/** Section 40(8) entertainer income (tiered expense rate). */\n\tSection40_8Entertainer: \"income.section40_8.entertainer\",\n} as const\n\n/**\n * Union of {@link ThLevyIncomeLabel} values.\n *\n * Purpose: Narrow type for the labels a caller may put on an income Flow.\n * Math: No arithmetic.\n * Units: Dimensionless label string.\n * Throws: Nothing.\n * @statute Thailand Revenue Code Sections 40(1)–(8), 40(4).\n */\nexport type ThLevyIncomeLabel = (typeof ThLevyIncomeLabel)[keyof typeof ThLevyIncomeLabel]\n\n/**\n * Tally labels a TH 2026 Levy reads from contribution Flows (`funded` basis).\n *\n * Purpose: One label per contribution-driven retirement allowance.\n * Math: No arithmetic; the label selects which tally a Measure reads.\n * Units: Dimensionless label string; the tallied amount is integer satang.\n * Throws: Nothing.\n * @statute Thailand Revenue Code Section 47; Ministerial Regulations No. 126, 390.\n */\nexport const ThLevyContributionLabel = {\n\t/** RMF contribution. */\n\tRmf: \"allowance.retirement.rmf\",\n\t/** SSF contribution. */\n\tSsf: \"allowance.retirement.ssf\",\n\t/** PVD contribution. */\n\tPvd: \"allowance.retirement.pvd\",\n\t/** GPF contribution. */\n\tGpf: \"allowance.retirement.gpf\",\n\t/** National Savings Fund contribution. */\n\tNsf: \"allowance.retirement.nsf\",\n\t/** Pension life insurance premium. */\n\tPensionLifeInsurance: \"allowance.retirement.pension_life\",\n\t/** ThaiESG fund contribution. */\n\tThaiEsg: \"allowance.thai_esg\",\n} as const\n\n/**\n * Union of {@link ThLevyContributionLabel} values.\n *\n * Purpose: Narrow type for the labels a caller may put on a contribution Flow.\n * Math: No arithmetic.\n * Units: Dimensionless label string.\n * Throws: Nothing.\n * @statute Thailand Revenue Code Section 47; Ministerial Regulations No. 126, 390.\n */\nexport type ThLevyContributionLabel =\n\t(typeof ThLevyContributionLabel)[keyof typeof ThLevyContributionLabel]\n\n/**\n * Every income tally label the emitter reads, in Section 40 order.\n *\n * Purpose: Ordered contract a caller can iterate to label or audit income Flows.\n * Math: No arithmetic.\n * Units: Dimensionless label strings.\n * Throws: Nothing.\n * @statute Thailand Revenue Code Sections 40(1)–(8), 40(4).\n */\nexport const TH_LEVY_INCOME_LABELS: readonly ThLevyIncomeLabel[] = [\n\tThLevyIncomeLabel.Section40_1,\n\tThLevyIncomeLabel.Section40_2,\n\tThLevyIncomeLabel.Section40_3,\n\tThLevyIncomeLabel.Section40_4Other,\n\tThLevyIncomeLabel.Section40_4Interest,\n\tThLevyIncomeLabel.Section40_4Dividends,\n\tThLevyIncomeLabel.Section40_4DigitalAssets,\n\tThLevyIncomeLabel.Section40_5Building,\n\tThLevyIncomeLabel.Section40_5AgriculturalLand,\n\tThLevyIncomeLabel.Section40_5NonAgriculturalLand,\n\tThLevyIncomeLabel.Section40_5Vehicles,\n\tThLevyIncomeLabel.Section40_5Other,\n\tThLevyIncomeLabel.Section40_6Medical,\n\tThLevyIncomeLabel.Section40_6Law,\n\tThLevyIncomeLabel.Section40_6Engineering,\n\tThLevyIncomeLabel.Section40_6Architecture,\n\tThLevyIncomeLabel.Section40_6Accounting,\n\tThLevyIncomeLabel.Section40_6FineArts,\n\tThLevyIncomeLabel.Section40_6Other,\n\tThLevyIncomeLabel.Section40_7,\n\tThLevyIncomeLabel.Section40_8General,\n\tThLevyIncomeLabel.Section40_8Entertainer,\n]\n\n/**\n * Every contribution tally label the emitter reads, in retirement-cluster order.\n *\n * Purpose: Ordered contract a caller can iterate to label or audit contribution Flows.\n * Math: No arithmetic.\n * Units: Dimensionless label strings.\n * Throws: Nothing.\n * @statute Thailand Revenue Code Section 47; Ministerial Regulations No. 126, 390.\n */\nexport const TH_LEVY_CONTRIBUTION_LABELS: readonly ThLevyContributionLabel[] = [\n\tThLevyContributionLabel.Rmf,\n\tThLevyContributionLabel.Ssf,\n\tThLevyContributionLabel.Pvd,\n\tThLevyContributionLabel.Gpf,\n\tThLevyContributionLabel.Nsf,\n\tThLevyContributionLabel.PensionLifeInsurance,\n\tThLevyContributionLabel.ThaiEsg,\n]\n","import { TH_2026_PARAMETERS } from \"../parameters\"\nimport { ThAllowanceField } from \"../types\"\nimport { toSatang } from \"../units\"\nimport type { ThLevyGraph } from \"./graph\"\nimport { rateCapAt, sumMeasures } from \"./graph-rates\"\nimport { ThLevyContributionLabel, ThLevyIncomeLabel } from \"./labels\"\nimport { ThLevyMeasureId } from \"./measure-ids\"\nimport type { ThLevyTallies } from \"./tallies\"\n\n/**\n * Retirement-cluster and ThaiESG allowance Measure ids.\n *\n * Purpose: Names the two audited retirement Measures for callers and tests.\n * Math: No arithmetic; ids only.\n * Units: The referenced Measures are integer satang.\n * Throws: Nothing.\n * @statute Thailand Revenue Code Section 47; Ministerial Regulations No. 126, 390.\n */\nexport interface ThRetirementAllowanceMeasures {\n\t/** Combined retirement cluster after the 500,000 THB ceiling. */\n\treadonly cluster: string\n\t/** ThaiESG allowance after its percentage and ceiling caps. */\n\treadonly thaiEsg: string\n}\n\n/**\n * Emits the Retirement Cluster and ThaiESG allowances from `funded` tallies.\n *\n * Purpose: Mirrors the percentage and ceiling caps of both retirement allowances.\n * Math: RMF/SSF/PVD/GPF and pension life insurance are capped at `min(income * rate, ceiling)`\n * (PVD/GPF on Section 40(1) wage, the rest on assessable income); NSF at its flat ceiling; the\n * six are then capped at the 500,000 THB cluster ceiling. ThaiESG is capped at\n * `min(assessable * 30%, 300,000)`.\n * Units: Integer satang Measures.\n * Throws: TaxValidationError if a statutory ceiling is not valid satang.\n *\n * @param graph Measure graph being built.\n * @param tallies Income and contribution tally ids.\n * @param assessable Assessable income Measure used as the percentage base.\n * @statute Thailand Revenue Code Section 47; Ministerial Regulations No. 126, 390.\n */\nexport function addRetirementAllowances(\n\tgraph: ThLevyGraph,\n\ttallies: ThLevyTallies,\n\tassessable: string,\n): ThRetirementAllowanceMeasures {\n\tconst p = TH_2026_PARAMETERS.allowances.retirementCluster\n\tconst income = tallies.income\n\tconst funded = tallies.contributions\n\tconst wage = income[ThLevyIncomeLabel.Section40_1]\n\tconst rmf = graph.min(\n\t\tfunded[ThLevyContributionLabel.Rmf],\n\t\trateCapAt(graph, assessable, p.rmf.maxRate, toSatang(p.rmf.ceiling, ThAllowanceField.Rmf)),\n\t)\n\tconst ssf = graph.min(\n\t\tfunded[ThLevyContributionLabel.Ssf],\n\t\trateCapAt(graph, assessable, p.ssf.maxRate, toSatang(p.ssf.ceiling, ThAllowanceField.Ssf)),\n\t)\n\tconst pvd = graph.min(\n\t\tfunded[ThLevyContributionLabel.Pvd],\n\t\trateCapAt(graph, wage, p.pvd.maxRate, toSatang(p.pvd.ceiling, ThAllowanceField.Pvd)),\n\t)\n\tconst gpf = graph.min(\n\t\tfunded[ThLevyContributionLabel.Gpf],\n\t\trateCapAt(graph, wage, p.gpf.maxRate, toSatang(p.gpf.ceiling, ThAllowanceField.Gpf)),\n\t)\n\tconst nsf = graph.min(\n\t\tfunded[ThLevyContributionLabel.Nsf],\n\t\tgraph.constant(toSatang(p.nsf.ceiling, ThAllowanceField.Nsf)),\n\t)\n\tconst pension = graph.min(\n\t\tfunded[ThLevyContributionLabel.PensionLifeInsurance],\n\t\trateCapAt(\n\t\t\tgraph,\n\t\t\tassessable,\n\t\t\tp.pensionLifeInsurance.maxRate,\n\t\t\ttoSatang(p.pensionLifeInsurance.ceiling, ThAllowanceField.PensionLife),\n\t\t),\n\t)\n\tconst individual = sumMeasures(graph, [rmf, ssf, pvd, gpf, nsf, pension])\n\tconst cluster = graph.min(\n\t\tindividual,\n\t\tgraph.constant(toSatang(p.combinedCeiling, ThAllowanceField.RetirementCluster)),\n\t\tThLevyMeasureId.RetirementCluster,\n\t)\n\tconst esg = TH_2026_PARAMETERS.allowances.thaiEsg\n\tconst thaiEsg = graph.min(\n\t\tfunded[ThLevyContributionLabel.ThaiEsg],\n\t\trateCapAt(graph, assessable, esg.maxRate, toSatang(esg.ceiling, ThAllowanceField.ThaiEsg)),\n\t\tThLevyMeasureId.ThaiEsg,\n\t)\n\treturn { cluster, thaiEsg }\n}\n","import { addDonationAllowances } from \"./allowances-donations\"\nimport { addFixedAllowances } from \"./allowances-fixed\"\nimport { addRetirementAllowances } from \"./allowances-retirement\"\nimport type { ThLevyGraph } from \"./graph\"\nimport { sumMeasures } from \"./graph-rates\"\nimport { ThLevyMeasureId } from \"./measure-ids\"\nimport type { ThLevyProfile } from \"./profile-types\"\nimport type { ThLevyTallies } from \"./tallies\"\n\n/**\n * Allowance total and the net taxable income left for the bracket schedule.\n *\n * Purpose: Names the two audited outputs of the allowance pipeline.\n * Math: No arithmetic; ids only.\n * Units: The referenced Measures are integer satang.\n * Throws: Nothing.\n * @statute Thailand Revenue Code Section 47.\n */\nexport interface ThAllowanceMeasures {\n\t/** Fixed + Retirement Cluster + ThaiESG + donations, after every cap. */\n\treadonly total: string\n\t/** Net taxable income in satang, never below zero. */\n\treadonly netTaxable: string\n}\n\n/**\n * Emits every Section 47 allowance and derives net taxable income.\n *\n * Purpose: Orders the fixed, retirement, ThaiESG, and donation rules into one pipeline.\n * Math: `netBefore = max(0, assessable - expenses - fixed - cluster - ThaiESG)`, then donations\n * are capped against that net; `netTaxable = max(0, netBefore - allowed donations)`.\n * Units: Integer satang Measures.\n * Throws: TaxValidationError for an invalid profile allowance or donation amount.\n *\n * @param graph Measure graph being built.\n * @param tallies Income and contribution tally ids.\n * @param profile Emit-time filing status and allowances.\n * @param assessable Assessable income Measure used as the percentage base.\n * @param expenses Total expense deduction Measure.\n * @statute Thailand Revenue Code Section 47.\n */\nexport function addAllowanceMeasures(\n\tgraph: ThLevyGraph,\n\ttallies: ThLevyTallies,\n\tprofile: ThLevyProfile,\n\tassessable: string,\n\texpenses: string,\n): ThAllowanceMeasures {\n\tconst fixed = addFixedAllowances(graph, profile.allowances, profile.filingStatus ?? \"single\")\n\tconst retirement = addRetirementAllowances(graph, tallies, assessable)\n\tconst nonDonation = sumMeasures(graph, [fixed, retirement.cluster, retirement.thaiEsg])\n\tconst netBefore = graph.max(\n\t\tgraph.sub(graph.sub(assessable, expenses), nonDonation),\n\t\tgraph.constant(0),\n\t)\n\tconst donations = addDonationAllowances(\n\t\tgraph,\n\t\tnetBefore,\n\t\tprofile.allowances?.donation2x,\n\t\tprofile.allowances?.donation1x,\n\t)\n\tconst total = graph.add(nonDonation, donations.total, ThLevyMeasureId.AllowancesTotal)\n\treturn { total, netTaxable: donations.netTaxable }\n}\n","import type { ThLevyGraph } from \"./graph\"\nimport { sumMeasures } from \"./graph-rates\"\nimport { ThLevyIncomeLabel } from \"./labels\"\nimport { ThLevyMeasureId } from \"./measure-ids\"\nimport type { ThSection40_4Measures } from \"./regimes\"\nimport type { ThLevyTallies } from \"./tallies\"\n\n/**\n * Assessable income Measures: the Section 40 total and the Section 48(2) eligible subtotal.\n *\n * Purpose: Names the two assessable-income Measures the later stages consume.\n * Math: No arithmetic; ids only.\n * Units: The referenced Measures are integer satang.\n * Throws: Nothing.\n * @statute Thailand Revenue Code Sections 40(1)–(8), 48(2).\n */\nexport interface ThAssessableMeasures {\n\t/** Section 40(1)–(8) assessable income after the Section 40(4) split. */\n\treadonly total: string\n\t/** Section 40(2)–(8) gross income, the Section 48(2) minimum-tax base. */\n\treadonly grossNonEmployment: string\n}\n\n/**\n * Emits the Section 40 assessable income totals.\n *\n * Purpose: Sums every Section 40 class after the Section 40(4) election split.\n * Math: total = 40(1) + 40(2) + 40(3) + 40(4) assessable + 40(5) + 40(6) + 40(7) + 40(8);\n * gross non-employment = the same sum without Section 40(1).\n * Units: Integer satang Measures.\n * Throws: Nothing; every input is a caller-supplied tally Measure.\n *\n * @param graph Measure graph being built.\n * @param tallies Income tally ids.\n * @param section40_4 Resolved Section 40(4) assessable Measure.\n * @statute Thailand Revenue Code Sections 40(1)–(8), 48(2).\n */\nexport function addAssessableMeasures(\n\tgraph: ThLevyGraph,\n\ttallies: ThLevyTallies,\n\tsection40_4: ThSection40_4Measures,\n): ThAssessableMeasures {\n\tconst income = tallies.income\n\tconst rental = sumMeasures(graph, [\n\t\tincome[ThLevyIncomeLabel.Section40_5Building],\n\t\tincome[ThLevyIncomeLabel.Section40_5AgriculturalLand],\n\t\tincome[ThLevyIncomeLabel.Section40_5NonAgriculturalLand],\n\t\tincome[ThLevyIncomeLabel.Section40_5Vehicles],\n\t\tincome[ThLevyIncomeLabel.Section40_5Other],\n\t])\n\tconst professions = sumMeasures(graph, [\n\t\tincome[ThLevyIncomeLabel.Section40_6Medical],\n\t\tincome[ThLevyIncomeLabel.Section40_6Law],\n\t\tincome[ThLevyIncomeLabel.Section40_6Engineering],\n\t\tincome[ThLevyIncomeLabel.Section40_6Architecture],\n\t\tincome[ThLevyIncomeLabel.Section40_6Accounting],\n\t\tincome[ThLevyIncomeLabel.Section40_6FineArts],\n\t\tincome[ThLevyIncomeLabel.Section40_6Other],\n\t])\n\tconst business = sumMeasures(graph, [\n\t\tincome[ThLevyIncomeLabel.Section40_8General],\n\t\tincome[ThLevyIncomeLabel.Section40_8Entertainer],\n\t])\n\tconst employment = income[ThLevyIncomeLabel.Section40_1]\n\tconst nonEmployment = [\n\t\tincome[ThLevyIncomeLabel.Section40_2],\n\t\tincome[ThLevyIncomeLabel.Section40_3],\n\t\tsection40_4.assessable,\n\t\trental,\n\t\tprofessions,\n\t\tincome[ThLevyIncomeLabel.Section40_7],\n\t\tbusiness,\n\t]\n\tconst total = sumMeasures(graph, [employment, ...nonEmployment], ThLevyMeasureId.AssessableTotal)\n\tconst grossNonEmployment = sumMeasures(graph, nonEmployment, ThLevyMeasureId.GrossNonEmployment)\n\treturn { total, grossNonEmployment }\n}\n","import type { TaxBracket } from \"../../core/brackets\"\nimport { rateFraction } from \"../../core/money/ratio\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport { toSatang } from \"../units\"\nimport type { ThLevyGraph } from \"./graph\"\nimport { sumMeasures } from \"./graph-rates\"\nimport { ThLevyRounding } from \"./graph-types\"\nimport { ThLevyMeasureId } from \"./measure-ids\"\n\n/**\n * Section 48(2) minimum gross tax Measures.\n *\n * Purpose: Separates the calculated tax from the portion that actually applies.\n * Math: No arithmetic; ids only.\n * Units: The referenced Measures are integer satang.\n * Throws: Nothing.\n * @statute Thailand Revenue Code Section 48(2).\n */\nexport interface ThMinimumTaxMeasures {\n\t/** 0.5% of Section 40(2)–(8) income when it reaches 120,000 THB, else zero. */\n\treadonly calculated: string\n\t/** The calculated tax when it exceeds 5,000 THB, else zero. */\n\treadonly applicable: string\n}\n\n/**\n * Emits progressive Section 48(1) tax as one slice per bracket tier.\n *\n * Purpose: Reproduces `evaluateBrackets` slice by slice as Measures.\n * Math: For tier `[lo, hi)` at rate `r`, tax += `round_half_up(min(max(x - lo, 0), hi - lo) * r)`;\n * the unbounded top tier omits the upper cap. Slices match `evaluateBrackets` exactly.\n * Units: Taxable income and tax are integer satang Measures; rates are dimensionless.\n * Throws: TaxValidationError if a bracket bound or rate is invalid.\n *\n * @param graph Measure graph being built.\n * @param taxable Net taxable income Measure.\n * @param brackets Ordered statutory bracket table in THB major units.\n * @statute Thailand Revenue Code Section 48(1); Royal Decree No. 650.\n */\nexport function addProgressiveTax(\n\tgraph: ThLevyGraph,\n\ttaxable: string,\n\tbrackets: readonly TaxBracket[] = TH_2026_PARAMETERS.brackets,\n): string {\n\tconst taxes: string[] = []\n\tlet lower = 0\n\tfor (const bracket of brackets) {\n\t\tconst upper = bracket.upTo === null ? null : toSatang(bracket.upTo)\n\t\tconst excess = graph.max(graph.sub(taxable, graph.constant(lower)), graph.constant(0))\n\t\tconst slice = upper === null ? excess : graph.min(excess, graph.constant(upper - lower))\n\t\tconst { num, den } = rateFraction(bracket.rate)\n\t\ttaxes.push(graph.scale(slice, num, den, ThLevyRounding.HalfUp))\n\t\tlower = upper ?? lower\n\t}\n\treturn sumMeasures(graph, taxes, ThLevyMeasureId.ProgressiveTax)\n}\n\n/**\n * Emits the Section 48(2) minimum gross tax and its applicability test.\n *\n * Purpose: Encodes both statutory thresholds as `pick` Measures.\n * Math: `calculated = gross >= 120,000 ? round_half_up(gross * 0.5%) : 0`;\n * `applicable = calculated > 5,000 ? calculated : 0`.\n * Units: Integer satang Measures; the threshold comparison uses satang.\n * Throws: TaxValidationError if the rate or a threshold is invalid.\n *\n * @param graph Measure graph being built.\n * @param grossNonEmployment Section 40(2)–(8) gross income Measure.\n * @statute Thailand Revenue Code Section 48(2).\n */\nexport function addMinimumGrossTax(\n\tgraph: ThLevyGraph,\n\tgrossNonEmployment: string,\n): ThMinimumTaxMeasures {\n\tconst p = TH_2026_PARAMETERS.minimumGrossTax\n\tconst { num, den } = rateFraction(p.rate)\n\tconst computed = graph.scale(grossNonEmployment, num, den, ThLevyRounding.HalfUp)\n\tconst calculated = graph.pick(\n\t\tgrossNonEmployment,\n\t\tgraph.constant(toSatang(p.incomeThreshold) - 1),\n\t\tcomputed,\n\t\tgraph.constant(0),\n\t\tThLevyMeasureId.MinimumGrossTax,\n\t)\n\tconst applicable = graph.pick(\n\t\tcalculated,\n\t\tgraph.constant(toSatang(p.taxThreshold)),\n\t\tcalculated,\n\t\tgraph.constant(0),\n\t)\n\treturn { calculated, applicable }\n}\n\n/**\n * Emits the regular-schedule tax: the larger of progressive tax and applicable minimum gross tax.\n *\n * Purpose: Encodes Section 48(2)'s \"payable only if it exceeds\" rule as `max`.\n * Math: `max(progressive, applicableMinimum)`.\n * Units: Integer satang Measures.\n * Throws: Nothing.\n *\n * @param graph Measure graph being built.\n * @param progressive Progressive bracket tax Measure.\n * @param applicableMinimum Minimum gross tax once the 5,000 THB floor applies.\n * @statute Thailand Revenue Code Sections 48(1)–(2).\n */\nexport function addRegularTax(\n\tgraph: ThLevyGraph,\n\tprogressive: string,\n\tapplicableMinimum: string,\n): string {\n\treturn graph.max(progressive, applicableMinimum, ThLevyMeasureId.RegularTax)\n}\n","import { validateMonetaryAmount } from \"../../core/money\"\nimport type { ThExpenseField } from \"../types\"\nimport { toSatang } from \"../units\"\nimport type { ThLevyGraph } from \"./graph\"\nimport { capAt, rateFlooredToBaht, sumMeasures } from \"./graph-rates\"\nimport type { ThLevyExpenseChoice } from \"./profile-types\"\n\n/**\n * One electable expense deduction: its income tally, election, standard rate, and optional cap.\n *\n * Purpose: Describes a single Section 40 expense item for {@link addItemExpense}.\n * Math: No arithmetic; carries the inputs of one standard-or-actual computation.\n * Units: `tally` is a Measure id over integer satang; `rate` is dimensionless; `capSatang` is satang.\n * Throws: Nothing at construction; the emitter validates `actualExpense`.\n * @statute Thailand Revenue Code Sections 42 bis, 42 ter, 43–46.\n */\nexport interface ThLevyExpenseLine {\n\t/** Tally Measure holding the item's assessable income in satang. */\n\treadonly tally: string\n\t/** Standard-versus-actual election fixed at emit time; omitted means standard. */\n\treadonly choice?: ThLevyExpenseChoice | undefined\n\t/** Standard statutory rate ratio (e.g. `0.5` for 50%). */\n\treadonly rate: number\n\t/** Diagnostic field reported when the actual expense is invalid. */\n\treadonly field: ThExpenseField\n\t/** Optional absolute ceiling in satang. */\n\treadonly capSatang?: number\n}\n\n/**\n * Emits the actual-expense branch: `min(actual, income)`, matching `clampCap(actual, income)`.\n *\n * Purpose: Shared actual-expense Measure for every electable Section 40 item.\n * Math: `min(tally, actualExpense)` in satang.\n * Units: Actual expense is THB major units in, satang Measures out.\n * Throws: TaxValidationError if the actual expense is negative, NaN, or non-finite.\n *\n * @param graph Measure graph being built.\n * @param tally Tally Measure of the item's income.\n * @param choice Election carrying the actual expense.\n * @param field Diagnostic field name.\n * @statute Thailand Revenue Code Sections 42 ter, 43–46; Royal Decree No. 11.\n */\nexport function addActualExpense(\n\tgraph: ThLevyGraph,\n\ttally: string,\n\tchoice: ThLevyExpenseChoice | undefined,\n\tfield: ThExpenseField,\n): string {\n\tconst actual = choice?.actualExpense ?? 0\n\tvalidateMonetaryAmount(actual, field)\n\treturn graph.min(tally, graph.constant(toSatang(actual, field)))\n}\n\n/**\n * Emits one Section 40 expense deduction, either actual-capped-at-income or the standard percentage.\n *\n * Purpose: Selects the election branch and applies the item's statutory formula.\n * Math: Actual election gives `min(actual, income)`; otherwise `min(floor(income * rate), cap)`\n * expressed in whole baht, reproducing the calculator's `Math.floor(major * rate)` step.\n * Units: Measures are integer satang; `rate` is dimensionless.\n * Throws: TaxValidationError if the actual expense is invalid.\n *\n * @param graph Measure graph being built.\n * @param line Expense line specification.\n * @statute Thailand Revenue Code Sections 42 bis, 42 ter, 43–46; Royal Decree No. 11.\n */\nexport function addItemExpense(graph: ThLevyGraph, line: ThLevyExpenseLine): string {\n\tif (line.choice?.election === \"actual\") {\n\t\treturn addActualExpense(graph, line.tally, line.choice, line.field)\n\t}\n\tconst standard = rateFlooredToBaht(graph, line.tally, line.rate)\n\treturn line.capSatang === undefined ? standard : capAt(graph, standard, line.capSatang)\n}\n\n/**\n * Emits and sums a list of expense lines, one per Section 40 item or sub-category.\n *\n * Purpose: Keeps multi-class categories such as rentals and professions readable.\n * Math: `sum(addItemExpense(line))`; an empty list is zero.\n * Units: Integer satang Measures.\n * Throws: TaxValidationError if an elected actual expense is invalid.\n *\n * @param graph Measure graph being built.\n * @param lines Expense lines for one Section 40 category.\n * @statute Thailand Revenue Code Sections 42 bis, 42 ter, 43–46; Royal Decree No. 11.\n */\nexport function addExpenseLines(graph: ThLevyGraph, lines: readonly ThLevyExpenseLine[]): string {\n\treturn sumMeasures(\n\t\tgraph,\n\t\tlines.map((line) => addItemExpense(graph, line)),\n\t)\n}\n","import { TH_2026_PARAMETERS } from \"../parameters\"\nimport { ThExpenseField } from \"../types\"\nimport { toSatang } from \"../units\"\nimport { addActualExpense, addItemExpense } from \"./expense-item\"\nimport type { ThLevyGraph } from \"./graph\"\nimport { capAt, rateFlooredToBaht, sumMeasures } from \"./graph-rates\"\nimport { ThLevyIncomeLabel } from \"./labels\"\nimport type { ThLevyExpenseElections } from \"./profile-types\"\nimport type { ThLevyTallies } from \"./tallies\"\n\nfunction addEntertainerExpense(\n\tgraph: ThLevyGraph,\n\ttally: string,\n\tchoice: ThLevyExpenseElections[\"section40_8\"],\n): string {\n\tconst entertainer = choice?.entertainer\n\tconst field = ThExpenseField.Section40_8_Entertainer\n\tif (entertainer?.election === \"actual\") {\n\t\treturn addActualExpense(graph, tally, entertainer, field)\n\t}\n\tconst p = TH_2026_PARAMETERS.expenses.section40_8\n\tconst threshold = toSatang(p.entertainerFirstTierThreshold, field)\n\tconst firstTier = graph.min(tally, graph.constant(threshold))\n\tconst secondTier = graph.max(graph.sub(tally, graph.constant(threshold)), graph.constant(0))\n\tconst computed = graph.add(\n\t\trateFlooredToBaht(graph, firstTier, p.entertainerFirstTierRate),\n\t\trateFlooredToBaht(graph, secondTier, p.entertainerSecondTierRate),\n\t)\n\treturn capAt(graph, computed, toSatang(p.entertainerCeiling, field))\n}\n\n/**\n * Emits the Section 40(8) business expense: general income at 60%, entertainer income on the\n * 60%/40% two-tier schedule capped at 600,000 THB; actual expenses cap at their income.\n *\n * Purpose: Mirrors both Section 40(8) expense schedules and their actual-expense elections.\n * Math: General `floor(income * 0.6)`. Entertainer\n * `min(floor(min(income, 300000) * 0.6) + floor(max(income - 300000, 0) * 0.4), 600000)`.\n * Units: Integer satang Measures.\n * Throws: TaxValidationError if an elected actual expense is invalid.\n * @statute Thailand Royal Decree No. 11 Section 8.\n */\nexport function addSection40_8Expense(\n\tgraph: ThLevyGraph,\n\ttallies: ThLevyTallies,\n\tchoice: ThLevyExpenseElections[\"section40_8\"],\n): string {\n\tconst rates = TH_2026_PARAMETERS.expenses.section40_8\n\tconst income = tallies.income\n\tconst general = addItemExpense(graph, {\n\t\ttally: income[ThLevyIncomeLabel.Section40_8General],\n\t\tchoice: choice?.general,\n\t\trate: rates.generalRate,\n\t\tfield: ThExpenseField.Section40_8_General,\n\t})\n\tconst entertainer = addEntertainerExpense(\n\t\tgraph,\n\t\tincome[ThLevyIncomeLabel.Section40_8Entertainer],\n\t\tchoice,\n\t)\n\treturn sumMeasures(graph, [general, entertainer])\n}\n","import { TH_2026_PARAMETERS } from \"../parameters\"\nimport { ThExpenseField } from \"../types\"\nimport { addItemExpense } from \"./expense-item\"\nimport type { ThLevyGraph } from \"./graph\"\nimport { ThLevyIncomeLabel } from \"./labels\"\nimport type { ThLevyExpenseChoice } from \"./profile-types\"\nimport type { ThLevyTallies } from \"./tallies\"\n\n/**\n * Emits the Section 40(7) contracting expense: standard 60%, or actual capped at the income.\n *\n * Purpose: Mirrors the contracting expense deduction and its actual-expense election.\n * Math: `floor(s7 * 0.6)` or `min(actual, s7)`.\n * Units: Integer satang Measures.\n * Throws: TaxValidationError if the elected actual expense is invalid.\n * @statute Thailand Royal Decree No. 11 Section 7.\n */\nexport function addSection40_7Expense(\n\tgraph: ThLevyGraph,\n\ttallies: ThLevyTallies,\n\tchoice: ThLevyExpenseChoice | undefined,\n): string {\n\treturn addItemExpense(graph, {\n\t\ttally: tallies.income[ThLevyIncomeLabel.Section40_7],\n\t\tchoice,\n\t\trate: TH_2026_PARAMETERS.expenses.section40_7.rate,\n\t\tfield: ThExpenseField.Section40_7,\n\t})\n}\n","import { TH_2026_PARAMETERS } from \"../parameters\"\nimport { ThExpenseField } from \"../types\"\nimport { toSatang } from \"../units\"\nimport { addItemExpense } from \"./expense-item\"\nimport type { ThLevyGraph } from \"./graph\"\nimport { ThLevyIncomeLabel } from \"./labels\"\nimport type { ThLevyExpenseChoice } from \"./profile-types\"\nimport type { ThLevyTallies } from \"./tallies\"\n\n/**\n * Emits the shared Section 40(1) and 40(2) expense: 50% of their combined income, capped at\n * 100,000 THB.\n *\n * Purpose: Mirrors the shared employment/service expense deduction as Measures.\n * Math: `min(floor((s1 + s2) * 0.5), 100000)` in whole baht, summed before the percentage.\n * Units: Integer satang Measures.\n * Throws: Nothing; both tallies are caller-supplied integer satang.\n * @statute Thailand Revenue Code Section 42 bis.\n */\nexport function addSection40_1_2Expense(graph: ThLevyGraph, tallies: ThLevyTallies): string {\n\tconst income = tallies.income\n\tconst combined = graph.add(\n\t\tincome[ThLevyIncomeLabel.Section40_1],\n\t\tincome[ThLevyIncomeLabel.Section40_2],\n\t)\n\tconst rate = TH_2026_PARAMETERS.expenses.section40_1_2\n\treturn addItemExpense(graph, {\n\t\ttally: combined,\n\t\trate: rate.rate,\n\t\tfield: ThExpenseField.Section40_1_2,\n\t\tcapSatang: toSatang(rate.ceiling, ThExpenseField.Section40_1_2),\n\t})\n}\n\n/**\n * Emits the Section 40(3) royalty expense: standard 50% capped at 100,000 THB, or actual capped\n * at the royalty income.\n *\n * Purpose: Mirrors the royalty expense deduction, including its actual-expense election.\n * Math: `min(floor(s3 * 0.5), 100000)` or `min(actual, s3)`.\n * Units: Integer satang Measures.\n * Throws: TaxValidationError if the elected actual expense is invalid.\n * @statute Thailand Revenue Code Section 42 ter; Royal Decree No. 11 Section 4.\n */\nexport function addSection40_3Expense(\n\tgraph: ThLevyGraph,\n\ttallies: ThLevyTallies,\n\tchoice: ThLevyExpenseChoice | undefined,\n): string {\n\tconst rate = TH_2026_PARAMETERS.expenses.section40_3\n\treturn addItemExpense(graph, {\n\t\ttally: tallies.income[ThLevyIncomeLabel.Section40_3],\n\t\tchoice,\n\t\trate: rate.rate,\n\t\tfield: ThExpenseField.Section40_3,\n\t\tcapSatang: toSatang(rate.ceiling, ThExpenseField.Section40_3),\n\t})\n}\n\n/**\n * Emits the Section 40(4) expense, statutorily zero.\n *\n * Purpose: Keeps the expense total's shape identical to the calculator's.\n * Math: `0`.\n * Units: Integer satang.\n * Throws: Nothing.\n * @statute Thailand Revenue Code Section 40(4).\n */\nexport function addSection40_4Expense(graph: ThLevyGraph): string {\n\treturn graph.constant(0)\n}\n","import { TH_2026_PARAMETERS } from \"../parameters\"\nimport { ThExpenseField } from \"../types\"\nimport { addExpenseLines } from \"./expense-item\"\nimport type { ThLevyGraph } from \"./graph\"\nimport { ThLevyIncomeLabel } from \"./labels\"\nimport type { ThLevyExpenseElections } from \"./profile-types\"\nimport type { ThLevyTallies } from \"./tallies\"\n\n/**\n * Emits the Section 40(6) liberal profession expense: 60% for medical, 30% for every other\n * profession; actual expenses cap at their class income.\n *\n * Purpose: Mirrors each profession class's standard percentage or actual-expense election.\n * Math: Per class, `floor(income * rate)` with medical 60% and the rest 30%.\n * Units: Integer satang Measures.\n * Throws: TaxValidationError if an elected actual expense is invalid.\n * @statute Thailand Royal Decree No. 11 Section 6.\n */\nexport function addSection40_6Expense(\n\tgraph: ThLevyGraph,\n\ttallies: ThLevyTallies,\n\tchoice: ThLevyExpenseElections[\"section40_6\"],\n): string {\n\tconst income = tallies.income\n\tconst rates = TH_2026_PARAMETERS.expenses.section40_6\n\treturn addExpenseLines(graph, [\n\t\t{\n\t\t\ttally: income[ThLevyIncomeLabel.Section40_6Medical],\n\t\t\tchoice: choice?.medical,\n\t\t\trate: rates.medical,\n\t\t\tfield: ThExpenseField.Section40_6_Medical,\n\t\t},\n\t\t{\n\t\t\ttally: income[ThLevyIncomeLabel.Section40_6Law],\n\t\t\tchoice: choice?.law,\n\t\t\trate: rates.other,\n\t\t\tfield: ThExpenseField.Section40_6_Law,\n\t\t},\n\t\t{\n\t\t\ttally: income[ThLevyIncomeLabel.Section40_6Engineering],\n\t\t\tchoice: choice?.engineering,\n\t\t\trate: rates.other,\n\t\t\tfield: ThExpenseField.Section40_6_Engineering,\n\t\t},\n\t\t{\n\t\t\ttally: income[ThLevyIncomeLabel.Section40_6Architecture],\n\t\t\tchoice: choice?.architecture,\n\t\t\trate: rates.other,\n\t\t\tfield: ThExpenseField.Section40_6_Architecture,\n\t\t},\n\t\t{\n\t\t\ttally: income[ThLevyIncomeLabel.Section40_6Accounting],\n\t\t\tchoice: choice?.accounting,\n\t\t\trate: rates.other,\n\t\t\tfield: ThExpenseField.Section40_6_Accounting,\n\t\t},\n\t\t{\n\t\t\ttally: income[ThLevyIncomeLabel.Section40_6FineArts],\n\t\t\tchoice: choice?.fineArts,\n\t\t\trate: rates.other,\n\t\t\tfield: ThExpenseField.Section40_6_FineArts,\n\t\t},\n\t\t{\n\t\t\ttally: income[ThLevyIncomeLabel.Section40_6Other],\n\t\t\tchoice: choice?.other,\n\t\t\trate: rates.other,\n\t\t\tfield: ThExpenseField.Section40_6_Other,\n\t\t},\n\t])\n}\n","import { TH_2026_PARAMETERS } from \"../parameters\"\nimport { ThExpenseField } from \"../types\"\nimport { addExpenseLines } from \"./expense-item\"\nimport type { ThLevyGraph } from \"./graph\"\nimport { ThLevyIncomeLabel } from \"./labels\"\nimport type { ThLevyExpenseElections } from \"./profile-types\"\nimport type { ThLevyTallies } from \"./tallies\"\n\n/**\n * Emits the Section 40(5) rental expense across the five property classes.\n *\n * Purpose: Mirrors each rental class's standard percentage or actual-expense election.\n * Math: Per class, `floor(income * rate)` with building 30%, agricultural land 20%,\n * non-agricultural land 15%, vehicles 30%, other 10%; actual expenses cap at their class income.\n * Units: Integer satang Measures.\n * Throws: TaxValidationError if an elected actual expense is invalid.\n * @statute Thailand Royal Decree No. 11 Section 5.\n */\nexport function addSection40_5Expense(\n\tgraph: ThLevyGraph,\n\ttallies: ThLevyTallies,\n\tchoice: ThLevyExpenseElections[\"section40_5\"],\n): string {\n\tconst income = tallies.income\n\tconst rates = TH_2026_PARAMETERS.expenses.section40_5\n\treturn addExpenseLines(graph, [\n\t\t{\n\t\t\ttally: income[ThLevyIncomeLabel.Section40_5Building],\n\t\t\tchoice: choice?.building,\n\t\t\trate: rates.building,\n\t\t\tfield: ThExpenseField.Section40_5_Building,\n\t\t},\n\t\t{\n\t\t\ttally: income[ThLevyIncomeLabel.Section40_5AgriculturalLand],\n\t\t\tchoice: choice?.agriculturalLand,\n\t\t\trate: rates.agriculturalLand,\n\t\t\tfield: ThExpenseField.Section40_5_AgriculturalLand,\n\t\t},\n\t\t{\n\t\t\ttally: income[ThLevyIncomeLabel.Section40_5NonAgriculturalLand],\n\t\t\tchoice: choice?.nonAgriculturalLand,\n\t\t\trate: rates.nonAgriculturalLand,\n\t\t\tfield: ThExpenseField.Section40_5_NonAgriculturalLand,\n\t\t},\n\t\t{\n\t\t\ttally: income[ThLevyIncomeLabel.Section40_5Vehicles],\n\t\t\tchoice: choice?.vehicles,\n\t\t\trate: rates.vehicles,\n\t\t\tfield: ThExpenseField.Section40_5_Vehicles,\n\t\t},\n\t\t{\n\t\t\ttally: income[ThLevyIncomeLabel.Section40_5Other],\n\t\t\tchoice: choice?.other,\n\t\t\trate: rates.other,\n\t\t\tfield: ThExpenseField.Section40_5_Other,\n\t\t},\n\t])\n}\n","import { addSection40_8Expense } from \"./expenses-business\"\nimport { addSection40_7Expense } from \"./expenses-contracting\"\nimport {\n\taddSection40_1_2Expense,\n\taddSection40_3Expense,\n\taddSection40_4Expense,\n} from \"./expenses-income\"\nimport { addSection40_6Expense } from \"./expenses-professions\"\nimport { addSection40_5Expense } from \"./expenses-rental\"\nimport type { ThLevyGraph } from \"./graph\"\nimport { sumMeasures } from \"./graph-rates\"\nimport { ThLevyMeasureId } from \"./measure-ids\"\nimport type { ThLevyExpenseElections } from \"./profile-types\"\nimport type { ThLevyTallies } from \"./tallies\"\n\n/**\n * Emits every Section 40(1)–40(8) expense deduction and totals them.\n *\n * Purpose: Reproduces the calculator's `calculateExpenseDeductions` as Measures.\n * Math: Sum of the seven statutory expense buckets, each already rounded the way its stage does.\n * Units: Integer satang Measures.\n * Throws: TaxValidationError if an elected actual expense is invalid.\n *\n * @param graph Measure graph being built.\n * @param tallies Income tally ids.\n * @param elections Standard-versus-actual choices fixed at emit time.\n * @statute Thailand Revenue Code Sections 42 bis, 42 ter, 43–46; Royal Decree No. 11.\n */\nexport function addExpenseMeasures(\n\tgraph: ThLevyGraph,\n\ttallies: ThLevyTallies,\n\telections: ThLevyExpenseElections = {},\n): string {\n\tconst buckets = [\n\t\taddSection40_1_2Expense(graph, tallies),\n\t\taddSection40_3Expense(graph, tallies, elections.section40_3),\n\t\taddSection40_4Expense(graph),\n\t\taddSection40_5Expense(graph, tallies, elections.section40_5),\n\t\taddSection40_6Expense(graph, tallies, elections.section40_6),\n\t\taddSection40_7Expense(graph, tallies, elections.section40_7),\n\t\taddSection40_8Expense(graph, tallies, elections.section40_8),\n\t]\n\treturn sumMeasures(graph, buckets, ThLevyMeasureId.ExpensesTotal)\n}\n","import { TaxValidationError } from \"../../core/money\"\nimport type {\n\tThLevy,\n\tThLevyMeasure,\n\tThLevyMonthDay,\n\tThLevyRounding,\n\tThLevyTallyBasis,\n} from \"./graph-types\"\nimport { ThLevyField } from \"./measure-ids\"\n\n/** Auto-generated Measure id prefix; explicit audited ids never start with it. */\nconst AUTO_ID = \"auto\"\n\n/**\n * Builder for a sim-core `Levy` rule graph.\n *\n * Purpose: The only place that constructs Measures, so the emitted operation set is closed by\n * the {@link ThLevyMeasure} union and every id is unique.\n * Math: Each method appends one Measure; arithmetic is deferred to sim-core's exact evaluator.\n * Units: `const` values are integer satang; `num`/`den` of `scale` are dimensionless integers.\n * Throws: TaxValidationError for a duplicate id, non-safe const, or invalid scale fraction.\n *\n * @statute Thailand Revenue Code Sections 40, 42 bis, 47, 47 bis, 48(1)–(2).\n */\nexport class ThLevyGraph {\n\tprivate readonly list: ThLevyMeasure[] = []\n\tprivate readonly ids = new Set<string>()\n\tprivate serial = 0\n\n\t/** Appends a fixed satang amount; `id` names an audited Measure. */\n\tconstant(value: number, id?: string): string {\n\t\tif (!Number.isSafeInteger(value)) {\n\t\t\tthrow new TaxValidationError(\n\t\t\t\tThLevyField.ConstValue,\n\t\t\t\tvalue,\n\t\t\t\t\"Const must be safe integer satang\",\n\t\t\t)\n\t\t}\n\t\treturn this.append({ id: this.claim(id), op: \"const\", value })\n\t}\n\n\t/** Appends a tally over one label and basis. */\n\ttally(label: string, basis: ThLevyTallyBasis, id?: string): string {\n\t\treturn this.append({ id: this.claim(id), op: \"tally\", label, basis })\n\t}\n\n\t/** Appends `a + b`. */\n\tadd(a: string, b: string, id?: string): string {\n\t\treturn this.append({ id: this.claim(id), op: \"add\", a, b })\n\t}\n\n\t/** Appends `a - b`. */\n\tsub(a: string, b: string, id?: string): string {\n\t\treturn this.append({ id: this.claim(id), op: \"sub\", a, b })\n\t}\n\n\t/** Appends `min(a, b)`. */\n\tmin(a: string, b: string, id?: string): string {\n\t\treturn this.append({ id: this.claim(id), op: \"min\", a, b })\n\t}\n\n\t/** Appends `max(a, b)`. */\n\tmax(a: string, b: string, id?: string): string {\n\t\treturn this.append({ id: this.claim(id), op: \"max\", a, b })\n\t}\n\n\t/** Appends `of * num / den`, rounded as `rounding` says. */\n\tscale(of: string, num: number, den: number, rounding: ThLevyRounding, id?: string): string {\n\t\tif (!Number.isSafeInteger(num)) {\n\t\t\tthrow new TaxValidationError(ThLevyField.ScaleNum, num, \"Scale numerator must be an integer\")\n\t\t}\n\t\tif (!Number.isSafeInteger(den) || den < 1) {\n\t\t\tthrow new TaxValidationError(ThLevyField.ScaleDen, den, \"Scale denominator must be >= 1\")\n\t\t}\n\t\treturn this.append({ id: this.claim(id), op: \"scale\", of, num, den, rounding })\n\t}\n\n\t/** Appends `then` when `a > b`, otherwise `otherwise`. */\n\tpick(a: string, b: string, then: string, otherwise: string, id?: string): string {\n\t\treturn this.append({ id: this.claim(id), op: \"pick\", a, b, then, else: otherwise })\n\t}\n\n\t/** Finishes the graph as a Levy, checking that every operand and the due id exist. */\n\tbuild(id: string, period: ThLevyMonthDay, settlement: ThLevyMonthDay, due: string): ThLevy {\n\t\tif (!this.ids.has(due)) {\n\t\t\tthrow new TaxValidationError(ThLevyField.MeasureDue, due, \"Due must name an emitted Measure\")\n\t\t}\n\t\tfor (const measure of this.list) {\n\t\t\tfor (const operand of operandsOf(measure)) {\n\t\t\t\tif (!this.ids.has(operand)) {\n\t\t\t\t\tthrow new TaxValidationError(\n\t\t\t\t\t\tThLevyField.MeasureId,\n\t\t\t\t\t\toperand,\n\t\t\t\t\t\t\"Operand must name an emitted Measure\",\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn { id, period, settlement, measures: [...this.list], due }\n\t}\n\n\tprivate append(measure: ThLevyMeasure): string {\n\t\tthis.list.push(measure)\n\t\treturn measure.id\n\t}\n\n\tprivate claim(id?: string): string {\n\t\tconst resolved = id ?? `${AUTO_ID}.${this.serial++}`\n\t\tif (this.ids.has(resolved)) {\n\t\t\tthrow new TaxValidationError(ThLevyField.MeasureId, resolved, \"Duplicate Measure id\")\n\t\t}\n\t\tthis.ids.add(resolved)\n\t\treturn resolved\n\t}\n}\n\nfunction operandsOf(measure: ThLevyMeasure): readonly string[] {\n\tswitch (measure.op) {\n\t\tcase \"const\":\n\t\tcase \"tally\":\n\t\t\treturn []\n\t\tcase \"scale\":\n\t\t\treturn [measure.of]\n\t\tcase \"pick\":\n\t\t\treturn [measure.a, measure.b, measure.then, measure.else]\n\t\tdefault:\n\t\t\treturn [measure.a, measure.b]\n\t}\n}\n","import { rateFraction } from \"../../core/money/ratio\"\nimport { TH_2026_PARAMETERS } from \"../parameters\"\nimport { ThFinalWithholdingElection } from \"../types\"\nimport { validateCorpTaxRate } from \"../validate\"\nimport type { ThLevyGraph } from \"./graph\"\nimport { sumMeasures } from \"./graph-rates\"\nimport { ThLevyRounding } from \"./graph-types\"\nimport { ThLevyIncomeLabel } from \"./labels\"\nimport { ThLevyMeasureId } from \"./measure-ids\"\nimport type { ThLevySection40_4Elections } from \"./profile-types\"\nimport type { ThLevyTallies } from \"./tallies\"\n\n/**\n * Section 40(4) Measures: included income, final withholding tax, and the Section 47 bis credit.\n *\n * Purpose: Names every output of the Section 40(4) election split.\n * Math: No arithmetic; ids only.\n * Units: The referenced Measures are integer satang.\n * Throws: Nothing.\n * @statute Thailand Revenue Code Sections 40(4), 47 bis, 50(2)(g), 57 ter.\n */\nexport interface ThSection40_4Measures {\n\t/** Included sources plus the dividend gross-up, added to Section 40(4) assessable income. */\n\treadonly assessable: string\n\t/** Tax withheld at source on sources elected final. */\n\treadonly finalWithholdingTax: string\n\t/** Section 47 bis credit for included dividends, or zero. */\n\treadonly dividendCredit: string\n}\n\nfunction withheld(graph: ThLevyGraph, tally: string, rate: number): string {\n\tconst { num, den } = rateFraction(rate)\n\treturn graph.scale(tally, num, den, ThLevyRounding.HalfUp)\n}\n\n/**\n * Emits the Section 40(4) split: sources elected final leave regular brackets and carry their\n * withholding tax; included sources join Section 40(4) income, and included dividends also earn\n * the Section 47 bis gross-up credit.\n *\n * Purpose: Applies each source's emit-time election and builds the Section 47 bis credit.\n * Math: Final tax = `round_half_up(amount * rate)`; credit = `round_half_up(dividend * r / (1-r))`;\n * assessable = sum of included sources + credit.\n * Units: Integer satang Measures; rates are dimensionless.\n * Throws: TaxValidationError if the corporate tax rate is outside [0, 1).\n *\n * @param graph Measure graph being built.\n * @param tallies Income tally ids.\n * @param elections Final-withholding and Section 47 bis elections fixed at emit time.\n * @statute Thailand Revenue Code Sections 40(4), 47 bis, 50(2)(g), 57 ter.\n */\nexport function addSection40_4(\n\tgraph: ThLevyGraph,\n\ttallies: ThLevyTallies,\n\telections: ThLevySection40_4Elections = {},\n): ThSection40_4Measures {\n\tconst rates = TH_2026_PARAMETERS.finalWithholding\n\tconst income = tallies.income\n\tconst corpTaxRate = elections.dividendCorpTaxRate\n\tif (corpTaxRate !== undefined) validateCorpTaxRate(corpTaxRate)\n\tconst isFinal = (election: ThFinalWithholdingElection | undefined): boolean =>\n\t\telection === ThFinalWithholdingElection.Final\n\tconst included = [income[ThLevyIncomeLabel.Section40_4Other]]\n\tconst finalTaxes: string[] = []\n\tconst interest = income[ThLevyIncomeLabel.Section40_4Interest]\n\tif (isFinal(elections.interest)) finalTaxes.push(withheld(graph, interest, rates.interestRate))\n\telse included.push(interest)\n\tconst dividend = income[ThLevyIncomeLabel.Section40_4Dividends]\n\tconst isDividendIncluded = !isFinal(elections.dividends)\n\tif (isDividendIncluded) included.push(dividend)\n\telse finalTaxes.push(withheld(graph, dividend, rates.dividendRate))\n\tconst digital = income[ThLevyIncomeLabel.Section40_4DigitalAssets]\n\tif (isFinal(elections.digitalAssets))\n\t\tfinalTaxes.push(withheld(graph, digital, rates.digitalAssetRate))\n\telse included.push(digital)\n\tlet credit: string\n\tif (isDividendIncluded && corpTaxRate !== undefined) {\n\t\tconst { num, den } = rateFraction(corpTaxRate)\n\t\tcredit = graph.scale(\n\t\t\tdividend,\n\t\t\tnum,\n\t\t\tden - num,\n\t\t\tThLevyRounding.HalfUp,\n\t\t\tThLevyMeasureId.DividendCredit,\n\t\t)\n\t\tincluded.push(credit)\n\t} else {\n\t\tcredit = graph.constant(0, ThLevyMeasureId.DividendCredit)\n\t}\n\tconst assessable = sumMeasures(graph, included)\n\tconst finalWithholdingTax = sumMeasures(graph, finalTaxes, ThLevyMeasureId.FinalWithholdingTax)\n\treturn { assessable, finalWithholdingTax, dividendCredit: credit }\n}\n","import type { ThLevyGraph } from \"./graph\"\nimport { ThLevyTallyBasis } from \"./graph-types\"\nimport {\n\tTH_LEVY_CONTRIBUTION_LABELS,\n\tTH_LEVY_INCOME_LABELS,\n\ttype ThLevyContributionLabel,\n\ttype ThLevyIncomeLabel,\n} from \"./labels\"\n\n/**\n * Measure ids of every tally a TH 2026 Levy reads, keyed by the label the caller must use.\n *\n * Purpose: The caller-facing contract between exported labels and emitted Measures.\n * Math: No arithmetic; ids only.\n * Units: Tallied amounts are integer satang supplied by the caller.\n * Throws: Nothing.\n * @statute Thailand Revenue Code Sections 40(1)–(8), 47.\n */\nexport interface ThLevyTallies {\n\t/** One `gross` tally per assessable income label. */\n\treadonly income: Readonly<Record<ThLevyIncomeLabel, string>>\n\t/** One `funded` tally per contribution-driven allowance label. */\n\treadonly contributions: Readonly<Record<ThLevyContributionLabel, string>>\n}\n\nfunction tallyRecord<K extends string>(\n\tgraph: ThLevyGraph,\n\tlabels: readonly K[],\n\tbasis: ThLevyTallyBasis,\n): Readonly<Record<K, string>> {\n\tconst record = {} as Record<K, string>\n\tfor (const label of labels) record[label] = graph.tally(label, basis)\n\treturn record\n}\n\n/**\n * Emits one tally Measure per exported label, so a caller can label any subset of Flows.\n *\n * Purpose: Fixes the caller-facing contract before any statutory arithmetic depends on it.\n * Math: No arithmetic; each Measure only reads its label from the matching sim-core tally map.\n * Units: Tallied amounts are integer satang supplied by the caller.\n * Throws: Nothing; the graph validates ids only.\n *\n * @param graph Measure graph being built.\n * @statute Thailand Revenue Code Sections 40(1)–(8), 47.\n */\nexport function addThLevyTallies(graph: ThLevyGraph): ThLevyTallies {\n\treturn {\n\t\tincome: tallyRecord(graph, TH_LEVY_INCOME_LABELS, ThLevyTallyBasis.Gross),\n\t\tcontributions: tallyRecord(graph, TH_LEVY_CONTRIBUTION_LABELS, ThLevyTallyBasis.Funded),\n\t}\n}\n","import { addAllowanceMeasures } from \"./allowances\"\nimport { addAssessableMeasures } from \"./assessable\"\nimport { addMinimumGrossTax, addProgressiveTax, addRegularTax } from \"./brackets\"\nimport { addExpenseMeasures } from \"./expenses\"\nimport { ThLevyGraph } from \"./graph\"\nimport type { ThLevy, ThLevyMonthDay } from \"./graph-types\"\nimport { ThLevyMeasureId } from \"./measure-ids\"\nimport type { ThLevyProfile } from \"./profile-types\"\nimport { addSection40_4 } from \"./regimes\"\nimport { addThLevyTallies } from \"./tallies\"\n\n/** Default Levy id: Thailand Personal Income Tax for the 2026 tax year. */\nconst DEFAULT_ID = \"th-2026-pit\"\n\n/** Default period start: the calendar tax year. */\nconst DEFAULT_PERIOD: ThLevyMonthDay = { month: 1, day: 1 }\n\n/** Default Settlement: the annual return date following the tax year (Revenue Code Section 56). */\nconst DEFAULT_SETTLEMENT: ThLevyMonthDay = { month: 3, day: 31 }\n\n/**\n * Emits a Thailand 2026 Personal Income Tax rule set as sim-core Levy data.\n *\n * Purpose: Lets sim-core charge the annual PIT assessment from Flow tallies without importing\n * this package. The profile, elections, and 2026 statutory parameters are frozen at emit time;\n * income and contribution amounts arrive as Flow tallies under the exported labels.\n * Math: `due = max(progressive, minimum gross tax) + final withholding tax\n * - final withholding tax - Section 47 bis credit`, which is the calculator's `netTaxPayable`\n * when no separate sheet, real estate, or other withholding is supplied.\n * Units: Every monetary Measure is integer satang.\n * Throws: TaxValidationError for an invalid profile allowance, donation, actual expense, or rate.\n *\n * @param profile Taxpayer profile and elections fixed at emit time.\n * @statute Thailand Revenue Code Sections 40, 42 bis, 47, 47 bis, 48(1)–(2), 50(2)(g), 56.\n */\nexport function emitThLevy(profile: ThLevyProfile = {}): ThLevy {\n\tconst graph = new ThLevyGraph()\n\tconst tallies = addThLevyTallies(graph)\n\tconst section40_4 = addSection40_4(graph, tallies, profile.section40_4)\n\tconst assessable = addAssessableMeasures(graph, tallies, section40_4)\n\tconst expenses = addExpenseMeasures(graph, tallies, profile.expenses)\n\tconst allowances = addAllowanceMeasures(graph, tallies, profile, assessable.total, expenses)\n\tconst progressive = addProgressiveTax(graph, allowances.netTaxable)\n\tconst minimum = addMinimumGrossTax(graph, assessable.grossNonEmployment)\n\tconst regular = addRegularTax(graph, progressive, minimum.applicable)\n\tconst total = graph.add(regular, section40_4.finalWithholdingTax, ThLevyMeasureId.TotalTax)\n\tconst credits = graph.add(section40_4.finalWithholdingTax, section40_4.dividendCredit)\n\tconst netPayable = graph.sub(total, credits, ThLevyMeasureId.NetTaxPayable)\n\treturn graph.build(\n\t\tprofile.levy?.id ?? DEFAULT_ID,\n\t\tprofile.levy?.period ?? DEFAULT_PERIOD,\n\t\tprofile.levy?.settlement ?? DEFAULT_SETTLEMENT,\n\t\tnetPayable,\n\t)\n}\n","import type { TaxResult, TaxSystem } from \"../core/system\"\nimport { calculateThTax } from \"./calculate\"\nimport type { ThBreakdown, ThTaxInput } from \"./types\"\n\n/**\n * Thailand Tax System implementation for 2026.\n *\n * Implements TaxSystem contract for `@trajectonomics/tax/th`.\n */\nexport const th: TaxSystem<ThTaxInput, \"THB\", ThBreakdown> = {\n\tjurisdiction: \"TH\",\n\ttaxYear: 2026,\n\tcurrency: \"THB\",\n\tcalculate(input: ThTaxInput = {}): TaxResult<\"THB\", ThBreakdown> {\n\t\treturn calculateThTax(input)\n\t},\n}\n"],"mappings":"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"}