/** * 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. */ // ============================================================ // Core Union Types // ============================================================ /** 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"; // ============================================================ // TaxRegime — Country-level tax system configuration // ============================================================ /** A tax component defined by the regime (e.g., CGST, SGST, IGST, GST, VAT, STATE_TAX) */ export interface IRegimeComponent { Code: string; // "CGST", "SGST", "GST", "VAT", "STATE_TAX" Name: string; // "Central GST", "Value Added Tax" Type: TaxComponentType; // "Tax", "Cess", "Levy", etc. } /** 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; // TDS/TCS (India), Withholding Tax (US/UK/etc.) 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; // Decimal places: 0 for INR/JPY, 2 for USD/EUR/GBP LineTax: boolean; // Round each Taxes[].Amt per line item (US states) TaxComponentTotal: boolean; // Round each tax component sum across lines — CGST total, SGST total (India Section 170) DocTotal: boolean; // Round the grand total (most countries) } /** A registration/treatment type valid for this regime */ export interface ITreatment { Code: string; // "REGULAR", "COMPOSITION", "UNREGISTERED" Name: string; // "GST Registered - Regular" Description?: string; ApplicableTo: "Seller" | "Buyer" | "Both"; } /** The full TaxRegime object */ export interface ITaxRegime { _id: number; OId: number; EId: number; Country: string; // ISO 3166-1 alpha-2: "IN", "AU", "US" Region?: string; // State/Province (null = national) Code: string; // "IN_GST", "AU_GST" Name: string; // "India GST" Currency?: string; // "INR", "AUD" Components?: IRegimeComponent[]; // Optional — vocabulary of allowed component codes Features: IRegimeFeatures; Rounding: IRounding; Treatments: ITreatment[]; IsActive: boolean; } // ============================================================ // TaxCode — Tax rate definitions with flexible components // ============================================================ /** A single component within a tax code (rate definition) */ export interface ITaxCodeComponent { Code: string; // "CGST", "SGST", "IGST", "CESS", "GST" Name: string; // "Central GST" Rate: number; // 9, 14, 10, 6.25 Type: TaxComponentType; CalcMethod: CalcMethod; PerUnitAmt?: number; // For PerUnit: Rs 5/ml, $2/unit Unit?: string; // Unit of measure for PerUnitAmt: "sticks", "ml", "kg" — when different from line UoM AppliedOn: AppliedOn; // "NetAmt", "PostTax", "Qty" } /** The full TaxCode object */ export interface ITaxCode { _id: number; OId: number; EId: number; RegimeId: number; Code: string; // "GST_18_INTRA" Name: string; // "GST 18% (Intra-State)" 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; // Deprecated fields (kept during migration) Agency?: string; Type?: string; CGST?: number; SGST?: number; IGST?: number; } // ============================================================ // TaxComponent — Immutable snapshot on line items (Taxes[]) // ============================================================ /** * 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; // "CGST", "SGST", "IGST", "CESS" Rate: number; // 9, 14, 10 — historical snapshot for rate-wise GST return grouping Amt: number; // Calculated tax amount (Rate × TaxableAmt) TaxCodeId?: number; // Audit trail — which TaxCode was used // Optional snapshot fields — only stored for non-percent components (PerUnit, PerUnitPlusPercent, MaxOfPercentOrPerUnit) CalcMethod?: CalcMethod; // "PerUnit", "MaxOfPercentOrPerUnit", etc. — omitted when "Percent" (default) PerUnitAmt?: number; // The resolved per-unit amount (may come from item CessConfig override) Unit?: string; // Unit of measure for PerUnitAmt when it differs from line item UoM (e.g., "sticks" when line is "Box") } // ============================================================ // TaxSummary — Document-level aggregated totals // ============================================================ /** * 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; // "CGST", "SGST", "IGST", "VAT", "STATE_TAX" Rate: number; // Tax rate for this bucket (grouping key) TaxableAmt: number; // Sum of line-item taxable amounts (NetAmts) for this bucket Amt: number; // Tax amount for this bucket (rounded per config) } /** Document-level tax summary — stored on Invoice, Bill, CreditNote, etc. */ export interface ITaxSummary { Lines: ITaxSummaryLine[]; TotalTaxable: number; // Sum of unique line-item taxable amounts (not double-counted across components) TotalTax: number; // Total tax — sum of per-component rounded amounts RegimeCode?: string; // "IN_GST" snapshot } // ============================================================ // Withholding — Generic TDS/TCS for all countries // ============================================================ /** * 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"; } // ============================================================ // Input types for tax calculation // ============================================================ /** * 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; // Override unit when cess basis differs from line UoM (e.g., "sticks" for "₹X per 1000 sticks") } /** * 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; // default false TaxComponentTotal?: boolean; // default false DocTotal?: boolean; // default true } // ============================================================ // Document Totals — Full document computation input/output // ============================================================ /** 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; } // ============================================================ // TaxId — Tax identification numbers per country // ============================================================ /** 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; // "GSTIN", "PAN", "ABN" Value: string; // The actual tax ID value Print?: boolean; // Show on invoice/PDF? Default: true (print if undefined) } // ============================================================ // TaxExemption — Named reasons for tax exemption (v2 master) // ============================================================ /** * 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; // "EXPORT_WO_PAY", "SEZ_WO_PAY", "RESALE", "GOVT" Name: string; // "Export without payment of tax" Description?: string; AppliesTo: "Item" | "Contact" | "Both"; Category: TaxCategory; // "Exempt", "Zero", "Nil", "NonTaxable" IsActive: boolean; } // ============================================================ // TaxAuthority — Tax jurisdictions (v2, primarily for US) // ============================================================ /** * 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; // "Texas Comptroller of Public Accounts" Jurisdiction: "Federal" | "State" | "County" | "City" | "District"; Country: string; // "US" State?: string; // "TX" County?: string; // "Travis" City?: string; // "Austin" TaxIdLabel?: string; // "State Tax ID" IsActive: boolean; } // ============================================================ // ExternalProvider — Hook for external tax engines (v2) // ============================================================ /** * 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; // "Avalara AvaTax" ApiUrl?: string; // Base URL (for Custom) ApiKeyRef?: string; // Reference to secret store (NOT the actual key) Country?: string; // "US" — scope the provider to a country IsActive: boolean; }