Skip to content

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:


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_id
  • taxResidenceCd: String (optional) — party_tax_info.tax_residence_cd
  • entityTypeCd: String (optional) — party_tax_info.entity_type_cd
  • tinTypeCd: String (optional) — party_tax_info.tin_type_cd
  • tinNumber: String (optional) — party_tax_info.tin_number
  • vatRegisteredInd: Boolean — party_tax_info.vat_registered_ind
  • vatNumber: String (optional) — party_tax_info.vat_number
  • hasW9OnFileInd: Boolean — party_tax_info.has_w9_on_file_ind
  • hasW8OnFileInd: Boolean — party_tax_info.has_w8_on_file_ind
  • w8FormTypeCd: String (optional) — party_tax_info.w8_form_type_cd
  • w8ExpirationDt: Date (optional) — party_tax_info.w8_expiration_dt
  • hasCwaApprovalInd: Boolean — party_tax_info.has_cwa_approval_ind
  • cwaExpirationDt: Date (optional) — party_tax_info.cwa_expiration_dt
  • cwaWithholdingAmt: Decimal string (optional) — party_tax_info.cwa_withholding_amt
  • hasTaxTreatyInd: Boolean — party_tax_info.has_tax_treaty_ind
  • treatyCountryCd: String (optional) — party_tax_info.treaty_country_cd
  • effectiveFromDt: Date (optional) — party_tax_info.effective_from_dt
  • actorId: 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_info where party_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.

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 (or null).
    • Set party_tax_info.entity_type_cd = :entityTypeCd (or null).
    • Set party_tax_info.tin_type_cd = :tinTypeCd (or null).
    • Set party_tax_info.tin_number = :tinNumber (or null).
    • Set party_tax_info.vat_registered_ind = :vatRegisteredInd.
    • Set party_tax_info.vat_number = :vatNumber (or null).
    • 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 (or null).
    • Set party_tax_info.w8_expiration_dt = :w8ExpirationDt (or null).
    • Set party_tax_info.has_cwa_approval_ind = :hasCwaApprovalInd.
    • Set party_tax_info.cwa_expiration_dt = :cwaExpirationDt (or null).
    • Set party_tax_info.cwa_withholding_amt = :cwaWithholdingAmt (or null).
    • Set party_tax_info.has_tax_treaty_ind = :hasTaxTreatyInd.
    • Set party_tax_info.treaty_country_cd = :treatyCountryCd (or null).
    • 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.

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_info record for this party is end-dated and marked inactive.

Postconditions:

  • Exactly one party_tax_info record for the party has current_item_ind = true.
  • The new profile is the active profile for all subsequent tax calculations.
  • The generated party_tax_info_id is 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_info where party_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_info record 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_id
  • actorId: 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_info record identified by :partyTaxInfoId.
  • Action: UPDATE party_tax_info where party_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.

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 = false and effective_to_dt set 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 (US or GB)
  • 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.rate
  • thresholdAmt: Decimal string (optional) — tax_rate_config.threshold_amt
  • currencyCd: String (optional) — tax_rate_config.currency_cd
  • effectiveFromDt: Date (required) — tax_rate_config.effective_from_dt
  • effectiveToDt: Date (optional) — tax_rate_config.effective_to_dt
  • actorId: 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.3000 for 30%; or null for threshold-only entries).
    • Set tax_rate_config.threshold_amt = :thresholdAmt (e.g., 12570.00 for UK personal allowance; or null).
    • Set tax_rate_config.currency_cd = :currencyCd (e.g., GBP; or null when no threshold).
    • Set tax_rate_config.effective_from_dt = :effectiveFromDt.
    • Set tax_rate_config.effective_to_dt = :effectiveToDt (or null for open-ended).
    • Set tax_rate_config.created_by = :actorId.
    • Set tax_rate_config.created_dt = current timestamp.

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_config record exists for the given jurisdiction, tax type, and effective date range.
  • The generated tax_rate_config_id is 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_id
  • rate: Decimal string (optional) — new rate value
  • thresholdAmt: Decimal string (optional) — new threshold amount
  • effectiveToDt: Date (optional) — end date to set
  • actorId: 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_config record identified by :taxRateConfigId.
  • Action: UPDATE tax_rate_config where tax_rate_config_id = :taxRateConfigId.
  • Logic:
    • If :rate provided: set tax_rate_config.rate = :rate.
    • If :thresholdAmt provided: set tax_rate_config.threshold_amt = :thresholdAmt.
    • If :effectiveToDt provided: set tax_rate_config.effective_to_dt = :effectiveToDt.
    • Set tax_rate_config.updated_by = :actorId.
    • Set tax_rate_config.updated_dt = current timestamp.

Side-effects:

  • None beyond the field modifications on the targeted record.

Postconditions:

  • The tax_rate_config record 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_id or billing_item.contracted_party_id
  • billingEntityCd: String — derived from uta_entity.uta_entity_name (e.g., US_AGENCY, UK_AGENCY)
  • grossAmt: Decimal — from billing_item_detail.billing_item_detail_gross_amt (REV type)
  • serviceLocationCd: String (optional) — from billing_item.service_country_cd
  • commissionRate: Decimal (optional) — commission rate for VAT calculation; defaults to 0.10
  • ukYtdEarnings: Decimal — cumulative prior UK earnings for the current UK tax year
  • ukExpectedYtdTotal: Decimal — total billing item amounts for this party in the current UK tax year
  • ukNonGbpCurrencyInd: Boolean (optional) — whether the fee is in a non-GBP currency
  • ukFeeCurrencyCd: String (optional) — currency code of the non-GBP fee
  • ukFxRate: Decimal (optional) — FX rate (units of fee currency per 1 GBP)
  • worksheetAmt: Decimal (optional) — split amount, used for VAT pass-through split validation
  • userId: 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_info where party_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 = true and cwa_expiration_dt < current date, treat CWA as inactive for this calculation.
    • Evaluate W-8 expiry: if has_w8_on_file_ind = true and w8_expiration_dt < current date, note for compliance alert.
    • Map party profile and deal context to withholding context:
      • billing_entity = :billingEntityCd
      • gross_amount = :grossAmt
      • talent_residence = party_tax_info.tax_residence_cd
      • service_location = :serviceLocationCd
      • has_cwa_approval = party_tax_info.has_cwa_approval_ind AND NOT cwa_expired
      • cwa_withholding_amt = party_tax_info.cwa_withholding_amt (only if CWA valid; otherwise 0)
      • has_tax_treaty = party_tax_info.has_tax_treaty_ind
      • treaty_country = party_tax_info.treaty_country_cd
      • uk_ytd_earnings = :ukYtdEarnings
      • aware_of_exceeding_threshold = true when :ukExpectedYtdTotal >= 12570
      • vat_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_cd differs from the billing entity's home jurisdiction (US for US_AGENCY, GB for UK_AGENCY). When tax_residence_cd is 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).

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."
    • Else check treaty: if has_tax_treaty = true: withholding = 0. Note treaty country.
    • Else default: query tax_rate_config where jurisdiction_cd = 'US' and tax_type_cd = 'WITHHOLDING_NRA' and effective_from_dt <= current_date and (effective_to_dt IS NULL OR effective_to_dt >= current_date), ordered by effective_from_dt DESC, limit 1. Fall back to 0.30 if 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'.

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). Otherwise current_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_config where jurisdiction_cd = 'GB' and tax_type_cd = 'WITHHOLDING_FEU', same date logic. Fall back to 0.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'.

Step 3. Calculate VAT (UK Agency Only)

  • Source: Withholding context and tax_rate_config. Applies only when billingEntityCd = '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. Query tax_rate_config for GB / 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 informational vat_pass_through data.
    • Commission VAT (deduction): Applies when UK Agency + UK resident. commission_amt = ROUND(gross_amt * commission_rate, 2). Query tax_rate_config for GB / 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 with billing_item_detail_type_cd = 'PAY', billing_item_deduction_type_cd = 'VAT_COMM'.
    • Split validation: When artist fee VAT applies and :worksheetAmt is provided: expected_amt = gross_amt + artist_vat_amt. If worksheet_amt < expected_amt - 0.005, generate a split validation warning.

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.

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 but cwa_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 (or cash_receipt_worksheet_id if 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 the party_tax_info record used at calculation time.
    • Set tax_calculation_audit.rate_config_snapshot_json = map of all active tax_rate_config rates 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.

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_audit record with context_type_cd = 'DEDUCTION_APPLIED'.

Postconditions:

  • A TaxDeductionResult is returned to the caller, containing: the list of deduction items, warnings, forms required, compliance alerts, the generated tax_calculation_audit_id, a calculation summary (badge label, withholding and VAT totals), and a calculationBreakdown (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_id
  • billingEntityCd: String — US_AGENCY or UK_AGENCY
  • splitAmt: 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_info where party_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_info record 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_config for estimated rate. Add IRS_1042_S to 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_config for estimated rate. Add FEU1, FEU2 to 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."

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 = current party_tax_info record snapshot.

Side-effects:

  • Creates one tax_calculation_audit record with context_type_cd = 'SPLIT_WARNING'.

Postconditions:

  • A SplitTaxWarning object is returned containing: partyId, partyName, jurisdictionCd, warnings, estimatedWithholdingRate, formsRequired, calculatedDt. This result is suitable for storing in cash_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 of billing_item_id to TaxDeductionResult — output of procedure 2.6
  • actorId: 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_detail for each billing_item_id in the tax preview map.
  • Action: READ from billing_item_detail.
  • Logic:
    • For each billing_item_id, query billing_item_detail to obtain the billing_item_detail_id for the relevant billing_item_detail_type_cd (e.g., PAY for withholding deductions).

Step 2. Load Existing Deductions

  • Source: billing_item_deduction for all resolved billing_item_detail_id values.
  • Action: READ from billing_item_deduction.
  • Logic:
    • Collect all existing deductions keyed by billing_item_detail_id + billing_item_deduction_type_cd to detect duplicates.

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_id and billing_item_deduction_type_cd already 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'.

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_deduction records 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_deduction records.
  • The list of billing_item_id values 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_id values on the worksheet
  • worksheetId: 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: query billing_item for uta_entity_id, client_id, contracted_party_id, service_country_cd.
    • Query billing_item_detail where billing_item_detail_type_cd = 'REV' for gross amount and commission rate.
    • Query uta_entity for entity name to determine billing_entity_cd (see field mapping 4.4).
    • Query cash_receipt_split via the worksheet to obtain split amount for VAT validation.

Step 2. Resolve Party Tax Profiles

  • Source: party, party_tax_info for each distinct party referenced by the billing items.
  • Action: READ from party and party_tax_info.
  • Logic:
    • For each distinct contracted_party_id or client_id: query party for display name and party_tax_info where current_item_ind = true.

Step 3. Compute UK Cumulative Earnings

  • Source: tax_rate_config (for UK tax year boundaries) and billing_item joined with billing_item_detail.
  • Action: READ and aggregate UK YTD billing totals for UK-entity billing items.
  • Logic:
    • Query tax_rate_config where jurisdiction_cd = 'GB' and tax_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_item records joined to billing_item_detail (REV type) where contracted_party_id = :partyId (or client_id) and uta_entity_id is a UK entity and billing_item_due_dt falls 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).

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, and userId to procedure 2.3.
    • Collect the TaxDeductionResult for each billing item.

Side-effects:

  • One tax_calculation_audit record with context_type_cd = 'DEDUCTION_APPLIED' is created per billing item (within procedure 2.3).

Postconditions:

  • Returns a map of billing_item_id to TaxDeductionResult, 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 as IRS_1042)
  • formCodeCd: String — tax_form.form_code_cd
  • jurisdictionCd: String — tax_form.jurisdiction_cd
  • taxYear: Integer — tax_form.tax_year
  • quarterCd: String (optional) — tax_form.quarter_cd (required for HMRC quarterly forms)
  • formDataJson: JSON (optional) — pre-built form data; if absent, aggregated from actuals
  • sourceMappingJson: JSON (optional) — pre-built source lineage; if absent, built during aggregation
  • sourceTypeCd: String — tax_form.source_type_cd (WORKSHEET for auto-built, MANUAL for user-provided)
  • sourceId: Integer (optional) — FK to the triggering source entity
  • taxFilingPackageId: Integer (optional) — tax_form.tax_filing_package_id
  • mailingCd: String (optional) — tax_form.mailing_cd
  • chapterCd: String (optional) — tax_form.chapter_cd
  • sequenceNo: Integer (optional) — tax_form.sequence_no
  • notes: String (optional) — tax_form.notes
  • userId: 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 formDataJson is not explicitly provided: route to procedure 2.8 by formCodeCd to build aggregated form data and source lineage map.
    • Determine tax period date range from procedure 2.8.1 using taxYear, quarterCd, and jurisdictionCd.

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 (or null for payer-level forms).
    • Set tax_form.party_name = party display name (looked up from party if partyId provided).
    • 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 (or null for 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 (or null).
    • Set tax_form.tax_filing_package_id = :taxFilingPackageId (or null).
    • Set tax_form.mailing_cd = :mailingCd (or null).
    • Set tax_form.chapter_cd = :chapterCd (or null).
    • Set tax_form.sequence_no = :sequenceNo (or null).
    • Set tax_form.notes = :notes (or null).
    • Set tax_form.created_by_user_id = :userId.
    • Set tax_form.created_dt = current timestamp.

Side-effects:

  • None beyond the new tax_form record.

Postconditions:

  • A tax_form record exists with status_cd = 'GENERATED', form_data_json populated, and source_map_json populated.
  • The generated tax_form_id is 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 apply
  • partyId: Integer (optional) — the party to aggregate for (not required for payer-level forms)
  • taxYear: Integer — the tax year
  • quarterCd: String (optional) — the HMRC quarter (for quarterly forms)
  • jurisdictionCd: String — US or GB

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, jurisdictionCd from 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.

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_application joined to cash_receipt_worksheet, billing_item_detail, and billing_item:
      • Filter: billing_item.client_id = :partyId AND billing_item_detail.billing_item_detail_type_cd = 'PAY' AND cash_receipt_worksheet.cash_receipt_worksheet_status_cd IN ('P','T','A') AND cash_receipt_application.reversal_of_application_id IS NULL AND cash_receipt_worksheet.applied_dt within period range.
    • total_gross = SUM of cash_receipt_application.cash_receipt_amt_applied.
    • For the collected application IDs, query cash_receipt_application_deduction filtered by applicable deduction type codes (e.g., WH_US_NRA for IRS forms, WH_UK_FEU for HMRC forms).
    • total_withholding = SUM of cash_receipt_application_deduction.deduction_amt_applied.

Step 3. Assemble Form-Specific Data

  • Source: Aggregated totals and source records from Step 2, plus party_tax_info.
  • Action: Build the form_data_json and source_map_json structures for the specific form type.
  • Logic:
    • IRS 1042-S (per-party annual): gross_income = total_gross; federal_tax_withheld = total WH_US_NRA deductions; tax_rate = 0.30 (or 0 if has_tax_treaty_ind = true); income_code = 17; exemption_code = 04 (treaty) or 00 (none); recipient fields from party_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-S gross_income values; total_withholding_tax = sum of all federal_tax_withheld values; 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_income and total_tax_withheld summed from all 1042-S forms.
    • IRS 1099-NEC (per-party annual): nonemployee_compensation = total_gross; federal_tax_withheld = total WH_US_NRA deductions (typically 0 for US residents).
    • HMRC FEU1 (payer-level quarterly): aggregate across all parties with WH_UK_FEU deductions in the UK quarter; total_gross_payment and total_withholding summed across all parties.
    • HMRC FEU2 (per-party per-payment): total_gross_payment = total_gross; total_withholding = total WH_UK_FEU deductions; net_payment = total_gross_payment - total_withholding; performances = distinct deals from application records.
    • Build source_map_json: for each form field, record aggregation method (SUM, LOOKUP, COUNT), description, and source_records[] array (each entry: entity_type, entity_id, amount, label, worksheet_id, deal_name).

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_NRA deductions on active worksheets within the US tax year, bucket each deduction by cash_receipt_worksheet.applied_dt day-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.

Side-effects:

  • None. This procedure is read-only.

Postconditions:

  • Returns form_data_json and source_map_json structures suitable for inserting into tax_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_id
  • newStatusCd: String — target status: GENERATED, FILED, or SUBMITTED
  • userId: Integer — tax_form.updated_by_user_id

Transitions a tax form through its status lifecycle: DRAFTGENERATEDFILEDSUBMITTED.

Step 1. Apply Status Transition

  • Source: tax_form record identified by :taxFormId.
  • Action: UPDATE tax_form where tax_form_id = :taxFormId.
  • Logic:
    • Set tax_form.status_cd = :newStatusCd.
    • If :newStatusCd is 'FILED' or 'SUBMITTED': set tax_form.filed_dt = current timestamp.
    • Set tax_form.updated_by_user_id = :userId.
    • Set tax_form.updated_dt = current timestamp.

Side-effects:

  • None beyond the status change on the targeted form record.

Postconditions:

  • The tax_form record has status_cd = :newStatusCd.
  • When status is FILED or SUBMITTED, filed_dt is set.
  • SUBMITTED is 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_id
  • userId: 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_form where tax_form_id = :taxFormId.
  • Action: READ tax_form.status_cd.
  • Logic:
    • If status_cd is not 'DRAFT' or 'GENERATED': reject the operation. Forms in FILED or SUBMITTED status cannot be regenerated.

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_json and source_map_json.

Step 3. Update Tax Form Record

  • Source: Aggregated data from Step 2.
  • Action: UPDATE tax_form where tax_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.

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_form record.

Postconditions:

  • tax_form.form_data_json and tax_form.source_map_json reflect the current state of actual transaction records for the tax period.
  • tax_form.generated_dt is 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 filing
  • userId: 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.

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', worksheet cash_receipt_worksheet_status_cd IN ('P','T','A'), reversal_of_application_id IS NULL, and cash_receipt_worksheet.applied_dt falls within January 1 through December 31 of :taxYear.
  • Source: Existing tax_form records and party list from Step 2.
  • Action: For each party, either UPDATE an existing tax_form or INSERT a new one via procedure 2.7.
  • Logic:
    • For each party: check for an existing tax_form with form_code_cd = 'IRS_1042_S' and tax_year = :taxYear and status_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_form record via procedure 2.7 with form_code_cd = 'IRS_1042_S', linked to the package, mailing_cd = 'MAILING_1', chapter_cd = 'CH3_ORIG', sequence_no = next sequential number.

Step 4. Create 1042-T Transmittal Form

  • Source: All 1042-S forms created/linked in Step 3.
  • Action: INSERT into tax_form via 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_income and total_tax_withheld summed from all 1042-S form_data_json values.

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_form via 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-S gross_income values; total_withholding_tax = sum of all 1042-S federal_tax_withheld values; number_of_1042s_forms = count of 1042-S forms; tax_liability_by_period = semi-monthly breakdown.

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 of gross_income from all 1042-S form_data_json.
      • total_withheld_from_1042s = SUM of federal_tax_withheld from all 1042-S form_data_json.
      • form_1042t_gross = 1042-T total_gross_income.
      • form_1042t_withheld = 1042-T total_tax_withheld.
      • form_1042_gross = 1042 total_gross_amounts.
      • form_1042_withheld = 1042 total_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.

Side-effects:

  • Multiple tax_form records created or updated and linked to the new package via tax_filing_package_id.
  • Existing 1042-S forms for the year are linked into the package without re-aggregating their data.

Postconditions:

  • A tax_filing_package record exists with status_cd = 'GENERATED' and reconciliation_json populated.
  • The package contains: 1042-T at MAILING_1 sequence 1, all 1042-S forms at MAILING_1 sequence 2+, and Form 1042 at MAILING_2 sequence 1.
  • The generated tax_filing_package_id is 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_form record identified by :taxFormId.
  • Action: DELETE from tax_form where tax_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_form record 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 — US or GB
  • asOfDate: 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_config where jurisdiction_cd = :jurisdictionCd and tax_type_cd = :taxType and effective_from_dt <= :asOfDate and (effective_to_dt IS NULL OR effective_to_dt >= :asOfDate), ordered by effective_from_dt DESC, limit 1.
      • Collect the result (rate, threshold_amt, currency_cd, effective_from_dt) if found.

Side-effects:

  • None. This procedure is read-only.

Postconditions:

  • Returns a map of tax_type_cd to active rate result, suitable for storing as tax_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_cd compared to 'US' to determine residency.
  • Read: party_tax_info.has_cwa_approval_ind and party_tax_info.cwa_expiration_dt to 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_cd is 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'.
RuleSpecification
Base rate30% on gross income for non-resident aliens with US-source income
Source ruleOnly US-performed services are US-source income; services performed outside the US are foreign-source (no US withholding)
CWA overridehas_cwa_approval_ind = true and not expired: withholding = cwa_withholding_amt (flat dollar, not percentage)
CWA expiryIf cwa_expiration_dt < current date: CWA inactive, standard 30% applies
Treaty overridehas_tax_treaty_ind = true: 0% withholding; Form 8233 must be on file
PriorityCWA checked before treaty; both checked before default 30%
RoundingWithholding amounts rounded to nearest whole number
1042-S requirementRequired for ANY payment to a non-resident alien, even if withholding = $0
IRS 3-day ruleWhen accumulated undeposited withholding reaches $2,000, deposit required within 3 business days via EFTPS
Tax yearUS calendar year: January 1 through December 31
Filing deadlineMarch 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_cd compared to 'GB' to determine residency.
  • Read: deal_tax_context.uk_expected_ytd_total to set aware_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_cd is non-GB): no FEU withholding.
  • Guard: If uk_ytd_earnings + current_gross < 12,570 AND aware_of_exceeding_threshold = false: no withholding.
  • Write: Withholding deduction created with billing_item_deduction_type_cd = 'WH_UK_FEU'.
RuleSpecification
Base rate20% on gross earnings for foreign entertainers/sportspersons
Personal allowanceNo withholding when cumulative UK earnings for the tax year are below £12,570
Proactive withholdingWhen uk_expected_ytd_total >= 12,570, withholding applies even if actuals have not yet crossed the threshold
Residencytax_residence_cd = 'GB' means UK resident — no FEU withholding
Service locationOnly UK-performed services trigger FEU withholding
Non-retroactiveFEU withholding is not retroactive — only current and future payments
Currency conversionNon-GBP fees divided by FX rate to get GBP equivalent; withholding can be converted back for display
RoundingWithholding amounts rounded to nearest whole number
Tax yearUK tax year: April 6 through April 5
FEU1 filingQuarterly: 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 filingWithin 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_ind and deal_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_through data only. Commission VAT IS written with billing_item_deduction_type_cd = 'VAT_COMM'.
RuleSpecification
Artist fee VAT20% 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 VAT20% on commission amount. Applies: UK Agency + UK resident. UTA's VAT obligation. Deducted from PAY detail, reducing client payout.
Commission rateDefaults to 10% if not specified on the billing item detail
VAT roundingFLOOR(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_info where current_item_ind = true for active profile resolution.
  • Read: tax_rate_config ordered by effective_from_dt DESC where date range contains target date for active rate resolution.
  • Guard: Once effective_to_dt is set on a party_tax_info or tax_rate_config record, that record must not be modified. Changes are captured by creating a new effective-dated version.
  • Guard: Only one party_tax_info record per party may have current_item_ind = true. Enforced transactionally in procedure 2.1.1.
  • Write: Fallback to hardcoded constants when no tax_rate_config record is found: US NRA 0.30, UK FEU 0.20, UK VAT 0.20, UK personal allowance 12570.

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_config where jurisdiction_cd = :jurisdiction and tax_type_cd = 'TAX_YEAR' with the target date in range.
  • Guard: If no TAX_YEAR config is found, fall back to the calendar year (January 1 through December 31).
  • Write: No mutations. Tax year config is seeded by administrators.
Jurisdictioneffective_from_dteffective_to_dtExample
USJanuary 1 of yearDecember 31 of year2025-01-01 to 2025-12-31
GBApril 6 of yearApril 5 of year + 12025-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_audit records have no UPDATE path — each recalculation produces a new row.
  • Write: party_tax_info_snapshot_json and rate_config_snapshot_json must 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.FieldTarget Table.FieldTransform
deal_tax_context.billing_entity_cdwithholding_context.billing_entityCopied as-is
deal_tax_context.gross_amtwithholding_context.gross_amountCopied as-is (from billing_item_detail.billing_item_detail_gross_amt REV type)
party_tax_info.tax_residence_cdwithholding_context.talent_residenceCast to jurisdiction code
deal_tax_context.service_location_cdwithholding_context.service_locationFrom billing_item.service_country_cd, mapped to jurisdiction (see 4.5)
party_tax_info.has_cwa_approval_indwithholding_context.has_cwa_approvaltrue only when has_cwa_approval_ind = true AND cwa_expiration_dt >= now
party_tax_info.cwa_withholding_amtwithholding_context.cwa_withholding_amtCopied only if CWA valid; otherwise 0
party_tax_info.has_tax_treaty_indwithholding_context.has_tax_treatyCopied as-is
party_tax_info.treaty_country_cdwithholding_context.treaty_countryCopied as-is
deal_tax_context.uk_ytd_earningswithholding_context.uk_ytd_earningsComputed upstream from cumulative billing items
deal_tax_context.uk_deductible_expenseswithholding_context.uk_deductible_expensesCopied as-is
deal_tax_context.uk_expected_ytd_totalwithholding_context.aware_of_exceeding_thresholdtrue when uk_expected_ytd_total >= 12570
deal_tax_context.uk_non_gbp_currency_indwithholding_context.uk_non_gbp_currency_indCopied as-is
deal_tax_context.uk_fee_currency_cdwithholding_context.uk_fee_currency_cdCopied as-is
deal_tax_context.uk_fx_ratewithholding_context.uk_fx_rateCurrency units per 1 GBP
deal_tax_context.uk_fx_rate_sourcewithholding_context.uk_fx_rate_sourceHMRC or OVERRIDE
party_tax_info.vat_registered_indwithholding_context.vat_registered_indCopied 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.FieldTarget Table.FieldTransform
Resolved from billing_item_id + billing_item_detail_type_cdbilling_item_deduction.billing_item_detail_idLookup via billing_item_detail
Calculation output deduction_type_cdbilling_item_deduction.billing_item_deduction_type_cdWH_US_NRA, WH_UK_FEU, or VAT_COMM
Calculation output amountbilling_item_deduction.billing_item_deduction_amtString, precision 15 scale 2
billing_item_deduction.billing_item_deduction_update_net_indAlways true for tax deductions
Calculation output explanationbilling_item_deduction.commentAuto-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.FieldTarget Table.FieldTransform
cash_receipt_application.cash_receipt_amt_applied (PAY type)tax_form.form_data_json.gross_incomeSUM
cash_receipt_application_deduction.deduction_amt_applied (WH_US_NRA)tax_form.form_data_json.federal_tax_withheldSUM
party_tax_info.tax_residence_cdtax_form.form_data_json.recipient.country_of_residenceLOOKUP
party_tax_info.tin_type_cdtax_form.form_data_json.recipient.tin_typeLOOKUP
party_tax_info.has_tax_treaty_indtax_form.form_data_json.tax_rate0 if true; 0.30 if false

HMRC FEU2 field mapping:

Source Table.FieldTarget Table.FieldTransform
cash_receipt_application.cash_receipt_amt_applied (PAY, UK quarter)tax_form.form_data_json.total_gross_paymentSUM
cash_receipt_application_deduction.deduction_amt_applied (WH_UK_FEU)tax_form.form_data_json.total_withholdingSUM
Computed: total_gross_payment - total_withholdingtax_form.form_data_json.net_paymentCOMPUTED
party_tax_info.tax_residence_cdtax_form.form_data_json.performer.country_of_residenceLOOKUP
party_tax_info.vat_registered_indtax_form.form_data_json.performer.is_vat_registeredLOOKUP

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.FieldTarget Table.FieldTransform
uta_entity.uta_entity_name contains UK, GB, or LONDON (case-insensitive)deal_tax_context.billing_entity_cdSet to 'UK_AGENCY'
uta_entity.uta_entity_name — anything else (or null)deal_tax_context.billing_entity_cdDefault 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.FieldTarget Table.FieldTransform
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 unmappablewithholding_context.service_locationnull (service location unknown)

5. Cross-References

DocumentRelationship
Tax Withholding Data ModelTable 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 QueriesRead operations and aggregation patterns used as inputs to these procedures.
Worksheets Data ModelThe 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 ModelTax 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 ModelSplit-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 ModelSettlement 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.

Confidential. For internal use only.