3 Way PO Matching SQL Query in Oracle Fusion: PO, Receipt, and Invoice

3 Way Matching SQL Query in Oracle Fusion: Purchase Order, Receipt, and Invoice Reconciliation

In any procurement system, ensuring the accuracy of supplier payments is critical. One of the most robust methods for validating supplier invoices is the 3-way matching process. This blog breaks down the 3-way matching concept in Oracle E-Business Suite (EBS) or Oracle Fusion, explains its benefits, and presents a detailed SQL query that helps extract and reconcile PO, Receipt, and Invoice data.


What is 3-Way Matching ?

3-Way Matching is a process that compares the following three documents before payment is released:

  1. Purchase Order (PO) – What you ordered

  2. Receipt – What you received

  3. Invoice – What the supplier billed

This comparison ensures that you only pay for goods that were ordered and received, avoiding overpayments, duplicates, and fraud.


Extract 3-Way Matching Data

Below is a comprehensive SQL query to pull data across PO, Receiving, and Invoices. It is helpful for audit, reconciliation, or payment troubleshooting in Oracle EBS or Fusion.

Query Breakdown:

  • PO details: po_headers_all, po_lines_all, po_line_locations_all, po_distributions_all

  • Receipts: rcv_transactions, rcv_shipment_headers, rcv_shipment_lines

  • Invoices: ap_invoices_all, ap_invoice_lines_all, ap_payment_schedules_all


SQL Code:

SELECT
    po.po_number,
    po.po_order_date,
    po.po_currency_code,
    po.po_line_number,
    po.po_status,
    po.quantity_p,
    po.amount_p,
    po.amount_received_p,
    po.po_line_qty,
    po.polineamount,
    po.quantity_received,
    po.po_promised_date,
    po.po_charge_account,
    po.promise_delivery_date,
    po.po_distribution_num,
    rcv.*,
    inv.invoice_num,
    inv.line_number,
    inv.invoice_line_amount_lc,
    inv.payment_status,
    inv.invoice_currency_code
FROM
    (
        SELECT
            pha.segment1 po_number,
            TO_CHAR(pha.creation_date, 'DD-Mon-YYYY', 'nls_date_language=english') po_order_date,
            pha.currency_code po_currency_code,
            pla.line_num po_line_number,
            pha.document_status po_status,
            pla.line_status po_line_status,
            pla.quantity quantity_p,
            plla.amount amount_p,
            plla.amount_received amount_received_p,
            CASE
                WHEN plla.quantity IS NULL THEN plla.amount
                ELSE pla.unit_price
            END polineamount,
            CASE
                WHEN pla.quantity IS NULL THEN plla.amount
                ELSE pla.quantity
            END po_line_qty,
            CASE
                WHEN pla.quantity IS NULL THEN plla.amount - plla.amount_received
                ELSE pla.quantity - plla.quantity_received
            END qty_open,
            CASE
                WHEN plla.quantity_received IS NULL THEN plla.amount_received
                ELSE plla.quantity_received
            END quantity_received,
            TO_CHAR(plla.promised_date, 'DD-Mon-YYYY', 'nls_date_language=english') po_promised_date,
            pda.distribution_num po_distribution_num,
            gcc1.segment1 || '-' || gcc1.segment2 || '-' || gcc1.segment3 || '-' || gcc1.segment4 || '-' ||
            gcc1.segment5 || '-' || gcc1.segment6 || '-' || gcc1.segment6 || '-' || gcc1.segment7 || '-' ||
            gcc1.segment8 || '-' || gcc1.segment9 po_charge_account,
            pda.req_distribution_id,
            plla.line_location_id,
            plla.promised_date promise_delivery_date,
            pha.po_header_id ppo_header_id,
            pla.po_line_id ppo_line_id
        FROM
            po_distributions_all pda,
            po_line_locations_all plla,
            po_lines_all pla,
            po_headers_all pha,
            gl_code_combinations gcc1
        WHERE
            pda.line_location_id = plla.line_location_id (+)
            AND plla.po_line_id = pla.po_line_id (+)
            AND pla.po_header_id = pha.po_header_id (+)
            AND pda.code_combination_id = gcc1.code_combination_id (+)
    ) po,
    (
        SELECT
            rt.po_header_id,
            rt.po_line_id,
            rt.po_line_location_id,
            rt.po_distribution_id,
            rt.transaction_id,
            rsh.receipt_num po_receipt_no,
            TO_CHAR(rsh.creation_date, 'DD-Mon-YYYY', 'nls_date_language=english') receipt_date,
            rsh.approval_status receipt_status,
            CASE
                WHEN rsl.quantity_received IS NULL THEN rsl.amount_received
                ELSE rsl.quantity_received
            END re_qty,
            rsh.SHIPMENT_NUM
        FROM
            po_headers_all pha1,
            rcv_shipment_headers rsh,
            rcv_shipment_lines rsl,
            rcv_transactions rt
        WHERE
            pha1.po_header_id = rt.po_header_id
            AND rt.shipment_header_id = rsh.shipment_header_id
            AND rsh.shipment_header_id = rsl.shipment_header_id
            AND rt.shipment_line_id = rsl.shipment_line_id
            AND pha1.po_header_id = rsl.po_header_id
            AND rt.transaction_type = 'RECEIVE'
    ) rcv,
    (
        SELECT
            aila.rcv_transaction_id,
            aila.po_line_id,
            aia.invoice_currency_code,
            aila.po_line_location_id,
            aila.po_distribution_id,
            aila.po_header_id,
            aia.invoice_id,
            aia.invoice_num,
            aila.line_number,
            aia.approval_status inv_status,
            DECODE(apsa.payment_status_flag, 'Y', 'Paid', 'P', 'Partially Paid', 'Unpaid') payment_status,
            (aila.amount) * (NVL(aia.exchange_rate, 1)) invoice_line_amount_lc
        FROM
            ap_invoices_all aia,
            ap_invoice_lines_all aila,
            ap_payment_schedules_all apsa
        WHERE
            aia.invoice_id = aila.invoice_id
            AND aia.cancelled_date IS NULL
            AND aia.invoice_id = apsa.invoice_id (+)
            AND aia.org_id = apsa.org_id (+)
    ) inv
WHERE
    po.line_location_id = rcv.po_line_location_id (+)
    AND rcv.transaction_id = inv.rcv_transaction_id (+)
    AND rcv.po_line_id = inv.po_line_id (+)
    AND rcv.po_line_location_id = inv.po_line_location_id (+)
    AND rcv.po_distribution_id = inv.po_distribution_id (+)
    AND rcv.po_header_id = inv.po_header_id (+)
    AND po.ppo_header_id = inv.po_header_id (+)

Comments

Popular posts from this blog

Query to find Responsibility and Request Group from Concurrent Program

How to Extract User Role Details with SQL in Oracle Fusion