Deals, Sales Items and Payment Terms Procedures
1. Executive Summary
This document describes the step-by-step data mutation operations for the Deals, Sales Items and Payment Terms domain. It covers deal creation and party management, the sales block pipeline that transforms sales items and payment terms into revenue items and billing items, and direct sales pipeline operations.
Sales block pipeline — The primary workflow. Sales items and payment terms arrive in sales blocks (sales_block, sales_item, payment_term) and are processed into revenue_items and billing_item records.
Tables in Scope:
| Table | Role |
|---|---|
deal | Core commercial agreement |
deal_party | Party-commission associations on a deal |
sales_item | Versioned revenue line items (sales pipeline) |
sales_item_party | Per-party commissions on a sales item |
sales_meta_data | Extensible key-value metadata on sales items |
sales_block | Batch container for sales pipeline processing |
payment_term | Versioned payment terms (sales pipeline) |
Not covered (documented separately):
- Revenue items (
revenue_items,revenue_item_schedules) — see Billing Items Data Model - Billing items (
billing_item,billing_item_detail) — see Billing Items Data Model - Parties (
party,party_role,party_bank_account) — see Parties Data Model
2. Key Procedures
2.1 Create Deal
Operation: createDeal
Trigger: User submits the Create Deal form with a deal name.
Input Parameters:
dealName: String (required) —deal.deal_namedealReference: String (optional) —deal.deal_referencedealStartDt: Date (optional) —deal.deal_start_dtdealEndDt: Date (optional) —deal.deal_end_dtactiveInd: Boolean (optional) —deal.active_indactorId: String — identifier for the user performing the operation
Creates a new commercial agreement record in deal.
Step 1. Resolve Deal Reference
- Source: Input parameters.
- Action: Compute the
deal_referencevalue. - Logic:
- If
dealReferenceis not provided, generate one using the patternDEAL-{random_alphanumeric}. - If
dealReferenceis provided, use it as-is.
- If
Step 2. Insert Deal Row
- Source: Resolved reference and all input parameters.
- Action: INSERT one row into
deal. - Logic:
- Set
deal_referenceto the resolved reference from Step 1. - Set
deal_name=dealName. - Set
deal_start_dt=dealStartDt(if provided). - Set
deal_end_dt=dealEndDt(if provided). - Set
active_ind=activeInd(if provided). - Set
created_by=actorId,created_dt=now(). - Set
updated_by=actorId,updated_dt=now().
- Set
Side-effects:
- None beyond the
dealrow itself.
Postconditions:
- One new
dealrow exists with the generated or provideddeal_reference. - The new
deal_idis returned to the caller.
2.2 Update Deal Details
Operation: updateDeal
Trigger: User edits deal fields and saves from the deal detail screen.
Input Parameters:
dealId: Integer (required) —deal.deal_iddealName: String (optional) —deal.deal_namedealReference: String (optional) —deal.deal_referencedealStartDt: Date (optional) —deal.deal_start_dtdealEndDt: Date (optional) —deal.deal_end_dtactiveInd: Boolean (optional) —deal.active_indactorId: String — identifier for the user performing the operation
Modifies an existing deal record in place.
Step 1. Update Deal Row
- Source:
deal(record matchingdealId). - Action: UPDATE the
dealrow matchingdeal_id. - Logic:
- Apply any provided field changes to
deal_name,deal_reference,deal_start_dt,deal_end_dt,active_ind. - Set
updated_dt=now(). - Set
updated_by=actorId.
- Apply any provided field changes to
Side-effects:
- None beyond the
dealrow itself.
Postconditions:
- The
dealrow now reflects the updated field values. - The updated deal record is returned to the caller.
2.3 Upsert Deal with Parties
Operation: upsertDealWithParties
Trigger: User saves a deal form that includes a party list, or an integration upserts a deal with its full party composition.
Input Parameters:
dealId: Integer (optional) —deal.deal_id; absent means createdealName: String (required for create) —deal.deal_nameparties: Array of party objects —deal_partyrows; each containsdealPartyId,partyId,partyRoleTypeCd,dealPartyCommissionFlatInd,dealPartyCommissionPerc,dealPartyCommissionAmt,bankAccountId,isDeletedactorId: String — identifier for the user performing the operation
Creates or updates a deal and synchronizes its party list in a single transaction. The party list provided is treated as authoritative — parties not in the list are deleted.
Step 1. Upsert the Deal
- Source: Input parameters.
- Action: INSERT or UPDATE
deal. - Logic:
- If
dealIdis provided: update the existingdealrow (apply field changes, setupdated_dt=now()). - If
dealIdis absent: insert a newdealrow (generate reference if not provided, setcreated_dt=now()). - Resolve the deal ID for subsequent steps.
- If
Step 2. Fetch Existing Parties
- Source:
deal_party(all rows wheredeal_id= the resolved deal ID). - Action: SELECT all existing
deal_partyrows. - Logic:
- Collect the set of existing
deal_party_idvalues for comparison in Step 3.
- Collect the set of existing
Step 3. Process the Provided Party List
- Source:
partiesinput array. - Action: INSERT, UPDATE, or DELETE
deal_partyrows based on each party's state. - Logic:
- Set
deal_id= the resolved deal ID on every party. - Sanitize commission fields: blank or null
deal_party_commission_percdefaults to'0'; blank or nulldeal_party_commission_amtdefaults to'0'. - If the party has a
dealPartyIdANDisDeleted= true: DELETE thedeal_partyrow. - If the party has a
dealPartyIdANDisDeleted= false: UPDATE the existingdeal_partyrow with new values. - If the party has no
dealPartyIdANDisDeleted= false: INSERT a newdeal_partyrow.
- Set
Step 4. Delete Orphaned Parties
- Source: The set of existing
deal_party_idvalues collected in Step 2. - Action: DELETE
deal_partyrows for any IDs not represented in the input. - Logic:
- For each
deal_party_idin the existing set that was not included or was flaggedisDeletedin the input, delete thedeal_partyrow.
- For each
IMPORTANT
The party list is authoritative. Any existing deal_party row not represented in the input (or marked isDeleted) will be deleted. This ensures the deal's party composition exactly matches the submitted list.
Side-effects:
- All
deal_partyrows for the deal are reconciled to the submitted list within the transaction.
Postconditions:
- The
dealrow reflects the upserted values. - All
deal_partyrows for the deal exactly match the provided party list.
2.4 Create Deal Party
Operation: createDealParty
Trigger: User adds a party to an existing deal from the deal parties management screen.
Input Parameters:
dealId: Integer (required) —deal_party.deal_idpartyId: Integer (required) —deal_party.party_idpartyRoleTypeCd: String (required) —deal_party.party_role_type_cd; must be a validPARTY_ROLE_TYPE_CDcode master valuedealPartyCommissionFlatInd: Boolean (required) —deal_party.deal_party_commission_flat_inddealPartyCommissionPerc: Decimal string (required) —deal_party.deal_party_commission_percdealPartyCommissionAmt: Decimal string (required) —deal_party.deal_party_commission_amtbankAccountId: Integer (optional) —deal_party.bank_account_idactorId: String — identifier for the user performing the operation
Inserts a single new deal_party row linking a party to a deal with its commission structure.
Step 1. Insert Deal Party Row
- Source: Input parameters.
- Action: INSERT one row into
deal_party. - Logic:
- Set
deal_id=dealId. - Set
party_id=partyId. - Set
party_role_type_cd=partyRoleTypeCd. - Set
deal_party_commission_flat_ind=dealPartyCommissionFlatInd. - Set
deal_party_commission_perc=dealPartyCommissionPerc(decimal(7,4)). - Set
deal_party_commission_amt=dealPartyCommissionAmt(decimal(15,2)). - Set
bank_account_id=bankAccountId(if provided). - Set
created_by=actorId,created_dt=now(). - Set
updated_by=actorId,updated_dt=now().
- Set
Side-effects:
- None beyond the
deal_partyrow itself.
Postconditions:
- One new
deal_partyrow exists linking the party to the deal.
2.5 Update Deal Party
Operation: updateDealParty
Trigger: User edits a party entry on a deal and saves.
Input Parameters:
dealPartyId: Integer (required) —deal_party.deal_party_id- Any subset of updatable fields:
partyRoleTypeCd,dealPartyCommissionFlatInd,dealPartyCommissionPerc,dealPartyCommissionAmt,bankAccountId actorId: String — identifier for the user performing the operation
Updates the deal_party row matching dealPartyId with the provided field changes.
Step 1. Update Deal Party Row
- Source:
deal_party(record matchingdealPartyId). - Action: UPDATE
deal_party. - Logic:
- Apply any provided field changes.
- Set
updated_by=actorId,updated_dt=now().
Side-effects:
- None beyond the
deal_partyrow itself.
Postconditions:
- The
deal_partyrow reflects the updated field values.
2.6 Delete Deal Party
Operation: deleteDealParty
Trigger: User removes a party from a deal.
Input Parameters:
dealPartyId: Integer (required) —deal_party.deal_party_id
Deletes the deal_party row matching dealPartyId.
Step 1. Delete Deal Party Row
- Source:
deal_party(record matchingdealPartyId). - Action: DELETE from
deal_party. - Logic:
- Delete the row where
deal_party_id=dealPartyId.
- Delete the row where
Side-effects:
- None beyond the deleted
deal_partyrow.
Postconditions:
- The
deal_partyrow no longer exists.
2.7 Process Revenue Update
Operation: processRevenueUpdate
Trigger: Called after a sales block is created, either immediately inline or as a deferred batch job.
Input Parameters:
salesBlockId: Integer (required) —sales_block.sales_block_id
Processes a sales block to generate or update revenue items and billing items downstream.
Step 1. Fetch Sales Item for Block
- Source:
sales_item(record linked tosalesBlockId). - Action: SELECT the
sales_itemfor the given block. - Logic:
- Abort with error if no sales item found for the block.
Step 2. Run Three-Layer Validation
- Source:
sales_item,payment_term,deal,party,code_master,revenue_items. - Action: Execute validation layers in order. On failure, update block status and abort.
- Logic:
- Layer 1 (Data): Verify all required fields on
sales_itemare present and non-null. Verifygross_amtanduta_commission_amtare non-negative. Verify|SUM(payment_term.gross_amt) - sales_item.gross_amt| <= 0.01. - Layer 2 (Consistency): If a
revenue_itemsrow already exists for thissales_item_ref(withcurrent_item_ind= true), verify that immutable fields match:sales_item_ref,deal_id,uta_entity_id,client_entity_id,buyer_entity_id,currency_cd. - Layer 3 (Referential): Verify all foreign key references exist:
deal.deal_id,party.party_idfor client, buyer, and payment parties; code master values foruta_commission_type,rev_rec_style_cd,revenue_date_status_cd,sales_item_status_cd,currency_cd, and each payment term'sdue_date_status_cd. - If any layer fails: UPDATE
sales_block.process_status_cd='F'and seterror_description. Abort.
- Layer 1 (Data): Verify all required fields on
Step 3. Check for Existing Revenue Item
- Source:
revenue_items(row withsales_item_refmatching the block's sales item,current_item_ind= true). - Action: SELECT to determine create vs. update path.
- Logic:
- If a
revenue_itemsrow exists for thissales_item_ref: proceed to the Update path (Step 4a). - If no
revenue_itemsrow exists: proceed to the Create path (Step 4b).
- If a
Step 4a. Update Path — Update Existing Revenue Item
- Source: Existing
revenue_itemsrow and updatedsales_itemdata. - Action: Update the existing
revenue_itemsrow with changed field values. - Logic:
- Apply changed fields:
revenue_item_name,revenue_item_gross_amt,revenue_item_commission_amt,revenue_item_start_dt,revenue_item_end_dt,revenue_item_commission_perc,revenue_item_rec_style_cd,revenue_item_status_cd,revenue_item_date_status_cd. - The revenue service handles versioning, schedule regeneration, and billing item updates internally.
- Apply changed fields:
Step 4b. Create Path — Create New Revenue Item
- Source:
sales_itemfields from the block. - Action: INSERT into
revenue_itemsand generate revenue schedules. - Logic:
- Map
sales_itemfields torevenue_itemsfields (see Section 4.1). - Set
current_item_ind= true. - Generate
revenue_item_schedulesbased onrev_rec_style_cd.
- Map
Step 5. Create Billing Items (Create Path Only)
- Source:
payment_termrows for the block and the newly createdrevenue_itemsrow. - Action: For each
payment_term, INSERT onebilling_itemand twobilling_item_detailrows. - Logic:
- Map
payment_termfields tobilling_item(see Section 4.2). - For each
billing_item, create exactly twobilling_item_detailrows using the REV/PAY split calculation (see Section 4.3). - Set
billing_item_status_cd='U',open_item_ind= true,current_item_ind= true.
- Map
Step 6. Update Sales Block Status
- Source:
sales_block(record matchingsalesBlockId). - Action: UPDATE
sales_block.process_status_cd. - Logic:
- Set
process_status_cd='P'(Processed) on success.
- Set
Side-effects:
sales_block.process_status_cdis updated to'P'on success or'F'on failure.- On failure:
sales_block.error_descriptionis populated with the failure reason.
Postconditions:
sales_block.process_status_cd='P'on success.- On create path: one
revenue_itemsrow and associated schedules created; Nbilling_itemrows and 2Nbilling_item_detailrows created. - On update path: the existing
revenue_itemsrow updated; downstream billing items updated by the revenue service.
2.8 Split Sales Item (Sales Pipeline)
Operation: splitSalesItemImmediate
Trigger: User initiates a payment term split from the Sales Pipeline screen.
Input Parameters:
paymentTermRef: String (required) —payment_term.payment_term_refof the term to splitrevenueItemId: Integer (required) —revenue_items.revenue_item_idof the associated revenue itemsplitAmount: Decimal (required) — the portion to carve off; must be > 0 and < original termgross_amtnewItemDueDate: Date (required) —payment_term.due_dtfor the versioned original's updated term
Splits a sales pipeline item directly, creating new revenue items and billing items for the split portion, and versioning the original with the remaining amount.
Step 1. Fetch and Validate Payment Term
- Source:
payment_term(record matchingpaymentTermRef). - Action: SELECT the payment term.
- Logic:
- Abort with error if not found.
- Abort with error if
splitAmount>= originalgross_amtorsplitAmount<= 0.
Step 2. Fetch Sales Item and Validate Single Term
- Source:
sales_item(viasales_block_idfrom the payment term). - Action: SELECT the sales item and verify the sales block has exactly one payment term.
- Logic:
- Abort with error if the sales item is not found.
- Abort with error if the sales block's payment term count != 1.
Step 3. Calculate Derived Amounts
- Source: Original
gross_amtanduta_commission_percfrom the sales item. - Action: Compute split amounts.
- Logic:
remaining_amount= originalgross_amt-splitAmount.remaining_commission=remaining_amount*uta_commission_perc.split_commission=splitAmount*uta_commission_perc.- Generate new references:
sales_item_ref=REF-{random},payment_term_ref=TERM-{random}.
Step 4. Create Split Portion (New Item)
- Source: Original sales item fields and computed amounts (within transaction).
- Action: INSERT
sales_block,sales_item,payment_termfor the split portion; then create revenue and billing items. - Logic:
- Insert
sales_blockwithprocess_status_cd='U'. - Insert
sales_itemwithgross_amt=splitAmount,revenue_end_dt= today, newsales_item_ref,sales_item_ver= 1. - Insert
payment_termwithgross_amt=splitAmount,due_dt= today. - Create a new
revenue_itemsrow from the split sales item. - Create
billing_itemandbilling_item_detailrows from the split payment term.
- Insert
Step 5. Create Versioned Original (Remaining Amount)
- Source: Original sales item fields and computed remaining amounts (within transaction).
- Action: INSERT new
sales_block,sales_item,payment_termrows for the versioned original; then update the existing revenue item. - Logic:
- Insert new
sales_blockwithprocess_status_cd='U'. - Insert new
sales_itemrow: samesales_item_refas original,sales_item_ver= original version + 1,gross_amt=remaining_amount,revenue_start_dt= today. - Insert new
payment_termrow: samepayment_term_refas original,payment_term_ver= original version + 1,gross_amt=remaining_amount,due_dt=newItemDueDate. - Update the existing
revenue_itemsrow with the remaining amount values.
- Insert new
NOTE
The sales pipeline split creates new rows for the versioned original rather than updating in place. Each version of a sales item exists as a separate row identified by sales_item_ref + sales_item_ver. This is the immutability pattern: once a sales block is created, its contents are never modified.
Side-effects:
- The existing
revenue_itemsrow is updated with the remaining amount. - New revenue and billing items are created for the split portion.
Postconditions:
- Two
sales_blockentries exist: one for the split portion, one for the versioned original. - Two
sales_itemrows share the originalsales_item_ref(differentsales_item_ver). - The split portion has its own new
sales_item_ref. - Revenue and billing items reflect the split.
2.9 Update Sales Item (Sales Pipeline — Versioned)
Operation: updateSalesItem
Trigger: User edits a sales item from the Sales management screen, or a package operation modifies revenue items.
Input Parameters:
salesItemId: Integer (required) —sales_item.sales_item_id- Any subset of updatable fields from
sales_item paymentTerms: Array (optional) — updated payment terms with refs and amountsactorId: String — identifier for the user performing the operation
Updates a sales item in the sales pipeline by creating a new version within a new sales block.
Step 1. Fetch Current Sales Item
- Source:
sales_item(record matchingsalesItemId). - Action: SELECT the current sales item row.
- Logic:
- Collect original field values for merging with updates.
Step 2. Create New Sales Block
- Source: Input
actorId. - Action: INSERT one row into
sales_block(within transaction). - Logic:
- Set
process_status_cd='U'.
- Set
Step 3. Insert New Sales Item Version
- Source: Original sales item fields merged with provided updates.
- Action: INSERT one new row into
sales_item. - Logic:
- Set
sales_block_id= new block's ID. - Set
sales_item_ref= same as original (preserves identity across versions). - Set
sales_item_ver= original version + 1. - Merge original field values with provided updates.
- Recalculate
uta_commission_amt=gross_amt*uta_commission_perc.
- Set
Step 4. Insert Payment Terms for New Version
- Source: Provided
paymentTermsinput or original block's terms. - Action: INSERT
payment_termrows into the new block. - Logic:
- If
paymentTermsprovided: for each term with an existing ref, fetch the latestpayment_term_verand increment; for new terms, generate aTERM-{random}ref with version 1. - If
paymentTermsnot provided: copy allpayment_termrows from the originalsales_block_idinto the new block, incrementing eachpayment_term_ver.
- If
IMPORTANT
This procedure does NOT modify or delete the original sales_item row. The original remains as a historical record. The new version with the higher sales_item_ver becomes the current version. The concept of "current" is determined by finding the highest sales_item_ver for a given sales_item_ref.
Side-effects:
- None to existing rows. The original
sales_itemand itspayment_termrows are untouched.
Postconditions:
- One new
sales_blockrow exists withprocess_status_cd='U'. - One new
sales_itemrow exists withsales_item_ver= original + 1. - N new
payment_termrows exist in the new block.
2.10 Create Sales Item (Sales Pipeline — New)
Operation: createSalesItemInPipeline
Trigger: A new sales item is submitted directly to the sales pipeline.
Input Parameters:
- All
sales_itemfields required for creation: entity refs, financial fields, status codes, dates paymentTerms: Array (optional) — payment term specificationsactorId: String — identifier for the user performing the operation
Creates a brand-new sales item in the sales pipeline from scratch.
Step 1. Create New Sales Block
- Source: Input
actorId. - Action: INSERT one row into
sales_block(within transaction). - Logic:
- Set
process_status_cd='U'.
- Set
Step 2. Insert Sales Item
- Source: Input parameters.
- Action: INSERT one row into
sales_item. - Logic:
- Set
sales_block_id= new block's ID. - Generate
sales_item_ref=REF-{random_alphanumeric}. - Set
sales_item_ver= 1. - Set
uta_commission_amt=gross_amt*uta_commission_perc. - Set all entity and status fields from input.
- Set
Step 3. Insert Payment Terms
- Source: Provided
paymentTermsarray or default single-term logic. - Action: INSERT
payment_termrows. - Logic:
- If
paymentTermsprovided: insert each with a generatedTERM-{random}ref,payment_term_ver= 1. - If
paymentTermsnot provided: create a single default term with fullgross_amt,due_dt= today,due_date_status_cd='EST'.
- If
Side-effects:
- None beyond the inserted rows.
Postconditions:
- One new
sales_blockrow withprocess_status_cd='U'. - One new
sales_itemrow withsales_item_ver= 1. - N new
payment_termrows.
2.11 Sales Metadata Management
Operation: createSalesMetaData / updateSalesMetaData / deleteSalesMetaData
Trigger: System or user creates, modifies, or removes metadata entries associated with a sales item reference.
Input Parameters:
salesItemRef: String (required for create) —sales_meta_data.sales_item_refmetaDataTypeCd: String (required for create) —sales_meta_data.meta_data_type_cdmetaDataValue: String (optional) —sales_meta_data.meta_data_valuemetaDataDateValue: Date (optional) —sales_meta_data.meta_data_date_valuecomment: String (optional) —sales_meta_data.commentstartDt: Date (optional) —sales_meta_data.start_dtendDt: Date (optional) —sales_meta_data.end_dtsalesMetaDataId: Integer (required for update/delete by ID) —sales_meta_data.sales_meta_data_idactorId: String (required for create) — identifier for the user performing the operation
CRUD operations on the sales_meta_data table, which stores extensible key-value pairs associated with a sales_item_ref.
Step 1. Create Metadata
- Source: Input parameters.
- Action: INSERT one row into
sales_meta_data. - Logic:
- Validate:
sales_item_ref(min 1 char),meta_data_type_cd(min 1 char),created_by(min 1 char) are required. - Insert with all provided fields.
- Validate:
Step 2. Update Metadata
- Source:
sales_meta_data(record matchingsalesMetaDataId). - Action: UPDATE
sales_meta_data. - Logic:
- Validate input against update schema.
- Apply provided field changes.
Step 3. Delete Metadata
- Source:
sales_meta_data. - Action: DELETE rows based on the specified granularity.
- Logic:
- By ID: delete the single row matching
salesMetaDataId. - By sales item ref: delete all rows matching
sales_item_ref. - By sales item ref and type: delete all rows matching both
sales_item_refandmeta_data_type_cd.
- By ID: delete the single row matching
Side-effects:
- None beyond the
sales_meta_datarows themselves.
Postconditions:
- On create: one new
sales_meta_datarow exists. - On update: the row reflects the updated field values.
- On delete: matching rows no longer exist.
2.12 Update Sales Block Status
Operation: updateSalesBlockStatus
Trigger: Called internally by processRevenueUpdate on success or failure, or manually by an administrator.
Input Parameters:
salesBlockId: Integer (required) —sales_block.sales_block_idstatusCd: String (required) — newprocess_status_cdvalue ('P'or'F')errorDescription: String (optional) —sales_block.error_description(used whenstatusCd='F')
Manually updates the processing status of a sales block.
Step 1. Update Sales Block Status
- Source:
sales_block(record matchingsalesBlockId). - Action: UPDATE
sales_block. - Logic:
- Set
process_status_cd=statusCd. - Set
error_description=errorDescriptionifstatusCd='F'.
- Set
Side-effects:
- None beyond the
sales_blockrow itself.
Postconditions:
sales_block.process_status_cdreflects the new status.sales_block.error_descriptionis populated if status is'F'.
3. Business Rules & Logic
3.1 Sales Item Immutability Pattern (Versioning)
Business rule: Sales items and payment terms in the sales pipeline are append-only. Once a row is written into a sales_block, it is never modified. Updates produce a new version row.
Data-level enforcement:
- Read: Each row has a stable
sales_item_ref(orpayment_term_ref) that persists across all versions, and an incrementingsales_item_ver(orpayment_term_ver). - Guard: The "current" version is determined by the highest
sales_item_verfor a givensales_item_ref. No unique constraint prevents multiple version rows — the application selects the maximum. - Write: Updates insert new
sales_itemandpayment_termrows in a newsales_block. The original rows remain as historical records.
3.2 Payment Terms Sum Constraint
Business rule: The sum of all payment terms for a sales item must equal the sales item's gross amount, within a tolerance of 0.01.
Data-level enforcement:
- Read:
SUM(payment_term.gross_amt)vs.sales_item.gross_amt. - Guard: If
|SUM - gross_amt| > 0.01, the processing operation is rejected with an error message. - Write: Not applicable — this rule is a precondition.
3.3 Revenue Item Existence Check (Create vs. Update)
Business rule: When processing a sales block, if a revenue item already exists for the sales_item_ref, the system updates it rather than creating a duplicate.
Data-level enforcement:
- Read:
revenue_itemswheresales_item_ref= the sales item's ref andcurrent_item_ind= true. - Guard: None — the check itself determines the code path.
- Write: On existing: UPDATE
revenue_itemsand cascade to billing items. On new: INSERTrevenue_items, schedules,billing_item, andbilling_item_detail.
This check is the mechanism by which repeated syncs update existing downstream records rather than creating duplicates.
3.4 Commission Percentage Rules
Business rule: Commission percentages are expressed as decimal fractions (not whole percentages). A 10% commission is stored as 0.10. Deal-party commissions use a wider decimal precision than sales-item-level commissions.
Data-level enforcement:
- Read:
deal_party.deal_party_commission_perc(decimal(7,4));sales_item.uta_commission_perc(decimal(5,4)). - Guard:
uta_commission_percmust be between 0 and 1 (inclusive). Enforced at the application validation layer. - Write:
- If
uta_commission_type='P': setuta_commission_amt=gross_amt*uta_commission_perc. - If
uta_commission_type='F': useuta_commission_amtdirectly;uta_commission_percis ignored.
- If
3.5 Sales Block Processing Lifecycle
Business rule: Each sales block moves through a one-way processing lifecycle. Once processed or failed, the status does not change except by explicit administrator action.
Data-level enforcement:
- Read:
sales_block.process_status_cd. - Guard: No formal DB constraint prevents re-processing, but application logic treats
'P'blocks as final. - Write:
'U'set at creation.'P'set after successful revenue and billing item generation.'F'set if any validation layer fails or processing throws an error;error_descriptionrecords the reason.'X'set manually for blocks that should be intentionally skipped.
3.6 Single Payment Term Requirement for Split
Business rule: A sales item can only be split if it has exactly one payment term. Items with multiple payment terms cannot be split.
Data-level enforcement:
- Read: Count of
payment_termrows for the item'ssales_block_id. - Guard: If count != 1, reject the split operation with an error message.
- Write: Not applicable — this rule is a precondition.
4. Field Mapping & Transformation
4.1 Sales Item to Revenue Item Mapping
When a sales_item from the pipeline is processed into a revenue_items record by 2.7 Process Revenue Update:
| Source Table.Field | Target Table.Field | Transform |
|---|---|---|
sales_item.uta_entity_id | revenue_items.uta_entity_id | Copied as-is |
sales_item.agent_group_id | revenue_items.agent_group_id | Copied as-is |
sales_item.sales_item_ref | revenue_items.sales_item_ref | Copied as-is |
sales_item.sales_item_status_cd | revenue_items.revenue_item_status_cd | Copied as-is (defaults to 'U' if null) |
sales_item.name | revenue_items.revenue_item_name | Copied as-is |
sales_item.revenue_date_status_cd | revenue_items.revenue_item_date_status_cd | Copied as-is (defaults to 'U' if null) |
sales_item.deal_id | revenue_items.deal_id | Parsed to integer |
sales_item.client_entity_id | revenue_items.client_id | Copied as-is |
sales_item.contracted_party_id | revenue_items.contracted_party_id | Copied as-is |
sales_item.buyer_entity_id | revenue_items.buyer_id | Copied as-is |
sales_item.department_id | revenue_items.department_id | Copied as-is |
sales_item.currency_cd | revenue_items.currency_cd | Copied as-is |
sales_item.gross_amt | revenue_items.revenue_item_gross_amt | Copied as-is |
sales_item.uta_commission_type | revenue_items.revenue_item_commission_flat_ind | 'FLAT' → true; else false |
sales_item.uta_commission_perc | revenue_items.revenue_item_commission_perc | Copied as-is |
sales_item.uta_commission_amt | revenue_items.revenue_item_commission_amt | Copied as-is |
sales_item.revenue_start_dt | revenue_items.revenue_item_start_dt | Copied as-is |
sales_item.revenue_end_dt | revenue_items.revenue_item_end_dt | Copied as-is |
sales_item.rev_rec_style_cd | revenue_items.revenue_item_rec_style_cd | Copied as-is |
| — | revenue_items.current_item_ind | Defaulted to true |
4.2 Payment Term to Billing Item Mapping
When a payment_term is processed into a billing_item by 2.7 Process Revenue Update:
| Source Table.Field | Target Table.Field | Transform |
|---|---|---|
payment_term.payment_term_ref | billing_item.payment_term_ref | Copied as-is |
payment_term.due_dt | billing_item.billing_item_due_dt | Copied as-is |
payment_term.due_dt | billing_item.billing_item_aging_dt | Copied as-is (same as due date) |
payment_term.due_date_status_cd | billing_item.billing_item_due_dt_status_cd | Copied as-is (defaults to 'U') |
payment_term.name | billing_item.billing_item_name | Copied as-is |
payment_term.payment_party_id | billing_item.collection_party_id | Copied as-is |
| — | billing_item.billing_item_status_cd | Defaulted to 'U' (Unprocessed) |
| — | billing_item.open_item_ind | Defaulted to true |
| — | billing_item.current_item_ind | Defaulted to true |
| — | billing_item.collection_style_cd | 'BUYER' if payment_party_id = buyer; else 'CLIENT' |
Context fields carried from the sales item context onto each billing_item:
| Source Table.Field | Target Table.Field | Transform |
|---|---|---|
sales_item.deal_id | billing_item.deal_id | Copied as-is |
sales_item.uta_entity_id | billing_item.uta_entity_id | Copied as-is |
sales_item.agent_group_id | billing_item.agent_group_id | Copied as-is |
sales_item.buyer_entity_id | billing_item.buyer_id | Copied as-is |
sales_item.client_entity_id | billing_item.client_id | Copied as-is |
sales_item.contracted_party_id | billing_item.contracted_party_id | Copied as-is |
sales_item.currency_cd | billing_item.billing_item_currency_cd | Copied as-is |
sales_item.department_id | billing_item.department_id | Copied as-is |
sales_item.service_country_cd | billing_item.service_country_cd | Copied as-is |
sales_item.service_state_cd | billing_item.service_state_cd | Copied as-is |
4.3 Billing Item Detail Split Calculation
For each billing_item, two billing_item_detail rows are created. Given gross_amt (payment term gross) and commission_perc (UTA commission percentage as decimal fraction):
REV detail (UTA's commission):
| Source Table.Field | Target Table.Field | Transform |
|---|---|---|
| — | billing_item_detail.billing_item_detail_type_cd | Defaulted to 'REV' |
payment_term.gross_amt | billing_item_detail.billing_item_detail_gross_amt | Copied as-is |
sales_item.uta_commission_perc | billing_item_detail.billing_item_detail_percent | Copied as-is |
| — | billing_item_detail.billing_item_detail_amt | gross_amt * commission_perc |
| — | billing_item_detail.billing_item_detail_tax_amt | Defaulted to 0.00 |
| — | billing_item_detail.billing_item_detail_total_amt | = billing_item_detail_amt |
| — | billing_item_detail.posting_status_cd | Defaulted to 'U' |
PAY detail (client's share):
| Source Table.Field | Target Table.Field | Transform |
|---|---|---|
| — | billing_item_detail.billing_item_detail_type_cd | Defaulted to 'PAY' |
payment_term.gross_amt | billing_item_detail.billing_item_detail_gross_amt | Copied as-is |
| — | billing_item_detail.billing_item_detail_percent | 1 - commission_perc |
| — | billing_item_detail.billing_item_detail_amt | gross_amt * (1 - commission_perc) |
| — | billing_item_detail.billing_item_detail_tax_amt | Defaulted to 0.00 |
| — | billing_item_detail.billing_item_detail_total_amt | = billing_item_detail_amt |
| — | billing_item_detail.posting_status_cd | Defaulted to 'U' |
NOTE
Tax amounts are initially zero and are populated later by the withholding tax service based on contracted_party_id, uta_entity_id, and service_country_cd.
4.4 Original to Split Sales Item Mapping
When a sales item is split via 2.8 Split Sales Item:
| Source Table.Field | Split-Off Item (New) | Versioned Original (Remaining) |
|---|---|---|
sales_item_ref | NEW reference generated | SAME as original |
sales_item_ver | 1 | Original version + 1 |
gross_amt | split_amount | original_gross - split_amount |
uta_commission_amt | split_amount * commission_perc | remaining * commission_perc |
revenue_start_dt | Original start date (preserved) | Today (PST) |
revenue_end_dt | Today (PST) | Original end date |
| All other entity and status fields | Copied from original | Copied from original |
5. Cross-References
| Document | Relationship |
|---|---|
| Deals, Sales Items and Payment Terms Data Model | Schema definitions, field types, constraints, code master values, and entity relationships for all tables in this domain. |
| Billing Items Data Model | billing_item.deal_id → deal.deal_id. billing_item.payment_term_ref matches payment_term.payment_term_ref by value. revenue_items.sales_item_ref matches sales_item.sales_item_ref by value. Downstream tables created by 2.7 Process Revenue Update. |
| Parties Data Model | deal_party.party_id → party.party_id. sales_item.client_entity_id, sales_item.buyer_entity_id, sales_item.contracted_party_id, and payment_term.payment_party_id all reference party.party_id. |
| Tax & Withholding Data Model | sales_item.service_country_cd and service_state_cd drive tax jurisdiction determination. Tax deductions are calculated at billing item creation time based on contracted_party_id, uta_entity_id, and service_country_cd. |