← Back to Cases
AP/PO Accrual Reconciliation Write-Off — Distribution-Level Variance Clearance with Override and Amount Validation
Case Number00077357
Oracle ModuleAccounts Payable (AP) / Purchasing (PO)
EBS Version11i (11.5.10.2)
Functional AreaPO Accrual Reconciliation / Accrual Write-Offs / Receipt Variance
Related PatchesNone
Related Case00059272 (earlier write-off implementation — see also)

Symptoms

Root Cause

The same root cause as case 00059272 applies: rounding or conversion differences between PO receipt amounts and AP invoice amounts accumulate in net_po_line_amount and cannot be resolved through re-matching. This case adds a more sophisticated grouping approach — processing by po_distribution_id rather than by individual transaction row — to correctly handle distributions with multiple PO receipt transaction rows. The write-off logic evolved through five iterative phases (G through M), each refining the AP matching criteria, amount comparison function, and idempotency guards.

The five fix scripts represent a progressive refinement:


Resolution — Script 01: Create Backup and Error Tables

Pre-requisite — Create backup table and error log

Run 01_setup_driver_tables.sql first. Creates:


Resolution — Script 02: Write-Off Creation (Phase G)

Per-row PO processing with distribution-based AP matching and round() amount validation

Accepts three runtime parameters: &p_variable1 (org_id), &p_variable2 (variance amount), &p_variable3 (override Y/N, default N).

Outer cursor getPOReceipts: selects all columns from PO_ACCRUAL_RECONCILE_TEMP_ALL where abs(net_po_line_amount) = &p_variable2, org_id = &p_variable1, transaction_source_code = 'PO', net_po_line_quantity = 0, write_off_id IS NULL.

For each PO row:

  1. Backs up matching rows from PO_ACCRUAL_RECONCILE_TEMP_ALL into RSI_PO_ACCR_77357 (DUP_VAL_ON_INDEX protected).
  2. Creates one PO write-off record in PO_ACCRUAL_WRITE_OFFS_ALL using PO_ACCRUAL_WRITE_OFFS_S.NEXTVAL; updates the PO row (write_off_id, write_off_flag='Y', net_po_line_amount=0).
  3. Sets matching variables: v_po_distribution_id, v_transaction_amount = round(abs(rec1.transaction_amount)), v_transaction_quantity = abs(rec1.transaction_quantity).
  4. Loops getAPRecs cursor (matches on po_distribution_id, transaction_source_code='AP'): for each AP row, creates an AP write-off record and updates the AP row.
  5. Amount validation (controlled by override parameter):
  6. Commits per PO row. Errors logged to RSI_AP_PO_ERRORS_77357.

Resolution — Script 03: Write-Off Creation (Phase H)

Distribution-level grouping with inner PO transaction loop and floor() amount comparison

Key changes from Phase G:

Per-distribution processing: back up → inner PO loop (write-off per transaction) → inner AP loop (write-off per AP row) → amount validation with override → commit or rollback.


Resolution — Script 04: Write-Off Creation (Phase K)

Phase H with UNIQUE deduplication on inner cursors

Identical to Phase H with one change: both getPOTransactions and getAPRecs cursors use SELECT UNIQUE ROWNUM, ... to deduplicate rows within each distribution group before creating write-off records. This prevents duplicate write-off insertions when the underlying data contains duplicate rows in PO_ACCRUAL_RECONCILE_TEMP_ALL.


Resolution — Script 05: Write-Off Creation (Phase L)

Cleaner distribution-level deduplication — drop po_transaction_id from outer key

Identical to Phase K with one change: outer cursor getPOReceipts selects UNIQUE (po_header_id, po_line_id, line_location_id, po_distribution_id)po_transaction_id is removed from the outer driver key. This ensures the outer loop deduplicates strictly at the distribution level, eliminating any risk of the same distribution being processed multiple times due to different po_transaction_id values within the same distribution group.


Resolution — Script 06: Write-Off Creation (Phase M)

Most defensive version — adds write_off_flag guard against re-processing

Identical to Phase L with one additional filter added to both the preview query and the outer cursor getPOReceipts: AND write_off_flag <> 'Y'. This ensures the script is fully idempotent — distributions that were already written off in a previous run (where write_off_flag was set to 'Y') are excluded from reprocessing even if write_off_id IS NULL due to a partial prior update. Phase M is the preferred version for new runs.

Note: All five fix scripts (Phases G–M) accept the same three runtime parameters: org_id (&p_variable1), variance amount (&p_variable2), and override flag (&p_variable3, default N). The override parameter controls whether amount mismatches between PO and AP transaction totals cause rollback (N) or proceed with a warning (Y). Amount comparison in Phase G uses round(); Phases H through M use floor(). All scripts use REASON_ID = 232, WRITE_OFF_CODE = 'WRITE OFF', and LAST_UPDATED_BY = -9999. Script 01 must be run before any fix script. Run phases in order (G → H → K → L → M) only if earlier phases fail to resolve all records; for a clean engagement, run Phase M directly. The error table RSI_AP_PO_ERRORS_77357 includes a distribution_id column (absent in case 59272) enabling per-distribution error attribution. This case is closely related to case 00059272; the write-off logic and table structures are consistent between both cases except for the distribution-level grouping and override parameter added here.

Script Inventory

FileDescription
01_setup_driver_tables.sqlCreates backup table RSI_PO_ACCR_77357 (mirrors PO_ACCRUAL_RECONCILE_TEMP_ALL with unique index on 5 key columns) and error table RSI_AP_PO_ERRORS_77357 (includes distribution_id column). Must be run before any fix script.
02_data_fix_phase_g.sqlPhase G: Per-PO-row write-off with 3 parameters (org_id, variance, override Y/N). AP matched via po_distribution_id. Amount validation uses round(). Creates one PO write-off and one-or-more AP write-offs per outer PO row. Rolls back (or proceeds with warning if override=Y) when AP amount sum mismatches.
03_data_fix_phase_h.sqlPhase H: Distribution-level grouping. Outer key is UNIQUE (po_header_id, po_line_id, line_location_id, po_distribution_id, po_transaction_id). Inner getPOTransactions loop creates write-offs for all PO transactions in the distribution; amount comparison uses floor(). AP matched via po_distribution_id.
04_data_fix_phase_k.sqlPhase K: Identical to Phase H with SELECT UNIQUE added to getPOTransactions and getAPRecs cursors to deduplicate inner result sets.
05_data_fix_phase_l.sqlPhase L: Identical to Phase K with po_transaction_id removed from outer getPOReceipts key (strictly distribution-level outer deduplication on 4 fields).
06_data_fix_phase_m.sqlPhase M (preferred): Identical to Phase L with write_off_flag <> 'Y' added to outer cursor and preview query, preventing re-processing of already written-off distributions. Most idempotent and defensive version.

Key Tables Affected

TableAction
PO_ACCRUAL_WRITE_OFFS_ALLInsert write-off records for all PO transaction rows within each qualifying distribution (via PO_ACCRUAL_WRITE_OFFS_S.NEXTVAL), and for each matching AP accrual row (all phases)
PO_ACCRUAL_RECONCILE_TEMP_ALLUpdate write_off_id, write_off_flag='Y', net_po_line_amount=0 for each written-off PO transaction and AP accrual row (all phases)
RSI_PO_ACCR_77357Backup of PO_ACCRUAL_RECONCILE_TEMP_ALL rows for the qualifying distribution before write-off (all phases)
RSI_AP_PO_ERRORS_77357Error log for per-transaction and per-distribution processing failures; includes distribution_id for precise error attribution (all phases)