Tax Withholding Procedures
1. Executive Summary
This document specifies the step-by-step data mutation procedures for the tax withholding domain. It covers the complete lifecycle of tax-related operations: maintaining party tax profiles, configuring jurisdiction-specific rates, calculating withholding and VAT amounts during worksheet processing, persisting audit records, generating tax forms from actual transaction data, and assembling filing packages for regulatory submission.
Every procedure is described at the data level — which tables are inserted, updated, or queried; which fields are set to which values; and the sequencing constraints between operations. The procedures span five core tables (party_tax_info, tax_rate_config, tax_calculation_audit, tax_form, tax_filing_package) and integrate with the billing item deduction module for applying calculated withholding as deductions on worksheet receivables.
Related foundation documents:
- Tax Withholding Data Model — table definitions, field catalogs, relationships
- Tax Withholding Queries — read operations and aggregation patterns
2. Key Procedures
2.1 Party Tax Profile Management
2.1.1 Create Party Tax Profile
Operation: createPartyTaxProfile
Trigger: User creates or replaces a party's tax profile through the party detail screen.
Input Parameters:
partyId: Integer —party_tax_info.party_idtaxResidenceCd: String (optional) —party_tax_info.tax_residence_cdentityTypeCd: String (optional) —party_tax_info.entity_type_cdtinTypeCd: String (optional) —party_tax_info.tin_type_cdtinNumber: String (optional) —party_tax_info.tin_numbervatRegisteredInd: Boolean —party_tax_info.vat_registered_indvatNumber: String (optional) —party_tax_info.vat_numberhasW9OnFileInd: Boolean —party_tax_info.has_w9_on_file_indhasW8OnFileInd: Boolean —party_tax_info.has_w8_on_file_indw8FormTypeCd: String (optional) —party_tax_info.w8_form_type_cdw8ExpirationDt: Date (optional) —party_tax_info.w8_expiration_dthasCwaApprovalInd: Boolean —party_tax_info.has_cwa_approval_indcwaExpirationDt: Date (optional) —party_tax_info.cwa_expiration_dtcwaWithholdingAmt: Decimal string (optional) —party_tax_info.cwa_withholding_amthasTaxTreatyInd: Boolean —party_tax_info.has_tax_treaty_indtreatyCountryCd: String (optional) —party_tax_info.treaty_country_cdeffectiveFromDt: Date (optional) —party_tax_info.effective_from_dtactorId: String — identifier for the user performing the operation
Creates a new tax profile for a party. Implements effective dating: the previously current profile is automatically deactivated before inserting the new one. This ensures exactly one active profile per party at any time.
Step 1. Deactivate Existing Current Profile
- Source:
party_tax_infowhereparty_id = :partyId AND current_item_ind = true. - Action: UPDATE
party_tax_info. - Logic:
- Set
party_tax_info.current_item_ind=false. - Set
party_tax_info.effective_to_dt= current date (YYYY-MM-DD). - Set
party_tax_info.updated_by=:actorId. - Set
party_tax_info.updated_dt= current timestamp.
- Set
Step 2. Insert New Current Profile
- Source: Input parameters supplied by the caller.
- Action: INSERT into
party_tax_info. - Logic:
- Set
party_tax_info.party_id=:partyId. - Set
party_tax_info.tax_residence_cd=:taxResidenceCd(ornull). - Set
party_tax_info.entity_type_cd=:entityTypeCd(ornull). - Set
party_tax_info.tin_type_cd=:tinTypeCd(ornull). - Set
party_tax_info.tin_number=:tinNumber(ornull). - Set
party_tax_info.vat_registered_ind=:vatRegisteredInd. - Set
party_tax_info.vat_number=:vatNumber(ornull). - Set
party_tax_info.has_w9_on_file_ind=:hasW9OnFileInd. - Set
party_tax_info.has_w8_on_file_ind=:hasW8OnFileInd. - Set
party_tax_info.w8_form_type_cd=:w8FormTypeCd(ornull). - Set
party_tax_info.w8_expiration_dt=:w8ExpirationDt(ornull). - Set
party_tax_info.has_cwa_approval_ind=:hasCwaApprovalInd. - Set
party_tax_info.cwa_expiration_dt=:cwaExpirationDt(ornull). - Set
party_tax_info.cwa_withholding_amt=:cwaWithholdingAmt(ornull). - Set
party_tax_info.has_tax_treaty_ind=:hasTaxTreatyInd. - Set
party_tax_info.treaty_country_cd=:treatyCountryCd(ornull). - Set
party_tax_info.effective_from_dt=:effectiveFromDt(or current date). - Set
party_tax_info.effective_to_dt=null(open-ended, currently active). - Set
party_tax_info.current_item_ind=true. - Set
party_tax_info.created_by=:actorId. - Set
party_tax_info.created_dt= current timestamp.
- Set
IMPORTANT
Steps 1 and 2 must execute atomically within a single transaction. If the insert fails, the prior profile must not remain deactivated.
Side-effects:
- The previously active
party_tax_inforecord for this party is end-dated and marked inactive.
Postconditions:
- Exactly one
party_tax_inforecord for the party hascurrent_item_ind = true. - The new profile is the active profile for all subsequent tax calculations.
- The generated
party_tax_info_idis returned to the caller.
2.1.2 Update Party Tax Profile
Operation: updatePartyTaxProfile
Trigger: User modifies an individual field on the party tax profile (e.g., updating CWA withholding amount, changing residency code).
Input Parameters:
partyTaxInfoId: Integer —party_tax_info.party_tax_info_id- Any updatable field (partial update):
tax_residence_cd,entity_type_cd,tin_type_cd,tin_number,vat_registered_ind,vat_number,has_w9_on_file_ind,has_w8_on_file_ind,w8_form_type_cd,w8_expiration_dt,has_cwa_approval_ind,cwa_expiration_dt,cwa_withholding_amt,has_tax_treaty_ind,treaty_country_cd,effective_from_dt,effective_to_dt,current_item_ind actorId: String — identifier for the user performing the operation
Updates fields on an existing party_tax_info record. Only provided fields are modified; absent fields remain unchanged.
Step 1. Apply Field Updates
- Source: Input parameters supplied by the caller.
- Action: UPDATE
party_tax_infowhereparty_tax_info_id = :partyTaxInfoId. - Logic:
- For each provided field, set the corresponding column to the new value.
- Always set
party_tax_info.updated_by=:actorId. - Always set
party_tax_info.updated_dt= current timestamp.
Side-effects:
- None beyond the field modifications on the targeted record.
Postconditions:
- The
party_tax_inforecord reflects the updated field values.
2.1.3 Deactivate Party Tax Profile
Operation: deactivatePartyTaxProfile
Trigger: User retires a tax profile without creating a replacement.
Input Parameters:
partyTaxInfoId: Integer —party_tax_info.party_tax_info_idactorId: String — identifier for the user performing the operation
Marks a tax profile as no longer current. Used when a profile must be retired without creating a replacement.
Step 1. Mark Profile Inactive
- Source:
party_tax_inforecord identified by:partyTaxInfoId. - Action: UPDATE
party_tax_infowhereparty_tax_info_id = :partyTaxInfoId. - Logic:
- Set
party_tax_info.current_item_ind=false. - Set
party_tax_info.effective_to_dt= current date (YYYY-MM-DD). - Set
party_tax_info.updated_by=:actorId. - Set
party_tax_info.updated_dt= current timestamp.
- Set
Side-effects:
- None. The party will have no active tax profile until a new one is created.
Postconditions:
- The deactivated record has
current_item_ind = falseandeffective_to_dtset to the current date.
2.2 Tax Rate Configuration Management
2.2.1 Create Tax Rate Configuration
Operation: createTaxRateConfig
Trigger: Administrator configures a new tax rate, such as a UK personal allowance change for the next tax year.
Input Parameters:
jurisdictionCd: String —tax_rate_config.jurisdiction_cd(USorGB)taxTypeCd: String —tax_rate_config.tax_type_cd(one of:WITHHOLDING_NRA,WITHHOLDING_FEU,VAT_ARTIST_FEE,VAT_COMMISSION,PERSONAL_ALLOWANCE,IRS_DEPOSIT_THRESHOLD,TAX_YEAR)rate: Decimal string (optional) —tax_rate_config.ratethresholdAmt: Decimal string (optional) —tax_rate_config.threshold_amtcurrencyCd: String (optional) —tax_rate_config.currency_cdeffectiveFromDt: Date (required) —tax_rate_config.effective_from_dteffectiveToDt: Date (optional) —tax_rate_config.effective_to_dtactorId: String — identifier for the user performing the operation
Adds a new effective-dated tax rate or threshold for a jurisdiction and tax type.
Step 1. Insert Rate Configuration Record
- Source: Input parameters supplied by the administrator.
- Action: INSERT into
tax_rate_config. - Logic:
- Set
tax_rate_config.jurisdiction_cd=:jurisdictionCd. - Set
tax_rate_config.tax_type_cd=:taxTypeCd. - Set
tax_rate_config.rate=:rate(decimal fraction, e.g.,0.3000for 30%; ornullfor threshold-only entries). - Set
tax_rate_config.threshold_amt=:thresholdAmt(e.g.,12570.00for UK personal allowance; ornull). - Set
tax_rate_config.currency_cd=:currencyCd(e.g.,GBP; ornullwhen no threshold). - Set
tax_rate_config.effective_from_dt=:effectiveFromDt. - Set
tax_rate_config.effective_to_dt=:effectiveToDt(ornullfor open-ended). - Set
tax_rate_config.created_by=:actorId. - Set
tax_rate_config.created_dt= current timestamp.
- Set
NOTE
To supersede an existing rate, insert a new record with a later effective_from_dt. Do not modify the existing record. Rate resolution always selects the most recently effective record whose date range contains the target date (ordered by effective_from_dt DESC, limit 1).
Side-effects:
- None. Historical records for the same jurisdiction and tax type are unaffected.
Postconditions:
- A new
tax_rate_configrecord exists for the given jurisdiction, tax type, and effective date range. - The generated
tax_rate_config_idis returned to the caller.
2.2.2 Update Tax Rate Configuration
Operation: updateTaxRateConfig
Trigger: Administrator end-dates an existing rate record when a successor has been created, or corrects a data entry error.
Input Parameters:
taxRateConfigId: Integer —tax_rate_config.tax_rate_config_idrate: Decimal string (optional) — new rate valuethresholdAmt: Decimal string (optional) — new threshold amounteffectiveToDt: Date (optional) — end date to setactorId: String — identifier for the user performing the operation
Modifies an existing rate configuration record in place.
Step 1. Apply Updates to Rate Record
- Source:
tax_rate_configrecord identified by:taxRateConfigId. - Action: UPDATE
tax_rate_configwheretax_rate_config_id = :taxRateConfigId. - Logic:
- If
:rateprovided: settax_rate_config.rate=:rate. - If
:thresholdAmtprovided: settax_rate_config.threshold_amt=:thresholdAmt. - If
:effectiveToDtprovided: settax_rate_config.effective_to_dt=:effectiveToDt. - Set
tax_rate_config.updated_by=:actorId. - Set
tax_rate_config.updated_dt= current timestamp.
- If
Side-effects:
- None beyond the field modifications on the targeted record.
Postconditions:
- The
tax_rate_configrecord reflects the updated values.
2.3 Calculate Withholding Tax for a Billing Item
Operation: calculateForBillingItem
Trigger: Worksheet tax preview is requested when receivables are displayed, or the user opens the billing item deduction dialog.
Input Parameters:
partyId: Integer —billing_item.client_idorbilling_item.contracted_party_idbillingEntityCd: String — derived fromuta_entity.uta_entity_name(e.g.,US_AGENCY,UK_AGENCY)grossAmt: Decimal — frombilling_item_detail.billing_item_detail_gross_amt(REV type)serviceLocationCd: String (optional) — frombilling_item.service_country_cdcommissionRate: Decimal (optional) — commission rate for VAT calculation; defaults to0.10ukYtdEarnings: Decimal — cumulative prior UK earnings for the current UK tax yearukExpectedYtdTotal: Decimal — total billing item amounts for this party in the current UK tax yearukNonGbpCurrencyInd: Boolean (optional) — whether the fee is in a non-GBP currencyukFeeCurrencyCd: String (optional) — currency code of the non-GBP feeukFxRate: Decimal (optional) — FX rate (units of fee currency per 1 GBP)worksheetAmt: Decimal (optional) — split amount, used for VAT pass-through split validationuserId: Integer (optional) — user triggering the calculation
This is the primary tax calculation procedure. It determines withholding tax, VAT obligations, required regulatory forms, and compliance alerts for a single billing item, and persists a full audit trail.
Step 1. Build Withholding Context
- Source:
party_tax_infowhereparty_id = :partyId AND current_item_ind = true. - Action: READ from
party_tax_info. - Logic:
- If no profile exists, return an empty result with warning: "No tax profile on file for this party. Tax calculation skipped."
- Evaluate document validity: if
has_cwa_approval_ind = trueandcwa_expiration_dt < current date, treat CWA as inactive for this calculation. - Evaluate W-8 expiry: if
has_w8_on_file_ind = trueandw8_expiration_dt < current date, note for compliance alert. - Map party profile and deal context to withholding context:
billing_entity=:billingEntityCdgross_amount=:grossAmttalent_residence=party_tax_info.tax_residence_cdservice_location=:serviceLocationCdhas_cwa_approval=party_tax_info.has_cwa_approval_ind AND NOT cwa_expiredcwa_withholding_amt=party_tax_info.cwa_withholding_amt(only if CWA valid; otherwise0)has_tax_treaty=party_tax_info.has_tax_treaty_indtreaty_country=party_tax_info.treaty_country_cduk_ytd_earnings=:ukYtdEarningsaware_of_exceeding_threshold=truewhen:ukExpectedYtdTotal >= 12570vat_registered_ind=party_tax_info.vat_registered_ind
IMPORTANT
The aware_of_exceeding_threshold flag enables proactive FEU withholding. When the total expected UK earnings for the tax year (sum of all billing items for this party with UK entities in the current UK tax year) meets or exceeds 12,570 GBP, this flag is set to true. This causes 20% FEU withholding to be applied even when cumulative actuals are still below the personal allowance.
Step 2. Determine Jurisdiction and Calculate Withholding
- Source: Withholding context from Step 1.
- Action: Apply jurisdiction-specific calculation rules.
- Logic:
- Non-resident determination: a party is non-resident when
tax_residence_cddiffers from the billing entity's home jurisdiction (USforUS_AGENCY,GBforUK_AGENCY). Whentax_residence_cdis null, treat as non-resident. - Route by billing entity and residency:
US_AGENCY+ US resident: no withholding, 1099-NEC reporting only.US_AGENCY+ non-resident + service outside US: no withholding (foreign-source income).US_AGENCY+ non-resident + service in US or unknown: apply US NRA rules (Step 2a).UK_AGENCY+ UK resident: no FEU withholding; proceed to VAT calculation (Step 3).UK_AGENCY+ non-resident + service outside UK: no FEU withholding (not UK-source income).UK_AGENCY+ non-resident + service in UK or unknown: apply UK FEU rules (Step 2b).
- Non-resident determination: a party is non-resident when
Step 2a. US NRA Withholding
- Source: Withholding context and
tax_rate_config. - Action: Apply US NRA exemption priority rules.
- Logic:
- Check CWA first: if
has_cwa_approval = true(not expired):- If
cwa_withholding_amt > 0: withholding =cwa_withholding_amt(flat dollar amount, rounded to nearest whole number). - If
cwa_withholding_amt = 0: withholding = 0 with warning "CWA amount not configured."
- If
- Else check treaty: if
has_tax_treaty = true: withholding = 0. Note treaty country. - Else default: query
tax_rate_configwherejurisdiction_cd = 'US'andtax_type_cd = 'WITHHOLDING_NRA'andeffective_from_dt <= current_dateand (effective_to_dt IS NULL OR effective_to_dt >= current_date), ordered byeffective_from_dt DESC, limit 1. Fall back to0.30if no record found. Withholding =ROUND(gross_amount * rate). - Output: deduction item with
billing_item_detail_type_cd = 'PAY',billing_item_deduction_type_cd = 'WH_US_NRA'.
- Check CWA first: if
Step 2b. UK FEU Withholding
- Source: Withholding context and
tax_rate_config. - Action: Apply UK FEU cumulative threshold and currency conversion rules.
- Logic:
- If fee is non-GBP:
current_gross_gbp = ROUND(gross_amount / uk_fx_rate, 2). Otherwisecurrent_gross_gbp = gross_amount. cumulative = uk_ytd_earnings + current_gross_gbp.- If
cumulative < 12570 AND NOT aware_of_exceeding_threshold: withholding = 0 (below threshold, no proactive flag). - Otherwise: query
tax_rate_configwherejurisdiction_cd = 'GB'andtax_type_cd = 'WITHHOLDING_FEU', same date logic. Fall back to0.20.withholding_amt_gbp = ROUND(current_gross_gbp * rate). - If non-GBP:
withholding_amt_original = ROUND(withholding_amt_gbp * uk_fx_rate)(for display). - When
aware_of_exceeding_threshold = true:total_feu_liability = ROUND(cumulative * rate)(informational, included in explanation). - Output: deduction item with
billing_item_detail_type_cd = 'PAY',billing_item_deduction_type_cd = 'WH_UK_FEU'.
- If fee is non-GBP:
Step 3. Calculate VAT (UK Agency Only)
- Source: Withholding context and
tax_rate_config. Applies only whenbillingEntityCd = 'UK_AGENCY'. - Action: Evaluate VAT applicability and compute amounts.
- Logic:
- Artist Fee VAT (pass-through): Applies when UK Agency + UK resident + UK show (
service_location = 'GB') +vat_registered_ind = true. Querytax_rate_configforGB/VAT_ARTIST_FEE(fallback:0.20).artist_vat_amt = FLOOR(gross_amt * vat_rate). This is a pass-through — charged to the buyer on top of the gross invoice. It is NOT a billing item deduction; it is returned as informationalvat_pass_throughdata. - Commission VAT (deduction): Applies when UK Agency + UK resident.
commission_amt = ROUND(gross_amt * commission_rate, 2). Querytax_rate_configforGB/VAT_COMMISSION(fallback:0.20).comm_vat_amt = FLOOR(commission_amt * vat_rate). This is UTA's VAT obligation on agency commission; it IS a deduction reducing client payout. Output: deduction item withbilling_item_detail_type_cd = 'PAY',billing_item_deduction_type_cd = 'VAT_COMM'. - Split validation: When artist fee VAT applies and
:worksheetAmtis provided:expected_amt = gross_amt + artist_vat_amt. Ifworksheet_amt < expected_amt - 0.005, generate a split validation warning.
- Artist Fee VAT (pass-through): Applies when UK Agency + UK resident + UK show (
Step 4. Determine Required Regulatory Forms
- Source: Calculation results from Steps 2 and 3.
- Action: Evaluate form requirement conditions.
- Logic:
- US Agency + non-resident + any US-source payment: require
IRS_1042_S,IRS_1042,IRS_1042_T. - US Agency + non-resident + CWA in effect: additionally require
IRS_CWA_Approval. - US Agency + US resident + gross >= $600: require
IRS_1099_NEC. - UK Agency + FEU withholding > 0: require
HMRC_FEU2,HMRC_FEU1. - UK Agency + any UK VAT > 0: require
HMRC_VAT_RETURN.
- US Agency + non-resident + any US-source payment: require
Step 5. Generate Compliance Alerts
- Source: Calculation results and deal context.
- Action: Evaluate compliance rules and produce sorted alert list.
- Logic:
IRS_3_DAY_RULE(CRITICAL): US accumulated withholding liability >= $2,000 — deposit required within 3 business days via EFTPS.IRS_3_DAY_APPROACHING(WARNING): US accumulated liability >= 80% of $2,000.UK_PERSONAL_ALLOWANCE_EXCEEDED(WARNING): UK cumulative earnings cross 12,570 GBP with this payment.UK_FEU_FILING_REQUIRED(INFO): UK FEU withholding > 0.CWA_IN_EFFECT(INFO): CWA active with a configured amount.CWA_AMOUNT_NOT_CONFIGURED(WARNING): CWA active butcwa_withholding_amt = 0.TREATY_FORM_8233_REQUIRED(INFO): non-resident with tax treaty (US).- Alerts are returned sorted by severity: CRITICAL → WARNING → INFO.
Step 6. Persist Audit Record
- Source: All outputs from Steps 1 through 5 plus point-in-time snapshots of reference data.
- Action: INSERT into
tax_calculation_audit. - Logic:
- Set
tax_calculation_audit.context_type_cd='DEDUCTION_APPLIED'. - Set
tax_calculation_audit.context_id=null(orcash_receipt_worksheet_idif available). - Set
tax_calculation_audit.input_json= JSON snapshot of{ partyId, dealContext, withholdingContext }. - Set
tax_calculation_audit.result_json= JSON snapshot of{ withholdingResult, vatResult, deductions, vatPassThrough, commissionVatPassThrough, splitValidation }. - Set
tax_calculation_audit.party_tax_info_snapshot_json= full snapshot of theparty_tax_inforecord used at calculation time. - Set
tax_calculation_audit.rate_config_snapshot_json= map of all activetax_rate_configrates for the jurisdiction at calculation time (see procedure 2.13). - Set
tax_calculation_audit.calculated_by_user_id=:userId(if provided). - Set
tax_calculation_audit.calculated_dt= current timestamp.
- Set
IMPORTANT
The party_tax_info_snapshot_json and rate_config_snapshot_json fields capture the exact reference data used at calculation time. This ensures compliance traceability even if the party's tax profile or the tax rates change after the calculation was performed. Audit records are write-once and must never be updated after creation.
Side-effects:
- Creates one
tax_calculation_auditrecord withcontext_type_cd = 'DEDUCTION_APPLIED'.
Postconditions:
- A
TaxDeductionResultis returned to the caller, containing: the list of deduction items, warnings, forms required, compliance alerts, the generatedtax_calculation_audit_id, a calculation summary (badge label, withholding and VAT totals), and acalculationBreakdown(gross amount, rate, withholding amount, UK cumulative earnings if applicable).
2.4 Split-Level Tax Warning
Operation: getTaxWarningsForParty
Trigger: A party reference is added to a cash_receipt_split.
Input Parameters:
partyId: Integer —party_tax_info.party_idbillingEntityCd: String —US_AGENCYorUK_AGENCYsplitAmt: Decimal (optional) —cash_receipt_split.split_amt
Generates early tax warnings when a party reference is added to a cash receipt split. These warnings are informational and do not create deductions — they alert users to potential withholding obligations before worksheet processing begins.
Step 1. Retrieve Party Tax Profile
- Source:
party_tax_infowhereparty_id = :partyId AND current_item_ind = true. - Action: READ from
party_tax_info. - Logic:
- If no profile found: return warning "No tax profile on file — withholding status unknown."
Step 2. Evaluate Residency and Determine Warnings
- Source:
party_tax_inforecord from Step 1. - Action: Apply residency-based warning rules.
- Logic:
- US Agency + non-US resident + no CWA + no treaty: warn "Non-resident alien — 30% NRA withholding expected." Query
tax_rate_configfor estimated rate. AddIRS_1042_Sto forms required. - US Agency + non-US resident + CWA active (not expired): warn "CWA in effect — $[amount] IRS-approved withholding." If CWA expired: warn "CWA expired — 30% NRA withholding may apply."
- US Agency + non-US resident + treaty: warn "Tax treaty applies — reduced or zero withholding."
- US Agency + US resident: warn "US resident — no withholding (1099-NEC reporting)."
- UK Agency + non-UK resident: warn "Non-UK resident — FEU withholding may apply (20% above £12,570 YTD)." Query
tax_rate_configfor estimated rate. AddFEU1,FEU2to forms required. - UK Agency + UK resident +
vat_registered_ind = true: warn "UK VAT-registered — VAT may apply on artist fee and commission." - UK Agency + UK resident: warn "UK resident — no FEU withholding."
- US Agency + non-US resident + no CWA + no treaty: warn "Non-resident alien — 30% NRA withholding expected." Query
Step 3. Persist Split Warning Audit Record
- Source: Warning results from Step 2.
- Action: INSERT into
tax_calculation_audit. - Logic:
- Set
tax_calculation_audit.context_type_cd='SPLIT_WARNING'. - Set
tax_calculation_audit.input_json= JSON:{ partyId, billingEntityCd, splitAmt }. - Set
tax_calculation_audit.result_json= JSON:{ warnings, estimatedRate, formsRequired }. - Set
tax_calculation_audit.party_tax_info_snapshot_json= currentparty_tax_inforecord snapshot.
- Set
Side-effects:
- Creates one
tax_calculation_auditrecord withcontext_type_cd = 'SPLIT_WARNING'.
Postconditions:
- A
SplitTaxWarningobject is returned containing:partyId,partyName,jurisdictionCd,warnings,estimatedWithholdingRate,formsRequired,calculatedDt. This result is suitable for storing incash_receipt_split.tax_warning_json.
2.5 Auto-Apply Tax Deductions to Billing Items
Operation: autoApplyTaxDeductions
Trigger: Tax previews load on the worksheet receivables table (batch entry point from the worksheet UI).
Input Parameters:
taxPreviewMap: Map ofbilling_item_idtoTaxDeductionResult— output of procedure 2.6actorId: String — identifier for the system or user performing the operation ('TAX_AUTO'for automated application)
Automatically creates missing withholding deductions on billing item details based on tax preview calculations. This procedure is additive only — it never removes existing deductions.
Step 1. Resolve Billing Item Details
- Source:
billing_item_detailfor eachbilling_item_idin the tax preview map. - Action: READ from
billing_item_detail. - Logic:
- For each
billing_item_id, querybilling_item_detailto obtain thebilling_item_detail_idfor the relevantbilling_item_detail_type_cd(e.g.,PAYfor withholding deductions).
- For each
Step 2. Load Existing Deductions
- Source:
billing_item_deductionfor all resolvedbilling_item_detail_idvalues. - Action: READ from
billing_item_deduction. - Logic:
- Collect all existing deductions keyed by
billing_item_detail_id + billing_item_deduction_type_cdto detect duplicates.
- Collect all existing deductions keyed by
Step 3. Insert Missing Deductions
- Source: Calculated deduction items from the tax preview map.
- Action: For each calculated deduction, conditionally INSERT into
billing_item_deduction. - Logic:
- Skip any deduction where
billing_item_deduction_amt <= 0. - Skip any deduction where a record with the same
billing_item_detail_idandbilling_item_deduction_type_cdalready exists with a non-zero amount. - For each remaining deduction: INSERT into
billing_item_deduction:- Set
billing_item_deduction.billing_item_detail_id= resolved detail ID. - Set
billing_item_deduction.billing_item_deduction_type_cd= calculated type code (e.g.,WH_US_NRA,WH_UK_FEU,VAT_COMM). - Set
billing_item_deduction.billing_item_deduction_amt= calculated amount (string, precision 15 scale 2). - Set
billing_item_deduction.billing_item_deduction_update_net_ind=true. - Set
billing_item_deduction.comment= auto-generated explanation from the tax engine. - Set
billing_item_deduction.created_by='TAX_AUTO'. - Set
billing_item_deduction.updated_by='TAX_AUTO'.
- Set
- Skip any deduction where
NOTE
The created_by value 'TAX_AUTO' distinguishes automatically created tax deductions from manually entered deductions. This allows downstream logic to identify and potentially update tax deductions when recalculation occurs.
Side-effects:
- New
billing_item_deductionrecords are created for billing items that did not already have the applicable deduction types.
Postconditions:
- Each billing item in the map that had calculable tax deductions now has corresponding
billing_item_deductionrecords. - The list of
billing_item_idvalues that had new deductions created is returned to the caller.
2.6 Worksheet Tax Preview (Batch)
Operation: worksheetTaxPreview
Trigger: User navigates to a worksheet detail page with receivable applications.
Input Parameters:
billingItemIds: Array of integers —billing_item.billing_item_idvalues on the worksheetworksheetId: Integer —cash_receipt_worksheet.cash_receipt_worksheet_id
Calculates tax previews for all billing items on a worksheet in a single batch operation. This is the entry point that populates tax badges and deduction suggestions on the receivables table.
IMPORTANT
Tax calculation is driven by the billing item itself — specifically billing_item.client_id (or contracted_party_id) and billing_item.uta_entity_id. Tax is NOT derived from split references (cash_receipt_reference). Split references are informational filters for finding receivables; the actual receivable added to the worksheet determines the tax treatment.
Step 1. Resolve Billing Item Context
- Source:
billing_item,billing_item_detail,uta_entity,cash_receipt_split(via the worksheet). - Action: READ context for each billing item ID.
- Logic:
- For each
billing_item_id: querybilling_itemforuta_entity_id,client_id,contracted_party_id,service_country_cd. - Query
billing_item_detailwherebilling_item_detail_type_cd = 'REV'for gross amount and commission rate. - Query
uta_entityfor entity name to determinebilling_entity_cd(see field mapping 4.4). - Query
cash_receipt_splitvia the worksheet to obtain split amount for VAT validation.
- For each
Step 2. Resolve Party Tax Profiles
- Source:
party,party_tax_infofor each distinct party referenced by the billing items. - Action: READ from
partyandparty_tax_info. - Logic:
- For each distinct
contracted_party_idorclient_id: querypartyfor display name andparty_tax_infowherecurrent_item_ind = true.
- For each distinct
Step 3. Compute UK Cumulative Earnings
- Source:
tax_rate_config(for UK tax year boundaries) andbilling_itemjoined withbilling_item_detail. - Action: READ and aggregate UK YTD billing totals for UK-entity billing items.
- Logic:
- Query
tax_rate_configwherejurisdiction_cd = 'GB'andtax_type_cd = 'TAX_YEAR'with effective date containing the current date to determine the UK tax year date range. Fall back to calendar year if no record found. - For each party with a UK billing entity: query all
billing_itemrecords joined tobilling_item_detail(REV type) wherecontracted_party_id = :partyId(orclient_id) anduta_entity_idis a UK entity andbilling_item_due_dtfalls within the UK tax year range. uk_expected_ytd_total= sum of gross amounts from all matching billing items.uk_ytd_earnings=MAX(0, uk_expected_ytd_total - current_gross_amt)(prior earnings, excluding the current billing item).
- Query
Step 4. Execute Per-Billing-Item Calculation
- Source: Resolved context from Steps 1–3.
- Action: For each billing item, invoke procedure 2.3 (Calculate Withholding Tax for a Billing Item) with the resolved context.
- Logic:
- Pass
partyId,billingEntityCd,grossAmt,serviceLocationCd,ukYtdEarnings,ukExpectedYtdTotal,worksheetAmt, anduserIdto procedure 2.3. - Collect the
TaxDeductionResultfor each billing item.
- Pass
Side-effects:
- One
tax_calculation_auditrecord withcontext_type_cd = 'DEDUCTION_APPLIED'is created per billing item (within procedure 2.3).
Postconditions:
- Returns a map of
billing_item_idtoTaxDeductionResult, used to populate tax badges, deduction dialogs, and to drive procedure 2.5.
2.7 Generate Tax Form Record
Operation: generateTaxFormRecord
Trigger: User requests form generation from the Tax Forms management screen.
Input Parameters:
partyId: Integer (optional) —tax_form.party_id(null for payer-level forms such asIRS_1042)formCodeCd: String —tax_form.form_code_cdjurisdictionCd: String —tax_form.jurisdiction_cdtaxYear: Integer —tax_form.tax_yearquarterCd: String (optional) —tax_form.quarter_cd(required for HMRC quarterly forms)formDataJson: JSON (optional) — pre-built form data; if absent, aggregated from actualssourceMappingJson: JSON (optional) — pre-built source lineage; if absent, built during aggregationsourceTypeCd: String —tax_form.source_type_cd(WORKSHEETfor auto-built,MANUALfor user-provided)sourceId: Integer (optional) — FK to the triggering source entitytaxFilingPackageId: Integer (optional) —tax_form.tax_filing_package_idmailingCd: String (optional) —tax_form.mailing_cdchapterCd: String (optional) —tax_form.chapter_cdsequenceNo: Integer (optional) —tax_form.sequence_nonotes: String (optional) —tax_form.notesuserId: Integer —tax_form.created_by_user_id
Creates a tax_form record populated with data aggregated from actual cash applications and deductions.
Step 1. Aggregate Form Data from Actuals
- Source:
cash_receipt_application,cash_receipt_application_deduction,cash_receipt_worksheet,billing_item,billing_item_detail,party_tax_info. - Action: READ and aggregate transaction data via procedure 2.8.
- Logic:
- If
formDataJsonis not explicitly provided: route to procedure 2.8 byformCodeCdto build aggregated form data and source lineage map. - Determine tax period date range from procedure 2.8.1 using
taxYear,quarterCd, andjurisdictionCd.
- If
Step 2. Insert Tax Form Record
- Source: Aggregated data from Step 1 and input parameters.
- Action: INSERT into
tax_form. - Logic:
- Set
tax_form.party_id=:partyId(ornullfor payer-level forms). - Set
tax_form.party_name= party display name (looked up frompartyifpartyIdprovided). - Set
tax_form.form_code_cd=:formCodeCd. - Set
tax_form.jurisdiction_cd=:jurisdictionCd. - Set
tax_form.tax_year=:taxYear. - Set
tax_form.quarter_cd=:quarterCd(ornullfor annual forms). - Set
tax_form.status_cd='GENERATED'. - Set
tax_form.generated_dt= current timestamp. - Set
tax_form.form_data_json= aggregated form data. - Set
tax_form.source_map_json= source lineage map. - Set
tax_form.source_type_cd=:sourceTypeCd. - Set
tax_form.source_id=:sourceId(ornull). - Set
tax_form.tax_filing_package_id=:taxFilingPackageId(ornull). - Set
tax_form.mailing_cd=:mailingCd(ornull). - Set
tax_form.chapter_cd=:chapterCd(ornull). - Set
tax_form.sequence_no=:sequenceNo(ornull). - Set
tax_form.notes=:notes(ornull). - Set
tax_form.created_by_user_id=:userId. - Set
tax_form.created_dt= current timestamp.
- Set
Side-effects:
- None beyond the new
tax_formrecord.
Postconditions:
- A
tax_formrecord exists withstatus_cd = 'GENERATED',form_data_jsonpopulated, andsource_map_jsonpopulated. - The generated
tax_form_idis returned to the caller.
2.8 Tax Form Data Aggregation Pipeline
Operation: aggregateTaxFormData
Trigger: Called internally by procedure 2.7 when formDataJson is not explicitly provided by the caller.
Input Parameters:
formCodeCd: String — determines which form-specific aggregation logic to applypartyId: Integer (optional) — the party to aggregate for (not required for payer-level forms)taxYear: Integer — the tax yearquarterCd: String (optional) — the HMRC quarter (for quarterly forms)jurisdictionCd: String —USorGB
Aggregates actual transaction data from worksheets, applications, and deductions to populate tax form fields. This is the bridge between the operational cash processing domain and the regulatory filing domain.
**PoC Artifact:** In the PoC, this aggregation is implemented as standalone functions rather than through a formal repository interface. The production system should formalize these into proper service methods with consistent error handling and transaction boundaries.
Step 1. Determine Tax Period Date Range
- Source:
taxYear,quarterCd,jurisdictionCdfrom input. - Action: Compute period start and end dates.
- Logic:
- US annual:
periodStart = January 1 of taxYear,periodEnd = December 31 of taxYear. - GB annual:
periodStart = April 6 of taxYear,periodEnd = April 5 of taxYear + 1. - GB Q1:
periodStart = April 6 of taxYear,periodEnd = June 30 of taxYear. - GB Q2:
periodStart = July 1 of taxYear,periodEnd = September 30 of taxYear. - GB Q3:
periodStart = October 1 of taxYear,periodEnd = December 31 of taxYear. - GB Q4:
periodStart = January 1 of taxYear + 1,periodEnd = April 5 of taxYear + 1.
- US annual:
Step 2. Aggregate PAY Applications and Withholding Deductions
- Source:
cash_receipt_application,cash_receipt_worksheet,billing_item_detail,billing_item,cash_receipt_application_deduction. - Action: READ and sum application and deduction amounts for the party and period.
- Logic:
- Query
cash_receipt_applicationjoined tocash_receipt_worksheet,billing_item_detail, andbilling_item:- Filter:
billing_item.client_id = :partyIdANDbilling_item_detail.billing_item_detail_type_cd = 'PAY'ANDcash_receipt_worksheet.cash_receipt_worksheet_status_cd IN ('P','T','A')ANDcash_receipt_application.reversal_of_application_id IS NULLANDcash_receipt_worksheet.applied_dtwithin period range.
- Filter:
total_gross= SUM ofcash_receipt_application.cash_receipt_amt_applied.- For the collected application IDs, query
cash_receipt_application_deductionfiltered by applicable deduction type codes (e.g.,WH_US_NRAfor IRS forms,WH_UK_FEUfor HMRC forms). total_withholding= SUM ofcash_receipt_application_deduction.deduction_amt_applied.
- Query
Step 3. Assemble Form-Specific Data
- Source: Aggregated totals and source records from Step 2, plus
party_tax_info. - Action: Build the
form_data_jsonandsource_map_jsonstructures for the specific form type. - Logic:
- IRS 1042-S (per-party annual):
gross_income=total_gross;federal_tax_withheld= totalWH_US_NRAdeductions;tax_rate= 0.30 (or 0 ifhas_tax_treaty_ind = true);income_code=17;exemption_code=04(treaty) or00(none);recipientfields fromparty_tax_info(tax_residence_cd,tin_type_cd). - IRS 1042 (annual summary): auto-generate any missing 1042-S forms first;
total_gross_amounts= sum of all 1042-Sgross_incomevalues;total_withholding_tax= sum of allfederal_tax_withheldvalues;number_of_1042s_forms= count of 1042-S forms;tax_liability_by_period= semi-monthly breakdown from Step 4. - IRS 1042-T (transmittal):
number_of_forms_1042s= count of 1042-S forms for the year;total_gross_incomeandtotal_tax_withheldsummed from all 1042-S forms. - IRS 1099-NEC (per-party annual):
nonemployee_compensation=total_gross;federal_tax_withheld= totalWH_US_NRAdeductions (typically 0 for US residents). - HMRC FEU1 (payer-level quarterly): aggregate across all parties with
WH_UK_FEUdeductions in the UK quarter;total_gross_paymentandtotal_withholdingsummed across all parties. - HMRC FEU2 (per-party per-payment):
total_gross_payment=total_gross;total_withholding= totalWH_UK_FEUdeductions;net_payment=total_gross_payment - total_withholding;performances= distinct deals from application records. - Build
source_map_json: for each form field, recordaggregationmethod (SUM, LOOKUP, COUNT),description, andsource_records[]array (each entry:entity_type,entity_id,amount,label,worksheet_id,deal_name).
- IRS 1042-S (per-party annual):
Step 4. Compute Semi-Monthly Tax Liability (IRS 1042 Only)
- Source:
cash_receipt_application_deduction,cash_receipt_application,cash_receipt_worksheet. - Action: READ and bucket deduction amounts by IRS semi-monthly deposit periods.
- Logic:
- For all
WH_US_NRAdeductions on active worksheets within the US tax year, bucket each deduction bycash_receipt_worksheet.applied_dtday-of-month:- Period 1: days 1–7.
- Period 2: days 8–15.
- Period 3: days 16–22.
- Period 4: days 23–end of month.
- Initialize all 12 months. Sum deduction amounts into the appropriate period bucket per month.
- For all
Side-effects:
- None. This procedure is read-only.
Postconditions:
- Returns
form_data_jsonandsource_map_jsonstructures suitable for inserting intotax_form.
2.9 Update Tax Form Status
Operation: updateTaxFormStatus
Trigger: User marks a tax form as filed or records acknowledgement from the tax authority.
Input Parameters:
taxFormId: Integer —tax_form.tax_form_idnewStatusCd: String — target status:GENERATED,FILED, orSUBMITTEDuserId: Integer —tax_form.updated_by_user_id
Transitions a tax form through its status lifecycle: DRAFT → GENERATED → FILED → SUBMITTED.
Step 1. Apply Status Transition
- Source:
tax_formrecord identified by:taxFormId. - Action: UPDATE
tax_formwheretax_form_id = :taxFormId. - Logic:
- Set
tax_form.status_cd=:newStatusCd. - If
:newStatusCdis'FILED'or'SUBMITTED': settax_form.filed_dt= current timestamp. - Set
tax_form.updated_by_user_id=:userId. - Set
tax_form.updated_dt= current timestamp.
- Set
Side-effects:
- None beyond the status change on the targeted form record.
Postconditions:
- The
tax_formrecord hasstatus_cd=:newStatusCd. - When status is
FILEDorSUBMITTED,filed_dtis set. SUBMITTEDis a terminal state — no further transitions are permitted.
2.10 Regenerate Tax Form Data
Operation: regenerateTaxFormData
Trigger: User requests regeneration after additional worksheets have been applied for the tax period.
Input Parameters:
taxFormId: Integer —tax_form.tax_form_iduserId: Integer —tax_form.updated_by_user_id
Re-queries actual transaction data and replaces the form data on an existing tax_form record. Only permitted for forms in DRAFT or GENERATED status.
Step 1. Validate Status
- Source:
tax_formwheretax_form_id = :taxFormId. - Action: READ
tax_form.status_cd. - Logic:
- If
status_cdis not'DRAFT'or'GENERATED': reject the operation. Forms inFILEDorSUBMITTEDstatus cannot be regenerated.
- If
Step 2. Re-Execute Data Aggregation
- Source: Form metadata fields:
tax_form.form_code_cd,tax_form.party_id,tax_form.tax_year,tax_form.quarter_cd,tax_form.jurisdiction_cd. - Action: Invoke procedure 2.8 with the form's existing metadata.
- Logic:
- Re-run the full aggregation pipeline to produce updated
form_data_jsonandsource_map_json.
- Re-run the full aggregation pipeline to produce updated
Step 3. Update Tax Form Record
- Source: Aggregated data from Step 2.
- Action: UPDATE
tax_formwheretax_form_id = :taxFormId. - Logic:
- Set
tax_form.form_data_json= newly aggregated form data. - Set
tax_form.source_map_json= newly computed source lineage map. - Set
tax_form.generated_dt= current timestamp. - Set
tax_form.updated_by_user_id=:userId. - Set
tax_form.updated_dt= current timestamp.
- Set
WARNING
Regeneration replaces all existing form data and source maps. If the form has been manually adjusted after initial generation, those adjustments will be lost. Forms in FILED or SUBMITTED status cannot be regenerated.
Side-effects:
- None beyond the field updates on the targeted
tax_formrecord.
Postconditions:
tax_form.form_data_jsonandtax_form.source_map_jsonreflect the current state of actual transaction records for the tax period.tax_form.generated_dtis updated to the current timestamp.
2.11 Generate Annual Filing Package (IRS 1042)
Operation: generateAnnualFilingPackage
Trigger: User initiates annual filing package generation for a tax year from the Tax Forms management screen.
Input Parameters:
taxYear: Integer — the US calendar year for the IRS annual filinguserId: Integer —tax_filing_package.created_by_user_id
Assembles a complete IRS 1042 annual filing package containing all required forms organized into two mailings: Mailing 1 (1042-T transmittal + all 1042-S Copy A forms) and Mailing 2 (Form 1042 annual return).
Step 1. Create Filing Package Record
- Source: Input parameters.
- Action: INSERT into
tax_filing_package. - Logic:
- Set
tax_filing_package.tax_year=:taxYear. - Set
tax_filing_package.jurisdiction_cd='US'. - Set
tax_filing_package.filing_type_cd='IRS_1042_ANNUAL'. - Set
tax_filing_package.status_cd='DRAFT'. - Set
tax_filing_package.created_by_user_id=:userId. - Set
tax_filing_package.created_dt= current timestamp.
- Set
Step 2. Discover Parties with Withholding
- Source:
cash_receipt_application_deduction,cash_receipt_application,cash_receipt_worksheet,billing_item_detail,billing_item,party. - Action: READ distinct party list.
- Logic:
- Query all distinct parties where
billing_item_deduction_type_cd = 'WH_US_NRA', worksheetcash_receipt_worksheet_status_cd IN ('P','T','A'),reversal_of_application_id IS NULL, andcash_receipt_worksheet.applied_dtfalls within January 1 through December 31 of:taxYear.
- Query all distinct parties where
Step 3. Create or Link 1042-S Forms for Each Party
- Source: Existing
tax_formrecords and party list from Step 2. - Action: For each party, either UPDATE an existing
tax_formor INSERT a new one via procedure 2.7. - Logic:
- For each party: check for an existing
tax_formwithform_code_cd = 'IRS_1042_S'andtax_year = :taxYearandstatus_cd IN ('GENERATED','FILED','SUBMITTED'). - If an existing form is found: UPDATE it to set
tax_filing_package_id= new package ID,mailing_cd = 'MAILING_1',chapter_cd = 'CH3_ORIG',sequence_no= sequential number starting at 2. - If no existing form: INSERT a new
tax_formrecord via procedure 2.7 withform_code_cd = 'IRS_1042_S', linked to the package,mailing_cd = 'MAILING_1',chapter_cd = 'CH3_ORIG',sequence_no= next sequential number.
- For each party: check for an existing
Step 4. Create 1042-T Transmittal Form
- Source: All 1042-S forms created/linked in Step 3.
- Action: INSERT into
tax_formvia procedure 2.7. - Logic:
- Set
form_code_cd = 'IRS_1042_T',mailing_cd = 'MAILING_1',sequence_no = 1(must be the cover sheet, always sequence 1). - Form data:
number_of_forms_1042s= count of 1042-S forms;total_gross_incomeandtotal_tax_withheldsummed from all 1042-Sform_data_jsonvalues.
- Set
Step 5. Create 1042 Annual Return Form
- Source: All 1042-S forms, semi-monthly liability data from procedure 2.8.4.
- Action: INSERT into
tax_formvia procedure 2.7. - Logic:
- Set
form_code_cd = 'IRS_1042',mailing_cd = 'MAILING_2',sequence_no = 1. - Form data:
total_gross_amounts= sum of all 1042-Sgross_incomevalues;total_withholding_tax= sum of all 1042-Sfederal_tax_withheldvalues;number_of_1042s_forms= count of 1042-S forms;tax_liability_by_period= semi-monthly breakdown.
- Set
Step 6. Build and Store Reconciliation Snapshot
- Source: All forms created or linked in Steps 3–5.
- Action: Compute reconciliation and UPDATE
tax_filing_package. - Logic:
- Compute reconciliation fields:
total_gross_from_1042s= SUM ofgross_incomefrom all 1042-Sform_data_json.total_withheld_from_1042s= SUM offederal_tax_withheldfrom all 1042-Sform_data_json.form_1042t_gross= 1042-Ttotal_gross_income.form_1042t_withheld= 1042-Ttotal_tax_withheld.form_1042_gross= 1042total_gross_amounts.form_1042_withheld= 1042total_withholding_tax.gross_match=|total_gross_from_1042s - form_1042t_gross| < 0.005.withheld_match=|total_withheld_from_1042s - form_1042t_withheld| < 0.005.all_reconciled=gross_match AND withheld_match AND form_1042_match.
- UPDATE
tax_filing_package:- Set
status_cd='GENERATED'. - Set
generated_dt= current timestamp. - Set
reconciliation_json= computed reconciliation snapshot. - Set
updated_by_user_id=:userId. - Set
updated_dt= current timestamp.
- Set
- Compute reconciliation fields:
Side-effects:
- Multiple
tax_formrecords created or updated and linked to the new package viatax_filing_package_id. - Existing 1042-S forms for the year are linked into the package without re-aggregating their data.
Postconditions:
- A
tax_filing_packagerecord exists withstatus_cd = 'GENERATED'andreconciliation_jsonpopulated. - The package contains: 1042-T at
MAILING_1sequence 1, all 1042-S forms atMAILING_1sequence 2+, and Form 1042 atMAILING_2sequence 1. - The generated
tax_filing_package_idis returned to the caller.
2.12 Delete Tax Form Record
Operation: deleteTaxFormRecord
Trigger: User deletes a tax form record from the Tax Forms management screen.
Input Parameters:
taxFormId: Integer —tax_form.tax_form_id
Removes a tax_form record permanently.
Step 1. Delete Tax Form
- Source:
tax_formrecord identified by:taxFormId. - Action: DELETE from
tax_formwheretax_form_id = :taxFormId. - Logic:
- Hard delete of the record.
WARNING
Deletion is permanent. In production, restrict deletion to forms in DRAFT status only. Forms that have been filed or submitted must not be deletable.
Side-effects:
- None (the form is removed from any filing package references, but the package record itself is not deleted).
Postconditions:
- The
tax_formrecord no longer exists.
2.13 Collect Rate Snapshot for Audit
Operation: collectRateSnapshot
Trigger: Called internally by procedure 2.3 (Step 6) when persisting tax_calculation_audit.rate_config_snapshot_json.
Input Parameters:
jurisdictionCd: String —USorGBasOfDate: Date — the date for which active rates are to be captured (typically the current date)
Creates a point-in-time snapshot of all active tax rate configurations for a jurisdiction.
Step 1. Query Active Rate for Each Tax Type
- Source:
tax_rate_config. - Action: For each known tax type code, READ the active rate record.
- Logic:
- For each tax type in (
WITHHOLDING_NRA,WITHHOLDING_FEU,VAT_ARTIST_FEE,VAT_COMMISSION,PERSONAL_ALLOWANCE,IRS_DEPOSIT_THRESHOLD,TAX_YEAR):- Query
tax_rate_configwherejurisdiction_cd = :jurisdictionCdandtax_type_cd = :taxTypeandeffective_from_dt <= :asOfDateand (effective_to_dt IS NULL OR effective_to_dt >= :asOfDate), ordered byeffective_from_dt DESC, limit 1. - Collect the result (
rate,threshold_amt,currency_cd,effective_from_dt) if found.
- Query
- For each tax type in (
Side-effects:
- None. This procedure is read-only.
Postconditions:
- Returns a map of
tax_type_cdto active rate result, suitable for storing astax_calculation_audit.rate_config_snapshot_json.
3. Business Rules & Logic
3.1 US NRA Withholding Rules
Business rule: When UTA pays a non-resident alien for US-source services, it must withhold 30% (or a CWA/treaty-reduced amount) and remit to the IRS, filing a 1042-S for each recipient.
Data-level enforcement:
- Read:
party_tax_info.tax_residence_cdcompared to'US'to determine residency. - Read:
party_tax_info.has_cwa_approval_indandparty_tax_info.cwa_expiration_dtto evaluate CWA validity. - Guard: If
tax_residence_cd = 'US': no withholding applied; 1099-NEC reporting triggered instead. - Guard: If service is performed outside the US (
service_location_cdis non-US): no withholding applied (foreign-source income). - Guard: CWA overrides treaty; treaty overrides default 30%. Priority is enforced in calculation order.
- Write: Withholding deduction created with
billing_item_deduction_type_cd = 'WH_US_NRA'.
| Rule | Specification |
|---|---|
| Base rate | 30% on gross income for non-resident aliens with US-source income |
| Source rule | Only US-performed services are US-source income; services performed outside the US are foreign-source (no US withholding) |
| CWA override | has_cwa_approval_ind = true and not expired: withholding = cwa_withholding_amt (flat dollar, not percentage) |
| CWA expiry | If cwa_expiration_dt < current date: CWA inactive, standard 30% applies |
| Treaty override | has_tax_treaty_ind = true: 0% withholding; Form 8233 must be on file |
| Priority | CWA checked before treaty; both checked before default 30% |
| Rounding | Withholding amounts rounded to nearest whole number |
| 1042-S requirement | Required for ANY payment to a non-resident alien, even if withholding = $0 |
| IRS 3-day rule | When accumulated undeposited withholding reaches $2,000, deposit required within 3 business days via EFTPS |
| Tax year | US calendar year: January 1 through December 31 |
| Filing deadline | March 15 following the calendar year (1042-S, 1042, 1042-T) |
3.2 UK FEU Withholding Rules
Business rule: When UTA pays a non-UK-resident entertainer for UK-performed services, it must withhold 20% of earnings above the personal allowance (currently £12,570 per UK tax year) and remit to HMRC via FEU1/FEU2 filings.
Data-level enforcement:
- Read:
party_tax_info.tax_residence_cdcompared to'GB'to determine residency. - Read:
deal_tax_context.uk_expected_ytd_totalto setaware_of_exceeding_threshold. - Guard: If
tax_residence_cd = 'GB': no FEU withholding; proceed to VAT checks only. - Guard: If service performed outside UK (
service_location_cdis non-GB): no FEU withholding. - Guard: If
uk_ytd_earnings + current_gross < 12,570ANDaware_of_exceeding_threshold = false: no withholding. - Write: Withholding deduction created with
billing_item_deduction_type_cd = 'WH_UK_FEU'.
| Rule | Specification |
|---|---|
| Base rate | 20% on gross earnings for foreign entertainers/sportspersons |
| Personal allowance | No withholding when cumulative UK earnings for the tax year are below £12,570 |
| Proactive withholding | When uk_expected_ytd_total >= 12,570, withholding applies even if actuals have not yet crossed the threshold |
| Residency | tax_residence_cd = 'GB' means UK resident — no FEU withholding |
| Service location | Only UK-performed services trigger FEU withholding |
| Non-retroactive | FEU withholding is not retroactive — only current and future payments |
| Currency conversion | Non-GBP fees divided by FX rate to get GBP equivalent; withholding can be converted back for display |
| Rounding | Withholding amounts rounded to nearest whole number |
| Tax year | UK tax year: April 6 through April 5 |
| FEU1 filing | Quarterly: Q1 Apr 6–Jun 30 (due Jul 14), Q2 Jul 1–Sep 30 (due Oct 14), Q3 Oct 1–Dec 31 (due Jan 14), Q4 Jan 1–Apr 5 (due Apr 19) |
| FEU2 filing | Within 14 days of each payment |
3.3 UK VAT Rules
Business rule: UK Agency transactions involving UK-resident, VAT-registered talent trigger two distinct VAT obligations — a pass-through artist fee VAT charged to the buyer, and a commission VAT deducted from the client's payout.
Data-level enforcement:
- Read:
party_tax_info.vat_registered_indanddeal_tax_context.billing_entity_cd. - Guard: VAT rules only apply when
billing_entity_cd = 'UK_AGENCY'. - Write: Artist fee VAT is NOT written as a deduction — it is returned as informational
vat_pass_throughdata only. Commission VAT IS written withbilling_item_deduction_type_cd = 'VAT_COMM'.
| Rule | Specification |
|---|---|
| Artist fee VAT | 20% on gross. Applies: UK Agency + UK Show + UK resident + VAT registered. Pass-through — charged to buyer on top of gross invoice. Not a billing item deduction. |
| Commission VAT | 20% on commission amount. Applies: UK Agency + UK resident. UTA's VAT obligation. Deducted from PAY detail, reducing client payout. |
| Commission rate | Defaults to 10% if not specified on the billing item detail |
| VAT rounding | FLOOR(basis * rate) — truncated to nearest whole number |
3.4 Effective Dating for Tax Data
Business rule: Tax rates and party tax profiles are version-controlled through effective dating. Only one version is active at any time per party (or per jurisdiction/type for rates), and historical versions are preserved immutably.
Data-level enforcement:
- Read:
party_tax_infowherecurrent_item_ind = truefor active profile resolution. - Read:
tax_rate_configordered byeffective_from_dt DESCwhere date range contains target date for active rate resolution. - Guard: Once
effective_to_dtis set on aparty_tax_infoortax_rate_configrecord, that record must not be modified. Changes are captured by creating a new effective-dated version. - Guard: Only one
party_tax_inforecord per party may havecurrent_item_ind = true. Enforced transactionally in procedure 2.1.1. - Write: Fallback to hardcoded constants when no
tax_rate_configrecord is found: US NRA0.30, UK FEU0.20, UK VAT0.20, UK personal allowance12570.
3.5 Tax Year Configuration
Business rule: Tax year date boundaries differ between jurisdictions and can change annually; they are stored in tax_rate_config rather than hardcoded.
Data-level enforcement:
- Read:
tax_rate_configwherejurisdiction_cd = :jurisdictionandtax_type_cd = 'TAX_YEAR'with the target date in range. - Guard: If no
TAX_YEARconfig is found, fall back to the calendar year (January 1 through December 31). - Write: No mutations. Tax year config is seeded by administrators.
| Jurisdiction | effective_from_dt | effective_to_dt | Example |
|---|---|---|---|
US | January 1 of year | December 31 of year | 2025-01-01 to 2025-12-31 |
GB | April 6 of year | April 5 of year + 1 | 2025-04-06 to 2026-04-05 |
3.6 Audit Record Immutability
Business rule: Every tax calculation must produce a new audit record capturing the full input, output, and reference data snapshots. Audit records are never updated after creation.
Data-level enforcement:
- Guard:
tax_calculation_auditrecords have no UPDATE path — each recalculation produces a new row. - Write:
party_tax_info_snapshot_jsonandrate_config_snapshot_jsonmust always capture the complete reference data used at calculation time, not just the fields relevant to the current scenario.
4. Field Mapping & Transformation
4.1 Party Tax Profile to Withholding Context
This transformation bridges the persistent party_tax_info record with the deal-specific DealTaxContext to produce the WithholdingContext consumed by the tax calculation engine.
| Source Table.Field | Target Table.Field | Transform |
|---|---|---|
deal_tax_context.billing_entity_cd | withholding_context.billing_entity | Copied as-is |
deal_tax_context.gross_amt | withholding_context.gross_amount | Copied as-is (from billing_item_detail.billing_item_detail_gross_amt REV type) |
party_tax_info.tax_residence_cd | withholding_context.talent_residence | Cast to jurisdiction code |
deal_tax_context.service_location_cd | withholding_context.service_location | From billing_item.service_country_cd, mapped to jurisdiction (see 4.5) |
party_tax_info.has_cwa_approval_ind | withholding_context.has_cwa_approval | true only when has_cwa_approval_ind = true AND cwa_expiration_dt >= now |
party_tax_info.cwa_withholding_amt | withholding_context.cwa_withholding_amt | Copied only if CWA valid; otherwise 0 |
party_tax_info.has_tax_treaty_ind | withholding_context.has_tax_treaty | Copied as-is |
party_tax_info.treaty_country_cd | withholding_context.treaty_country | Copied as-is |
deal_tax_context.uk_ytd_earnings | withholding_context.uk_ytd_earnings | Computed upstream from cumulative billing items |
deal_tax_context.uk_deductible_expenses | withholding_context.uk_deductible_expenses | Copied as-is |
deal_tax_context.uk_expected_ytd_total | withholding_context.aware_of_exceeding_threshold | true when uk_expected_ytd_total >= 12570 |
deal_tax_context.uk_non_gbp_currency_ind | withholding_context.uk_non_gbp_currency_ind | Copied as-is |
deal_tax_context.uk_fee_currency_cd | withholding_context.uk_fee_currency_cd | Copied as-is |
deal_tax_context.uk_fx_rate | withholding_context.uk_fx_rate | Currency units per 1 GBP |
deal_tax_context.uk_fx_rate_source | withholding_context.uk_fx_rate_source | HMRC or OVERRIDE |
party_tax_info.vat_registered_ind | withholding_context.vat_registered_ind | Copied as-is |
4.2 Calculation Result to Billing Item Deduction Record
The tax calculation engine output maps to billing_item_deduction fields when deductions are auto-applied.
| Source Table.Field | Target Table.Field | Transform |
|---|---|---|
Resolved from billing_item_id + billing_item_detail_type_cd | billing_item_deduction.billing_item_detail_id | Lookup via billing_item_detail |
Calculation output deduction_type_cd | billing_item_deduction.billing_item_deduction_type_cd | WH_US_NRA, WH_UK_FEU, or VAT_COMM |
Calculation output amount | billing_item_deduction.billing_item_deduction_amt | String, precision 15 scale 2 |
| — | billing_item_deduction.billing_item_deduction_update_net_ind | Always true for tax deductions |
Calculation output explanation | billing_item_deduction.comment | Auto-generated by tax engine |
| — | billing_item_deduction.created_by | 'TAX_AUTO' for automated application |
4.3 Application Data to Tax Form Fields
PAY applications and their deductions are aggregated into tax form data.
IRS 1042-S field mapping:
| Source Table.Field | Target Table.Field | Transform |
|---|---|---|
cash_receipt_application.cash_receipt_amt_applied (PAY type) | tax_form.form_data_json.gross_income | SUM |
cash_receipt_application_deduction.deduction_amt_applied (WH_US_NRA) | tax_form.form_data_json.federal_tax_withheld | SUM |
party_tax_info.tax_residence_cd | tax_form.form_data_json.recipient.country_of_residence | LOOKUP |
party_tax_info.tin_type_cd | tax_form.form_data_json.recipient.tin_type | LOOKUP |
party_tax_info.has_tax_treaty_ind | tax_form.form_data_json.tax_rate | 0 if true; 0.30 if false |
HMRC FEU2 field mapping:
| Source Table.Field | Target Table.Field | Transform |
|---|---|---|
cash_receipt_application.cash_receipt_amt_applied (PAY, UK quarter) | tax_form.form_data_json.total_gross_payment | SUM |
cash_receipt_application_deduction.deduction_amt_applied (WH_UK_FEU) | tax_form.form_data_json.total_withholding | SUM |
Computed: total_gross_payment - total_withholding | tax_form.form_data_json.net_payment | COMPUTED |
party_tax_info.tax_residence_cd | tax_form.form_data_json.performer.country_of_residence | LOOKUP |
party_tax_info.vat_registered_ind | tax_form.form_data_json.performer.is_vat_registered | LOOKUP |
4.4 Billing Entity Determination
The billing entity code (US_AGENCY or UK_AGENCY) is derived from the UTA entity name stored in uta_entity.uta_entity_name.
| Source Table.Field | Target Table.Field | Transform |
|---|---|---|
uta_entity.uta_entity_name contains UK, GB, or LONDON (case-insensitive) | deal_tax_context.billing_entity_cd | Set to 'UK_AGENCY' |
uta_entity.uta_entity_name — anything else (or null) | deal_tax_context.billing_entity_cd | Default to 'US_AGENCY' |
4.5 Service Location Mapping
The service location from billing_item.service_country_cd is mapped to a jurisdiction code used by the withholding calculation engine.
| Source Table.Field | Target Table.Field | Transform |
|---|---|---|
billing_item.service_country_cd = 'US' | withholding_context.service_location | 'US' |
billing_item.service_country_cd = 'GB' | withholding_context.service_location | 'GB' |
billing_item.service_country_cd matches United Kingdom, LONDON, ENGLAND (case-insensitive regex) | withholding_context.service_location | 'GB' |
billing_item.service_country_cd is null or unmappable | withholding_context.service_location | null (service location unknown) |
5. Cross-References
| Document | Relationship |
|---|---|
| Tax Withholding Data Model | Table definitions, field catalogs, and entity relationships for all tables referenced by these procedures: party_tax_info, tax_rate_config, tax_calculation_audit, tax_form, tax_filing_package. |
| Tax Withholding Queries | Read operations and aggregation patterns used as inputs to these procedures. |
| Worksheets Data Model | The worksheet is where tax deductions are surfaced (preview badges, tax summary headers) and where cash_receipt_application_deduction records are applied. tax_calculation_audit.context_id references cash_receipt_worksheet_id for WORKSHEET_PREVIEW context. |
| Billing Items Data Model | Tax withholding is calculated against billing item details (REV/PAY). billing_item.client_id and billing_item.uta_entity_id drive jurisdiction determination. Tax deductions write to billing_item_deduction. |
| Cash Receipts Data Model | Split-level party references trigger early tax warnings (SPLIT_WARNING context in tax_calculation_audit). Split references are informational only — they do not drive worksheet-level tax calculations. |
| Settlements Data Model | Settlement and payout flow is downstream of tax application on the PAY receivable. |
**PoC Artifact:** The tax scenario wizard (`/tax-scenarios`) and its underlying engine in `tax-scenario/_lib/` were built as a requirements-driving tool to model and validate tax rules. The pure calculation functions (jurisdiction rules, form requirements, compliance alerts) are reused in the production worksheet flow through the procedures described in this document. The wizard UI itself is not a production feature. The production tax integration surfaces through the worksheet flow — tax badges on receivable rows, tax summary in worksheet headers, and pre-populated deduction dialogs.