A tax rate change looks like a one-field edit. On a supported instance, doing it sloppily was survivable — if you mangled the setup badly enough, Oracle Support would eventually help you untangle it. On an unsupported instance there is no such backstop, which means the discipline has to live with you. This is the runbook I use: the data model, the procedure, and the verification, table by table.
The regime-to-rate data model
E-Business Tax stores your entire tax configuration in the ZX schema as a strict hierarchy. Every level is date-effective and most tables come in _B (base) and _TL (translated) pairs:
| Level | Table | One row means | Example |
|---|---|---|---|
| Tax Regime | ZX_REGIMES_B | A system of regulations administered together | US-SALES-TAX, GB-VAT |
| Tax | ZX_TAXES_B | One tax within a regime, per configuration owner | STATE, COUNTY, CITY; or GST, HST |
| Tax Status | ZX_STATUS_B | The taxable nature bucket a rate belongs to | STANDARD, REDUCED, ZERO, EXEMPT |
| Tax Jurisdiction | ZX_JURISDICTIONS_B | The geographic incidence of a tax | Georgia state; Fulton County; City of Atlanta |
| Tax Rate | ZX_RATES_B | One percentage, for one status or jurisdiction, for one effective period | Fulton County 3% effective 2019-04-01 → 2026-03-31 |
Around that spine sit the tables that decide when a tax applies and to whom:
| Concern | Tables | Notes |
|---|---|---|
| Tax rules | ZX_RULES_B | Rule per determination step (applicability, place of supply, registration, rate determination…) |
| Determining factors | ZX_DET_FACTOR_TEMPL_B and related | The “IF” vocabulary: geography, party registration, fiscal classifications, transaction attributes |
| Conditions | ZX_CONDITION_GROUPS_B, ZX_CONDITIONS | The concrete values a rule tests (MOS note 1111553.1 walks the setup) |
| Party profiles | ZX_PARTY_TAX_PROFILE | Tax registrations and attributes for legal entities, operating units, suppliers, customers |
| Exemptions | ZX_EXEMPTIONS | Customer/product exemption certificates with their own effective dates |
| Migrated 11i codes | Lookups ZX_INPUT_CLASSIFICATIONS, ZX_OUTPUT_CLASSIFICATIONS | Where your upgraded AP/AR tax codes live if you came from 11i |
| Geography | HZ_GEOGRAPHIES (TCA) | Master geography — for the US: State → County → City → Postal Code. Jurisdictions can be mass-created from it, and it accepts exactly one master source; you cannot mix providers |
| Accounting | ZX_ACCOUNTS | Which liability/recovery accounts each rate posts to |
And the runtime side — the tables your update ultimately has to prove itself in:
| Table | What lands there |
|---|---|
ZX_LINES | Every calculated tax line, per transaction |
ZX_LINES_DET_FACTORS | The snapshot of determining factors the engine used for each transaction line (this is where PO and requisition tax context lives) |
ZX_REP_EXTRACT (via ZX_REP_EXTRACT_V) | The reporting extract behind the Financial Tax Register |
JG_ZZ_VAT_REP_ENTITIES, JG_ZZ_VAT_TRX_DETAILS | EMEA VAT reporting entities (one per legal entity + regime + registration number) and their transaction details |
The golden rule: end-date and create, never update
ZX_RATES_B is deliberately built so that one rate percentage for one period is one row. When Fulton County moves from 3% to 3.5% on April 1, the correct change is:
- The existing rate row gets
EFFECTIVE_TO = 31-MAR-2026. - A new rate row is created: same regime, tax, status, jurisdiction —
PERCENTAGE_RATE = 3.5,EFFECTIVE_FROM = 01-APR-2026, effective-to open.
Never edit the percentage on the live row. Three reasons, in increasing order of severity: transactions dated in the old period must continue to calculate and recalculate at the old rate (credit memos against March invoices care deeply about this); your audit trail is the row history itself; and the engine caches and validates against effective ranges, so in-place edits are where the classic date-overlap errors come from (“Enter a date range that is within the date range of this component” — the subject of its own MOS note, 2184159.1, and of two more on repairing rates that were end-dated or disabled wrongly: 2353608.1 and 2109672.1). If you have inherited a setup where somebody did overwrite percentages in place, treat that as a finding — historical recalculations may already be wrong.
The same end-date-and-create discipline applies one level up. A new tax (say, a state adopts a retail delivery fee, which is legally a fee per order, not a rate on a line) is a new ZX_TAXES_B entry with its own statuses and rates — not a contortion of an existing rate. A boundary change (Texas publishes city annexation lists quarterly) is a geography and jurisdiction problem: the address moves between jurisdictions, so the fix is in TCA geography and ZX_JURISDICTIONS_B, and no amount of rate editing will produce the right answer.
The procedure
All of this is done from the Tax Managers responsibility. On 12.2 the Tax Configuration Workbook (an Excel upload with nine worksheets) can carry bulk loads; on 12.1 you are in the HTML UI — which is fine, because a quarterly change set is rarely more than a few dozen rows.
In a clone first. Always. The sequence:
- Diff sources against setup. Take the quarter’s rate/boundary files and DOR bulletins (see the companion article on sources and schedules) and produce the change list: rate changes, new jurisdictions, boundary moves, base changes that need a rule edit rather than a rate edit.
- Apply in the clone. Tax Managers → Tax Configuration → Tax Rates: query the rate, set the end date, create the successor row effective the first of the quarter. New jurisdictions: create from the geography hierarchy so the jurisdiction ties to TCA geography rather than free text.
- Verify the setup rows. A query like this confirms what the UI did:
SELECT r.tax_regime_code, r.tax, r.tax_status_code,
j.tax_jurisdiction_code, r.percentage_rate,
r.effective_from, r.effective_to, r.active_flag
FROM zx_rates_b r
LEFT JOIN zx_jurisdictions_b j
ON r.tax_jurisdiction_code = j.tax_jurisdiction_code
AND r.tax_regime_code = j.tax_regime_code
WHERE r.tax_regime_code = 'US-SALES-TAX'
AND r.tax = 'COUNTY'
AND j.tax_jurisdiction_code LIKE '%FULTON%'
ORDER BY r.effective_from;
You want to see contiguous, non-overlapping effective ranges: old row closed March 31, new row open from April 1, no gap, no overlap.
- Prove it on transactions. Enter one transaction per changed jurisdiction, dated in the new period, in each affected module — an AP invoice, an AR invoice, a PO if procurement matters to you. Then check the engine’s answer at the source:
SELECT trx_id, tax, tax_rate, tax_amt, tax_jurisdiction_code
FROM zx_lines
WHERE application_id = 222 -- 222 AR, 200 AP, 201 PO
AND trx_id = :your_test_transaction
ORDER BY tax_line_number;
And one transaction dated in the old period, to prove history still calculates at the old rate.
- Run the Financial Tax Register over the test window and eyeball the changed jurisdictions. If EMEA VAT reporting applies, run the relevant VAT register against the reporting entity too — reporting reads the extract, not your good intentions.
- Migrate to production by repeating the UI steps (or workbook load on 12.2) inside a change ticket with the source bulletin attached. Then spot-check the first live transactions of the new quarter — the first Tuesday of a new quarter is when a missed jurisdiction announces itself.
What not to do
Do not update ZX tables with SQL. I write data fixes against Oracle Financials for a living, and I still say it: tax setup is the wrong place for direct DML. (What you use instead — the screen, program, or workbook for every object — is covered object-by-object in the supported update paths article.) The UI maintains a web of consistency — rate-to-status links, jurisdiction geography references, _TL translations, flexfield defaults, cache invalidation — that a direct update silently bypasses. The engine may keep working while the reporting extract, or a credit memo eight months from now, quietly disagrees. Data fixes belong on the transaction side (repairing bad ZX_LINES the engine already produced — a different article’s subject), not the configuration side.
Do not “fix” a wrong rate by deleting anything. Setup rows that have been used by transactions are load-bearing. The correction for a wrongly created rate is an end date; the correction for a wrongly end-dated rate is documented in the MOS notes above and is fussier than you expect — which is the point of doing all of this in a clone first.
Do not let the geography go stale. Rates get all the attention, but three years of unapplied annexations and new incorporations means addresses silently calculating in the wrong jurisdiction at the correct rate — the hardest kind of wrong to spot. Boundary files are published on the same quarterly schedule as rates; process them with the same discipline.
The uncomfortable part
Everything above assumes the engine itself behaves. On a supported release, when it doesn’t — a recovery rate that misapplies, a determining-factor bug, a reporting extract that drops lines — you file an SR and eventually get a patch. On 12.1 since January 2022, no patch is coming. The remedy is a diagnosed, tested, custom fix, which is precisely the third-party-support skill set: reproduce it, find it in the ZX code or data, correct it, and stand behind the correction. That, plus the quarterly discipline above, is what “keeping E-Business Tax current without Oracle” actually consists of.
Inherited a tax setup with overlapping rates, free-text jurisdictions, or in-place edits in its history? Describe what you’re seeing — I’ll tell you what a cleanup costs before you commit to anything.