/** * Tax System - Shared Type Definitions * * These types are used by both the UI (Angular) for tax calculation * and the API (Node.js) for validation and future server-side calculation. * * IMPORTANT: This file has NO dependencies on Angular, Mongoose, or any * framework-specific code. Pure TypeScript interfaces and types only. */ /** Tax component types — what kind of tax is this? */ export type TaxComponentType = "Tax" | "Cess" | "Levy" | "Excise" | "Surcharge" | "Duty"; /** How the tax is calculated */ export type CalcMethod = "Percent" | "PerUnit" | "PerUnitPlusPercent" | "MaxOfPercentOrPerUnit"; /** What base amount the tax applies to */ export type AppliedOn = "NetAmt" | "PostTax" | "Qty"; /** Rounding methods */ export type RoundingMethod = "Round" | "Floor" | "Ceil" | "BankersRound"; /** Tax code categories */ export type TaxCategory = "Standard" | "Reduced" | "Zero" | "Exempt" | "Nil" | "NonTaxable" | "ReverseCharge" | "Export"; /** Supply type for a transaction or tax code */ export type SupplyType = "Intra" | "Inter" | "All"; /** Resolved supply type — includes "Unknown" for missing state data */ export type ResolvedSupplyType = "Intra" | "Inter" | "Unknown"; /** Withholding type — Deducted (TDS) or Collected (TCS) */ export type WithholdingType = "Deducted" | "Collected"; /** External tax engine resolver type */ export type ResolverType = "Internal" | "External"; /** A tax component defined by the regime (e.g., CGST, SGST, IGST, GST, VAT, STATE_TAX) */ export interface IRegimeComponent { Code: string; Name: string; Type: TaxComponentType; } /** Feature flags — UI visibility toggles + feature gating. * These flags tell the UI WHAT to show. The Country field determines HOW each feature behaves. * Treatment-driven behavior (e.g., Composition scheme) is handled in code per Country + Treatment. */ export interface IRegimeFeatures { HasPlaceOfSupply: boolean; HasReverseCharge: boolean; HasWithholding: boolean; HasHSN: boolean; HasSAC: boolean; /** Tax ID field definitions for this regime. * UI reads this to build tax ID forms on Entity Settings, Customer, and Vendor screens. * Only Primary entries are shown on Customer/Vendor; all shown on Entity Settings. */ TaxIdLabels?: ITaxIdLabel[]; } /** Rounding rules for a regime. * Three rounding points control WHERE in the calculation chain rounding is applied. * LineTax and TaxComponentTotal are mutually exclusive in practice (line-level OR document-level tax rounding). * DocTotal controls whether the grand total is rounded independently. * * Country presets: * India GST: { Method: "Round", Precision: 0, TaxComponentTotal: true, DocTotal: true } * US Sales Tax: { Method: "Round", Precision: 2, LineTax: true, DocTotal: true } * Japan CT: { Method: "Floor", Precision: 0, TaxComponentTotal: true } * EU/UK/AU: { Method: "Round", Precision: 2, DocTotal: true } */ export interface IRounding { Method: RoundingMethod; Precision: number; LineTax: boolean; TaxComponentTotal: boolean; DocTotal: boolean; } /** A registration/treatment type valid for this regime */ export interface ITreatment { Code: string; Name: string; Description?: string; ApplicableTo: "Seller" | "Buyer" | "Both"; } /** The full TaxRegime object */ export interface ITaxRegime { _id: number; OId: number; EId: number; Country: string; Region?: string; Code: string; Name: string; Currency?: string; Components?: IRegimeComponent[]; Features: IRegimeFeatures; Rounding: IRounding; Treatments: ITreatment[]; IsActive: boolean; } /** A single component within a tax code (rate definition) */ export interface ITaxCodeComponent { Code: string; Name: string; Rate: number; Type: TaxComponentType; CalcMethod: CalcMethod; PerUnitAmt?: number; Unit?: string; AppliedOn: AppliedOn; } /** The full TaxCode object */ export interface ITaxCode { _id: number; OId: number; EId: number; RegimeId: number; Code: string; Name: string; Category: TaxCategory; Components: ITaxCodeComponent[]; /** * Pre-computed sum of PERCENTAGE-based rates. * * DISPLAY HINT ONLY — do NOT use for matching logic. * When PerUnit or PostTax components exist, CombinedRate does NOT represent * the true effective tax burden. Use Code/_id for programmatic matching. */ CombinedRate: number; SupplyType: SupplyType; EffectiveFrom?: Date | string; EffectiveTo?: Date | string; IsActive: boolean; Agency?: string; Type?: string; CGST?: number; SGST?: number; IGST?: number; } /** * Lean tax component snapshot stored on each line item. * * DESIGN PRINCIPLE: Store results of multiplications, not additions. * - Amt = Rate × TaxableAmt (multiplication → stored) * - TaxableAmt is derivable from line item fields (Qty × UnitPrice - Discount) * so NOT stored here — line item is the source of truth. * - TaxAmt (sum of Taxes[].Amt) is a simple addition → computed on the fly via sumTaxComponents(). * * CalcMethod and PerUnitAmt are optional snapshot fields for non-percent cess. * They capture the resolved calculation parameters at save time so the invoice * remains self-contained and reproducible even if item cess config changes later. * For standard percent-based components (CGST, SGST, IGST), these are omitted. * * NOTE: ITC is NOT stored here. ITC is at the line-item and document * level (purchase-side only). */ export interface ITaxComponent { Code: string; Rate: number; Amt: number; TaxCodeId?: number; CalcMethod?: CalcMethod; PerUnitAmt?: number; Unit?: string; } /** * TaxSummary is computed by shareneus at save time and stored on the document. * Line-item Taxes[] remains the source of truth. TaxSummary is a materialized * snapshot for reporting efficiency — avoids complex $unwind/$group in aggregation. * * Grouped by Code+Rate (e.g., CGST@9, CGST@14 are separate entries). * Rounding is applied per the RoundingConfig: * - If TaxComponentTotal ON: each entry's Amt is rounded to Precision * - If LineTax ON: Amts are already rounded at line level, summary just sums them * - Otherwise: Amts are at currency precision (2 decimals) */ /** One line in the TaxSummary — grouped by Code+Rate */ export interface ITaxSummaryLine { Code: string; Rate: number; TaxableAmt: number; Amt: number; } /** Document-level tax summary — stored on Invoice, Bill, CreditNote, etc. */ export interface ITaxSummary { Lines: ITaxSummaryLine[]; TotalTaxable: number; TotalTax: number; RegimeCode?: string; } /** * Withholding tax is a generic concept covering: * - India: TDS (Section 51) and TCS (Section 52) * - USA: Federal/State withholding tax * - UK: CIS (Construction Industry Scheme) * - Australia: PAYG withholding * - Brazil: IRRF * * Stored on: Invoice, Bill, SalesReceipt — at document level. * A document can have multiple withholding entries (e.g., TDS + TCS, * or federal + state withholding in the US). * * The Withholding[] array replaces the old separate TDS/TCS sub-schemas. */ export interface IWithholding { /** "Deducted" = TDS/Withholding (buyer deducts from payment to seller) * "Collected" = TCS (seller collects from buyer and deposits to govt) */ Type: WithholdingType; /** Legal section or code reference * India TDS: "51" (GST) or "194C", "194J" (Income Tax) * India TCS: "52" (GST) or "206C(1H)" (Income Tax) * US: "FITW" (Federal Income Tax Withholding) */ Section: string; /** Tax rate — total withholding rate */ Rate: number; /** Base amount on which withholding is calculated */ BaseAmt: number; /** Calculated withholding amount */ Amt: number; /** Who is liable to deduct/collect and deposit * "Buyer" for TDS, "Seller" for TCS */ PartyLiable?: "Buyer" | "Seller"; } /** Input for withholding calculation */ export interface IWithholdingCalcInput { Type: WithholdingType; Section: string; Rate: number; BaseAmt: number; PartyLiable?: "Buyer" | "Seller"; } /** * Component-level overrides for tax calculation. * * Keyed by component Code (e.g., "CESS"). When provided, these values * replace the TaxCode component's defaults at calculation time. * * Primary use case: CESS rates are HSN-specific, so the TaxCode defines * the structure (CalcMethod, AppliedOn) while the actual Rate/PerUnitAmt * comes from the item master's CessConfig. * * Only Rate and PerUnitAmt can be overridden — structural fields (Code, * Name, Type, CalcMethod, AppliedOn) always come from the TaxCode. */ export interface IComponentOverride { Rate?: number; PerUnitAmt?: number; Unit?: string; } /** * Base fields for computing taxable amount (NetAmt) from line item quantities and prices. * NetAmt = (Qty × UnitPrice) - Disc - RecDisc * * Generic across both goods and services: * For Items (goods): UnitPrice maps to UnPr (unit price) * For Ops (services): UnitPrice maps to Pr (operation rate) */ export interface ILineAmountFields { /** Quantity */ Qty: number; /** Unit price — Items: UnPr, Ops: Pr */ UnitPrice: number; /** Line-level discount amount */ Disc?: number; /** Record-level (document-level) discount allocated to this line */ RecDisc?: number; } /** Input context for calculating tax on a single line item */ export interface ITaxCalcLineInput extends ILineAmountFields { /** The TaxCode to apply */ TaxCode: ITaxCode; /** Is this a reverse charge line? */ RCM?: boolean; /** * Optional component-level overrides, keyed by component Code. * * Used when CESS Rate/PerUnitAmt comes from item master (CessConfig) * rather than the TaxCode itself (which has placeholder values like Rate: 0). * * @example * // Item has CessRate: 12 (percent cess from HSN config) * ComponentOverrides: { CESS: { Rate: 12 } } * * @example * // Item has CessPerUnitAmt: 5 (₹5/liter from HSN config) * ComponentOverrides: { CESS: { PerUnitAmt: 5 } } */ ComponentOverrides?: Record; } /** Input context for computing document-level TaxSummary. * * Since TaxableAmt is NOT stored on TaxComponent (it's derivable from line item fields), * each line provides Qty/UnitPrice/Disc/RecDisc and shareneus computes NetAmt internally. */ export interface ITaxSummaryInput { /** All line items with amount fields and calculated Taxes[] arrays */ Lines: Array; /** Tax regime code for the snapshot */ RegimeCode?: string; /** Rounding config — if provided, applies TaxComponentTotal rounding to summary Amts */ Rounding?: IRoundingConfig; } /** * Rounding settings passed to calculator functions. * * Method + Precision define HOW to round. * LineTax / TaxComponentTotal / DocTotal define WHERE (which rounding points are active). * * When passed to calculateLineTax: * - LineTax ON → round each Taxes[].Amt to Precision * - LineTax OFF → round to standard currency precision (2 decimals) * * When passed to computeTaxSummary: * - TaxComponentTotal ON → round each Code+Rate bucket's Amt to Precision * * When passed to computeDocumentTotals: * - DocTotal ON → round the grand total to Precision */ export interface IRoundingConfig { Method: RoundingMethod; Precision: number; LineTax?: boolean; TaxComponentTotal?: boolean; DocTotal?: boolean; } /** Input for computing all document-level totals in one call */ export interface IDocumentTotalsInput { /** All line items with amount fields (Qty, UnitPrice, Disc, RecDisc) and Taxes[] */ Lines: Array; /** Rounding configuration (from TaxRegime.Rounding or Entity Settings) */ Rounding: IRoundingConfig; /** Tax regime code for the TaxSummary snapshot */ RegimeCode?: string; /** Optional adjustment to grand total before rounding (e.g., for round-off entries) */ Adjust?: number; } /** Result of full document totals computation */ export interface IDocumentTotals { /** Sum of all gross line values before discounts and tax */ SubTotal: number; /** Sum of line discounts + prorated record/document discount allocations */ Discount: number; /** Sum of all line NetAmts after discount allocation, before tax */ TaxableAmount: number; /** Total tax amount — sum of rounded component Amts per RoundingConfig */ TaxTotal: number; /** Rate-wise tax breakdown — grouped by Code+Rate, rounded per config */ TaxSummary: ITaxSummaryLine[]; /** TaxableAmount + TaxTotal (before DocTotal rounding) */ GrandTotal: number; /** Rounding adjustment: RoundedTotal - GrandTotal (0 if DocTotal is off) */ Round: number; /** Final amount: GrandTotal + Round */ Total: number; } /** Defines a tax ID field on TaxRegime — the source of truth for what tax IDs a regime needs. * UI reads TaxRegime.Features.TaxIdLabels[] to build form fields dynamically. * Entity Settings shows all entries; Customer/Vendor shows only Primary entries. */ export interface ITaxIdLabel { /** Display label — "GSTIN", "PAN", "CIN", "ABN", "EIN", "VAT No" */ Label: string; /** Regex pattern for format validation (e.g., "^[0-9]{2}[A-Z]{5}..." for GSTIN) */ RegexValidate?: string; /** UI hint — should the form field be required? (not a mongoose-level required) */ Required?: boolean; /** Show on Customer/Vendor screens too? If false/undefined, only shown on Entity Settings. */ Primary?: boolean; } /** A stored tax ID entry on Entity Settings, Customer, or Vendor */ export interface ITaxIdEntry { Label: string; Value: string; Print?: boolean; } /** * Tax exemption reasons — used when a line item or contact is exempt from tax. * * v1 approach: Store ExmReason directly on transaction lines/headers. * v2 approach: Create TaxExemption master collection for standardized reasons. * * In India GST, the reason matters for return filing: * - "EXPORT_WO_PAY" -> Export without payment of tax (GSTR1 Table 6A) * - "SEZ_WO_PAY" -> Supply to SEZ without payment * - "NIL_RATED" -> Goods/services that are nil rated (0% by law) * - "EXEMPT" -> Goods/services specifically exempt from GST * - "NON_GST" -> Outside the scope of GST entirely * * In US, the reason matters for audit: * - "RESALE" -> Buyer has a resale certificate * - "GOVT" -> Government entity (exempt in most states) * - "NONPROFIT" -> Registered nonprofit organization */ export interface ITaxExemption { _id: number; OId: number; EId: number; RegimeId?: number; Code: string; Name: string; Description?: string; AppliesTo: "Item" | "Contact" | "Both"; Category: TaxCategory; IsActive: boolean; } /** * Tax Authority represents a government body that levies and collects tax. * * Not needed for India v1. Critical for US sales tax where each * state, county, city, and special district can levy tax independently. */ export interface ITaxAuthority { _id: number; OId: number; EId: number; Name: string; Jurisdiction: "Federal" | "State" | "County" | "City" | "District"; Country: string; State?: string; County?: string; City?: string; TaxIdLabel?: string; IsActive: boolean; } /** * Configuration for an external tax calculation provider * (Avalara, TaxJar, Vertex). Not implemented in v1. */ export interface IExternalProvider { _id: number; OId: number; EId: number; Engine: "Avalara" | "TaxJar" | "Vertex" | "Custom"; Name: string; ApiUrl?: string; ApiKeyRef?: string; Country?: string; IsActive: boolean; }