Saltar al contenido

← Inicio

Oracle Cloud: query para identificar Tax Rate Variance (TRV) originada en FDC

Se tem preguiça de ler, clique aqui pro TL;DR

Este artículo complementa Oracle Cloud: Tax Rate Variance (TRV) en FDC y su impacto en el costo del artículo.

Después de entender cómo puede generarse el TRV, la necesidad práctica es identificar qué invoices de Payables produjeron realmente una línea contable clasificada como Tax Rate Variance.

El modelo de BI Publisher utiliza cuatro parámetros:

Business Unit
Ledger
Fecha inicial
Fecha final

y devuelve, cuando esté disponible:

Invoice
FDC
Purchase Order
Importe total de la invoice
Importe total del FDC
Importe contabilizado como TRV
Cuenta contable
Estado de Payables

Oracle documenta ACCOUNTING_CLASS_CODE en XLA_AE_LINES como la clasificación de una línea de Subledger Accounting, y TRV se utiliza para Tax Rate Variance. Oracle — XLA_AE_LINES Oracle — Event Cost Source Type

Restricción para invoices de FDC

En el escenario analizado, las invoices creadas por Fiscal Document Capture llegan a Payables con:

SOURCE = ORA_CMF

Por eso la query aplica explícitamente:

AND ai.source = 'ORA_CMF'

Esto evita mezclar invoices creadas por otras fuentes de Payables.

Oracle documenta SOURCE como el feeder system de la invoice. En el flujo CMF, CMF_AP_INVOICES_GT también expone una columna SOURCE. Oracle — CMF_AP_INVOICES_GT

Query

WITH bu AS
(
    SELECT bu_id,
           bu_name,
           TO_NUMBER(primary_ledger_id) primary_ledger_id
      FROM fun_all_business_units_v
     WHERE bu_name = :P_BU_NAME
),
ledger AS
(
    SELECT DISTINCT
           gl.ledger_id,
           gl.name ledger_name
      FROM gl_ledgers gl
      JOIN
           (
               SELECT primary_ledger_id ledger_id
                 FROM bu

               UNION

               SELECT glr.target_ledger_id ledger_id
                 FROM gl_ledger_relationships glr
                 JOIN bu
                   ON bu.primary_ledger_id = glr.primary_ledger_id
           ) bu_ledgers
        ON bu_ledgers.ledger_id = gl.ledger_id
     WHERE gl.name = :P_LEDGER_NAME
),
po_base AS
(
    SELECT DISTINCT
           ail.invoice_id,
           poh.segment1 po_number
      FROM ap_invoice_lines_all ail
      JOIN po_headers_all poh
        ON poh.po_header_id = ail.po_header_id
     WHERE ail.po_header_id IS NOT NULL
),
po_info AS
(
    SELECT invoice_id,
           LISTAGG(po_number, ', ')
             WITHIN GROUP (ORDER BY po_number) po_number
      FROM po_base
     GROUP BY invoice_id
),
fdc_base AS
(
    /* Preferred relationship when the FDC header already stores the AP invoice. */
    SELECT DISTINCT
           fdh.invoice_id,
           fdh.document_header_id,
           fdh.document_number,
           fdh.access_key_number,
           fdh.total_amount
      FROM cmf_fiscal_doc_headers fdh
     WHERE fdh.invoice_id IS NOT NULL

    UNION

    /* Fallback through the source transaction carried to AP invoice lines. */
    SELECT DISTINCT
           ail.invoice_id,
           fdh.document_header_id,
           fdh.document_number,
           fdh.access_key_number,
           fdh.total_amount
      FROM ap_invoice_lines_all ail
      JOIN cmf_fiscal_doc_headers fdh
        ON fdh.document_header_id = ail.source_trx_id
     WHERE ail.source_trx_id IS NOT NULL
),
fdc_info AS
(
    SELECT invoice_id,
           LISTAGG(document_number, ', ')
             WITHIN GROUP (ORDER BY document_number) fdc_number,
           LISTAGG(access_key_number, ', ')
             WITHIN GROUP (ORDER BY access_key_number) fdc_access_key,
           SUM(total_amount) fdc_total_amount
      FROM fdc_base
     GROUP BY invoice_id
),
trv_accounting AS
(
    SELECT xah.event_id,
           xah.accounting_date,
           xah.period_name,
           xah.accounting_entry_status_code,
           xah.gl_transfer_status_code,
           xal.ae_line_num,
           xal.code_combination_id,
           xal.accounting_class_code,
           xal.accounted_dr,
           xal.accounted_cr
      FROM xla_ae_headers xah
      JOIN ledger
        ON ledger.ledger_id = xah.ledger_id
      JOIN xla_ae_lines xal
        ON xal.application_id = xah.application_id
       AND xal.ae_header_id   = xah.ae_header_id
     WHERE xah.application_id = 200
       AND xal.accounting_class_code = 'TRV'
       AND xah.accounting_date >= :P_DATE_FROM
       AND xah.accounting_date <  :P_DATE_TO + 1
)
SELECT TO_CHAR(trv.accounting_date, 'DD/MM/RRRR') accounting_date,
       trv.period_name,
       ai.invoice_num,
       TO_CHAR(ai.invoice_date, 'DD/MM/RRRR') invoice_date,
       ai.invoice_currency_code currency,
       ai.invoice_amount ap_invoice_amount,
       ap_invoices_utility_pkg.get_approval_status
       (
           ai.invoice_id,
           ai.invoice_amount,
           ai.payment_status_flag,
           ai.invoice_type_lookup_code
       ) ap_status,
       ai.wfapproval_status ap_workflow_status,
       ai.payment_status_flag ap_payment_status,
       po.po_number,
       fdc.fdc_number,
       fdc.fdc_access_key,
       fdc.fdc_total_amount,
       NVL(trv.accounted_dr, 0)
         - NVL(trv.accounted_cr, 0) trv_amount,
       gcc.segment1 || '.' ||
       gcc.segment2 || '.' ||
       gcc.segment3 || '.' ||
       gcc.segment4 || '.' ||
       gcc.segment5 || '.' ||
       gcc.segment6 || '.' ||
       gcc.segment7 account_combination,
       trv.accounting_entry_status_code sla_status,
       trv.gl_transfer_status_code gl_transfer_status
  FROM trv_accounting trv
  JOIN ap_invoices_all ai
    ON EXISTS
       (
           SELECT 1
             FROM ap_invoice_distributions_all aid
            WHERE aid.invoice_id = ai.invoice_id
              AND aid.accounting_event_id = trv.event_id
       )
  JOIN bu
    ON bu.bu_id = ai.org_id
  JOIN gl_code_combinations gcc
    ON gcc.code_combination_id = trv.code_combination_id
  LEFT JOIN po_info po
    ON po.invoice_id = ai.invoice_id
  LEFT JOIN fdc_info fdc
    ON fdc.invoice_id = ai.invoice_id
 WHERE ai.source = 'ORA_CMF'
 ORDER BY trv.accounting_date,
          ai.invoice_num,
          trv.ae_line_num

Todas las fechas mostradas en el resultado se formatean como DD/MM/RRRR con TO_CHAR. Los parámetros de BI Publisher siguen siendo de tipo Date.

Parámetros de BI Publisher

P_BU_NAME
P_LEDGER_NAME
P_DATE_FROM
P_DATE_TO

LOV de Business Unit:

SELECT bu_name display_value,
       bu_name return_value
  FROM fun_all_business_units_v
 WHERE status = 'A'
 ORDER BY bu_name

Para P_LEDGER_NAME, usaría una LOV dependiente de la Business Unit. Después de seleccionar la BU, el usuario puede elegir entre sus ledgers relacionados, por ejemplo un ledger Local o Global:

SELECT DISTINCT
       gl.name display_value,
       gl.name return_value
  FROM gl_ledgers gl
  JOIN
       (
           SELECT TO_NUMBER(fbu.primary_ledger_id) ledger_id
             FROM fun_all_business_units_v fbu
            WHERE fbu.bu_name = :P_BU_NAME

           UNION

           SELECT glr.target_ledger_id ledger_id
             FROM fun_all_business_units_v fbu
             JOIN gl_ledger_relationships glr
               ON glr.primary_ledger_id = TO_NUMBER(fbu.primary_ledger_id)
            WHERE fbu.bu_name = :P_BU_NAME
       ) bu_ledgers
    ON bu_ledgers.ledger_id = gl.ledger_id
 ORDER BY gl.name

La Business Unit expone PRIMARY_LEDGER_ID mediante FUN_ALL_BUSINESS_UNITS_V, pero esa view obtiene el valor de ORG_INFORMATION3. Para evitar ORA-01790 al combinarlo con los IDs numéricos de GL_LEDGER_RELATIONSHIPS, la query convierte explícitamente el campo con TO_NUMBER(PRIMARY_LEDGER_ID). GL_LEDGER_RELATIONSHIPS mantiene las relaciones entre ledgers primarios, secundarios y de reporting. El valor seleccionado se utiliza para filtrar XLA_AE_HEADERS.LEDGER_ID. Oracle — FUN_ALL_BUSINESS_UNITS_V Oracle — GL_LEDGER_RELATIONSHIPS Oracle — XLA_AE_HEADERS

Cómo se encuentra la invoice

AP_INVOICE_DISTRIBUTIONS_ALL.ACCOUNTING_EVENT_ID identifica el evento contable que contabilizó la distribución de la invoice. Oracle — AP_INVOICE_DISTRIBUTIONS_ALL

La query relaciona:

aid.accounting_event_id = trv.event_id

y conserva únicamente las líneas de SLA donde:

xal.accounting_class_code = 'TRV'

Así el informe no depende de una cuenta fija.

Relación con FDC

CMF_FISCAL_DOC_HEADERS contiene el INVOICE_ID de Payables, además de DOCUMENT_NUMBER, ACCESS_KEY_NUMBER y TOTAL_AMOUNT. Oracle — CMF_FISCAL_DOC_HEADERS

La relación más simple utiliza el INVOICE_ID almacenado en el encabezado de FDC. Sin embargo, ese campo es nullable y puede no estar informado en todos los registros. Por eso la query utiliza dos caminos: el INVOICE_ID de FDC y, como fallback, AP_INVOICE_LINES_ALL.SOURCE_TRX_ID relacionado con el DOCUMENT_HEADER_ID de FDC.

Esto evita perder el número del documento fiscal, la clave de acceso y el importe total cuando el vínculo directo con la invoice no está disponible.

Purchase Order

Cuando existe PO, AP_INVOICE_LINES_ALL.PO_HEADER_ID puede relacionarse con PO_HEADERS_ALL.PO_HEADER_ID. Oracle documenta esa foreign key, y SEGMENT1 representa el número del documento de compra. Oracle — AP_INVOICE_LINES_ALL Oracle — PO_HEADERS_ALL

Se utiliza LISTAGG porque una invoice puede contener líneas relacionadas con más de un PO.

Estado de Payables

Además de AP_INVOICES_UTILITY_PKG.GET_APPROVAL_STATUS, devuelto como AP_STATUS, la salida mantiene WFAPPROVAL_STATUS, PAYMENT_STATUS_FLAG, SLA_STATUS y GL_TRANSFER_STATUS.

Importe y cuenta del TRV

El importe contabilizado es:

NVL(accounted_dr, 0) - NVL(accounted_cr, 0)

XLA_AE_LINES contiene la combinación contable y los importes contabilizados de débito/crédito utilizados por SLA. Oracle — XLA_AE_LINES

El ejemplo de ACCOUNT_COMBINATION considera siete segmentos del Chart of Accounts y puede ajustarse si se utiliza otra estructura.

Resultado esperado

El informe responde:

¿Qué invoices originadas en FDC
generaron Tax Rate Variance
para una BU y un período determinados?

La principal ventaja es identificar primero la naturaleza contable de la línea y después mostrar la cuenta derivada por SLA.