efrisapi.com

Error codes / 1343

EFRIS error 1343 — summary.netAmount does not equal gross minus tax

URA recomputed your summary block and found that summary.netAmount is not summary.grossAmount − summary.taxAmount. Derive netAmount by subtraction, from the converted taxDetails you are actually transmitting — not from a net your own system computed and rounded earlier.

netAmount = grossAmount - taxAmount

Both operands come from the transmitted taxDetails. Never copy netAmount from a caller, an order model, or a per-line sum.

What URA returns

The rejection arrives in the response envelope with the code in returnStateInfo.returnCode:

{
  "returnStateInfo": {
    "returnCode": "1343",
    "returnMessage": "..."
  }
}

URA rewords returnMessage between releases and some messages come back in mixed language, so branch on returnCode and never on the message text:

from efris import EfrisError

try:
    client.upload_invoice(document)
except EfrisError as exc:
    if exc.code == "1343":
        ...   # rebuild summary from taxDetails, then resubmit
    raise

Log the whole decrypted payload when a document fails. URA frequently puts the useful detail in a nested field rather than in returnMessage, and a batch can be partly accepted — the envelope says "00" while individual lines were rejected, surfaced as EfrisValidationError.failures.

What it actually means

The summary block is not header data URA stores on trust. URA recomputes it from the arrays you sent and rejects any disagreement. 1343 is the identity check on that recomputation.

Almost every occurrence has the same shape: grossAmount and taxAmount were derived from taxDetails, but netAmount came from somewhere else — an ERP order total, a per-line net summed at a different precision, or a value the API caller supplied. The two paths agree to within a fraction of a shilling, and URA allows no tolerance. Note that the check applies to the converted taxDetails: if your pipeline normalises tax rates, maps snake_case keys to camelCase, or reformats amounts to two decimal places, the numbers URA validates are the numbers produced after that step, not the ones you computed before it.

The fix

Build all three summary amounts in one function, from the taxDetails list that is about to be serialised into the request body. Call it last, after every conversion, immediately before signing.

def summary_amounts(tax_details):
    """The three summary amounts, derived from the transmitted taxDetails.

    taxAmount  - sum of every group, INCLUDING excise (category "05").
    grossAmount- sum of every group EXCEPT excise; excise is already embedded
                 in the standard-rate group's gross.
    netAmount  - derived, never supplied.
    """
    tax_amount = sum(float(td["taxAmount"]) for td in tax_details)
    gross_amount = sum(
        float(td["grossAmount"])
        for td in tax_details
        if str(td.get("taxCategoryCode", "")) != "05"
    )
    net_amount = gross_amount - tax_amount
    return {
        "netAmount": f"{net_amount:.2f}",
        "taxAmount": f"{tax_amount:.2f}",
        "grossAmount": f"{gross_amount:.2f}",
    }


document["summary"].update(summary_amounts(document["taxDetails"]))

Amounts are transmitted as JSON strings, so format them to a fixed two decimal places rather than letting a float render as 59000.000000001.

On an invoice carrying excise duty, summary.netAmount is not the sum of the taxDetails net amounts. It is derived from a gross that excludes tax category 05 and a tax that includes it. Summing the per-group nets gives a different, wrong figure. See 1344 and 1345.

Why this happens

EFRIS treats taxDetails as the authoritative statement of the document and summary as a checksum over it. Anything URA can recompute, URA does recompute: the amounts here, and the line count in 1304. The purpose is that a fiscal document cannot claim one tax position in its detail and another in its header.

That framing gives the general rule for the whole summary block. There should be exactly one place in your code that produces summary, it should take the final goodsDetails and taxDetails as its only inputs, and it should run after every transformation those arrays undergo. A summary assembled earlier in the pipeline — or partly supplied by the caller — will eventually disagree with what is transmitted, and the failure appears as 1343, 1344, 1345 or 1304 depending on which field drifted.

Related errors

The full list, with the fix for each, is at /docs/errors/.

If you would rather not maintain this

The EFRIS API Kit builds the summary from the converted taxDetails at submission time, on invoices and credit notes alike, so the identity above always holds. The rule is correct whether or not you use it.