Engineering Blog / Inside TaxBridge

The Three-Body Problem of EFRIS Units: Why Your Export Invoices Keep Failing

Published December 11, 2025
Read Time 6 min read

A practical guide to piece/scaling/customs units, how they collide, and the normalization approach TaxBridge uses.

We recently sat in a two-hour integration meeting with a major ERP vendor and a commodity exporter. There were five engineers on the call. The agenda? How to send an invoice for a bag of Chia seeds.

At the 90-minute mark, the developers had to break for a “side meeting” to ask their boss if they were allowed to create a new product code in their database.

Why? Because they had run into the EFRIS Three-Body Problem.

If you’re building a system-to-system integration for a Ugandan taxpayer—especially one that exports goods—you will eventually hit Error 2197 or Error 677. This article explains why those errors happen and the “Scaling Factor” architecture you need to solve it.

The Conflict: Sales vs. Stock vs. Customs

The root cause is simple: a single invoice must satisfy three different masters at once.

  1. The Commercial Master (The Invoice) — What your salespeople enter: “I sold 10 Bags”. Unit: BAG.
  2. The Inventory Master (The Stock) — How your warehouse tracks quantity: “Deduct 500 Kilograms”. Unit: KGM.
  3. The Customs Master (The Border) — What customs use for declarations: “We exported 0.5 Tonnes”. Unit: TNE.

Most integrations treat units naively with a 1:1 mapping and send qty: 10, unit: 'BAG' to URA.

  • Result: URA rejects because it doesn’t know how much a BAG weighs.
  • Workaround: Developers change the unit to KGM and send qty: 10.
  • Consequence: Accounting and stock are incorrect — you reported 10 KGs instead of 10 Bags.

The Solution: The “Physics” of the Good

To pass validation, the invoice payload must define the relationship between the three systems. EFRIS exposes fields in the T109 payload that let you describe the “physics” of the product:

  • measureUnit — The sales unit (e.g., BAG).
  • pieceMeasureUnit — The stock base unit (e.g., KGM).
  • pieceScaledValue — The scaling factor describing how many piece units exist in one sales unit.

The Math

If 1 Bag = 50 KG, and you sell 10 Bags:

{
  "goodsDetails": [
    {
      "item": "Portland Cement 50kg",
      "qty": "10",               // Sales Qty
      "unitOfMeasure": "BAG",    // Sales Unit
      "unitPrice": "30000",      // Price per BAG

      // The Physics (required by URA)
      "pieceMeasureUnit": "KGM", // Stock Unit
      "pieceScaledValue": "50",  // 1 BAG = 50 KGM
      "pieceQty": "500"          // Calculated: 10 * 50
    }
  ]
}

If you omit pieceScaledValue, EFRIS assumes it’s 1. 10 Bags × 1 = 10 KGM, and you’ll be in trouble.

This mismatch generates a reconciliation error (or Error 677) when stock or customs quantities don’t add up.

The Edge Case: Exports & “XJ” Codes

Exports add another wrinkle. URA requires standardized package codes (e.g., XJ for multi-wall bag) and often expects a customs unit like TNE (tonne) rather than KGM.

Therefore the same sale might be represented three different ways — Sales, Stock, and Customs — as in this example:

{
  "goodsDetails": [
    {
      "item": "Organic Chia",
      "qty": "100",              // Commercial: 100 Bags
      "unitOfMeasure": "XJ",     // ISO code for Bag

      "pieceMeasureUnit": "KGM", // Stock: Kilograms
      "pieceScaledValue": "25",  // 1 Bag = 25 KG
      "pieceQty": "2500",        // Stock deduction: 100 * 25 = 2500 KG

      "customsUnit": "TNE",      // Customs: Tonnes
      "customsQuantity": "2.5"   // 2500 KG / 1000 = 2.5 Tonnes
    }
  ]
}

Stop Hardcoding This in Your ERP

We saw this problem in that 2-hour meeting: an ERP vendor forced the salesperson to enter KGM instead of BAG. That breaks the product experience.

Do not make end-users think in Tonnes. Sales teams think in Bags. Warehouses think in KGs. Customs think in Tonnes.

One-time Mapping, Zero Headache

Instead of forcing your ERP to shift its UX, map the product once in a registry and let the integration layer handle the math.

// Product mapping in the Smart Registry
{
  "externalRef": "CHIA-25KG",
  "uraCode": "204024...",
  "scalingFactor": 25,
  "exportUnit": "TNE"
}

Runtime flow:

  1. Your ERP sends: POST /tax_intents with { "items": [{ "externalRef": "CHIA-25KG", "qty": 100 }] }.
  2. TaxBridge looks up the scalingFactor and calculates: pieceQty = qty × scalingFactor.
  3. TaxBridge injects the XJ package code, KGM stock unit, TNE customs unit, and signs the payload.

The result: one translated invoice, valid for Sales, Stock, and Customs. The record is legal and fiscalized; the truck leaves the warehouse.

Why This Matters

Unit mismatches are not a feature request — they are a core tax compliance problem.

If you’re exporting, this is the difference between a delayed container at port (lots of paperwork and fines) and a smooth reconciliation that keeps your warehouse and accounting teams in sync.

The TaxBridge Approach: Smart Normalization

TaxBridge is a translation layer. We normalise the product once and perform the math during runtime. That removes the need to change your POS and prevents manual mistakes.

If you’re struggling with unit mismatches, TaxBridge handles the math so you can focus on building your products — not debugging URA errors.

Get your API Keys


By the TaxBridge Engineering Team