ETA Document Flow
Complete lifecycle, statuses, actions, and enablement rules for E-Invoices, E-Receipts, and E-Received Invoices.
ETA Document Flow
Egyptian Tax Authority (ETA) documents share one policy: EtaActionPolicy in the shared library. The same rules disable Actions in Blazor (EtaActionGate / EtaActionMenuItem) and reject illegal calls on the API.
This guide documents implemented behavior for:
- E-Invoices — sent invoices (
Direction = Sent,Kind = Invoice) - E-Received Invoices — inbound supplier invoices (
Direction = Received,Kind = Invoice) - E-Receipts — sent receipts (
Kind = Receipt; use return, not cancel)
Parent hub: Target Document Flow.
- Draft / Validated / Queued
- Submitted
- Valid
- Invalid / Rejected
- Cancelled / Returned
#Status catalog
Statuses are stored as strings on the document and mapped to EtaDocumentStatus.
| Status | Invoices | Receipts | Meaning |
|---|---|---|---|
Draft |
Yes | Yes | Editable local draft |
Validated |
Yes | Yes | Passed local/pre-ETA validation |
Queued |
— | Yes | Accepted offline; waiting to submit |
Submitted |
Yes | Yes | Sent; awaiting authority result |
Valid |
Yes | Yes | Accepted by ETA |
Invalid |
Yes | Yes | Refused by ETA; fix and resubmit |
Rejected |
Yes | Yes* | Counterparty refused (invoice-centric) |
Cancelled |
Yes | — | Issuer cancelled at ETA |
Returned |
— | Yes | Fully reversed by return receipt |
* Receipts do not use recipient Reject / Decline Cancellation; reverse with Create Return Or Cancel Receipt.
#Lifecycle overview
Core path — prepare, submit, ETA result
flowchart TB
subgraph Prepare["1 - Prepare"]
direction TB
Draft[Draft]
Validated[Validated]
Queued[Queued - receipts]
Draft -->|Validate| Validated
end
subgraph Authority["2 - Authority"]
Submitted[Submitted]
end
subgraph Result["3 - Result"]
direction TB
Valid[Valid]
Invalid[Invalid]
end
Start((Create)) --> Draft
Draft -->|Submit| Submitted
Validated -->|Submit| Submitted
Queued -->|Submit| Submitted
Submitted -->|ETA accept| Valid
Submitted -->|ETA refuse| Invalid
Draft -.->|Submit refused| Invalid
Validated -.->|Submit refused| Invalid
Invalid -->|Edit| Draft
Invalid -->|Resubmit| Submitted
class Start egkStart
class Draft,Validated,Queued egkDraft
class Submitted egkActive
class Valid egkSuccess
class Invalid egkError
Terminal paths from Valid — cancel, reject, or return
flowchart TB Valid[Valid] Valid -->|Cancel on ETA| Cancelled[Cancelled] Valid -->|Reject invoice| Rejected[Rejected] Valid -->|Create return| Returned[Returned] Cancelled --> DoneNode((Done)) Rejected --> DoneNode Returned --> DoneNode class Valid egkSuccess class Cancelled,Rejected,Returned egkTerminal class DoneNode egkStart
#Direction split (invoices)
ETA gives each side one reversal verb. Receipts use Create Return Or Cancel Receipt, not Cancel On Eta.
Issuer vs recipient reversal actions
flowchart TB
subgraph Issuer["Issuer - E-Invoices"]
direction TB
S1[Valid] -->|Cancel on ETA| S2[Cancelled]
S1 -->|Decline rejection| S3[Reinstate / clear pending]
end
subgraph Recipient["Recipient - E-Received Invoices"]
direction TB
R1[Valid] -->|Reject invoice| R2[Rejected]
R1 -->|Decline cancellation| R3[Reinstate / clear pending]
end
class S1,R1 egkSuccess
class S2,R2 egkTerminal
class S3,R3 egkActive
| Verb (UI label) | Who | Policy action |
|---|---|---|
| Cancel On Eta | Issuer | Cancel |
| Decline Rejection | Issuer | DeclineRejection |
| Reject Invoice | Recipient | Reject |
| Decline Cancellation | Recipient | DeclineCancellation |
| Create Return Or Cancel Receipt | Receipt issuer | IssueReturn |
| Create Credit Note | Invoice issuer | IssueCreditNote |
#Action availability matrix
Legend: Y = allowed when other flags permit · — = never for this kind/direction · C = conditional (see notes)
Sent e-invoice matrix
| Action (UI) | Draft | Validated | Submitted | Valid | Invalid | Rejected | Cancelled |
|---|---|---|---|---|---|---|---|
| Edit / Delete / Line Items | Y | Y | — | — | Y | — | — |
| Validate | Y | Y | — | — | Y | — | — |
| Submit | Y | Y | — | — | Y | — | — |
| Sync Status | C¹ | C¹ | C¹ | C¹ | C¹ | C¹ | C¹ |
| Cancel On Eta | — | — | — | C² | — | — | — |
| Decline Rejection | — | — | — | C³ | — | C³ | C³ |
| Create Credit Note | — | — | — | C⁴ | — | — | — |
| Duplicate | Y | Y | Y | Y | Y | Y | Y |
| Eta Pdf | C¹ | C¹ | C¹ | C¹ | C¹ | C¹ | C¹ |
¹ Requires ETA document UUID (HasEtaUuid).
² Valid only; blocked if pending cancellation, pending rejection, or reversal window closed.
³ Requires pending rejection or status Rejected/Cancelled per decline rules; blocked if already declined, no rejection, wrong direction, or decline window closed while still Valid.
⁴ Valid only and not fully credited; no cancellation deadline (credit note remains available after the cancel window).
Received e-invoice & e-receipt matrices
#Received e-invoice
| Action (UI) | Valid | Other statuses |
|---|---|---|
| Reject Invoice | C⁵ | Denied (not submitted / wrong status / already reversed) |
| Decline Cancellation | C⁶ | See decline rules |
| Sync / Eta Pdf | C¹ | C¹ |
| Submit / Cancel / Credit Note | — | Sender-only or N/A |
⁵ Valid only; within rejection window; no pending rejection.
⁶ Pending cancellation or status Cancelled; receiver only; one decline; window rules as in policy.
#E-receipt
| Action (UI) | Draft / Validated / Queued / Invalid | Submitted | Valid | Returned |
|---|---|---|---|---|
| Edit / Delete | Y | — | — | — |
| Validate / Submit | Y | — | — | — |
| Create Return Or Cancel Receipt | — | — | C⁷ | — |
| Cancel On Eta | — | — | — | — (receipts use return) |
| Duplicate | Y | Y | Y | Y |
⁷ Valid and not fully returned.
#Decision trees
#When is Cancel On Eta enabled?
Cancel On Eta — policy gates
flowchart TB
A[Cancel On Eta] --> B{Kind = Invoice?}
B -->|No| X1[Denied - use return]
B -->|Yes| C{Direction = Sent?}
C -->|No| X2[Denied - sender only]
C -->|Yes| D{Pending cancellation?}
D -->|Yes| X3[Denied - already requested]
D -->|No| E{Pending rejection?}
E -->|Yes| X4[Denied - rejection pending]
E -->|No| F{Status = Valid?}
F -->|No| X5[Denied - not Valid]
F -->|Yes| G{Reversal window closed?}
G -->|Yes| X6[Denied - window closed]
G -->|No| OK[Allowed]
class A egkActive
class B,C,D,E,F,G egkDecision
class OK egkSuccess
class X1,X2,X3,X4,X5,X6 egkError
#When is Decline Rejection enabled?
Decline Rejection — policy gates
flowchart TB
A[Decline Rejection] --> B{Invoice + Sent?}
B -->|No| X1[Denied - wrong kind]
B -->|Yes| C{Has ETA UUID?}
C -->|No| X2[Denied - not submitted]
C -->|Yes| D{Already declined?}
D -->|Yes| X3[Denied - already declined]
D -->|No| E{Pending rejection or Rejected?}
E -->|No| X4[Denied - no rejection]
E -->|Yes| F{Status}
F -->|Valid| G{Decline window closed?}
G -->|Yes| X5[Denied - window closed]
G -->|No| OK[Allowed]
F -->|Rejected or Cancelled| OK
F -->|Other| X4
class A egkActive
class B,C,D,E,F,G egkDecision
class OK egkSuccess
class X1,X2,X3,X4,X5 egkError
#After-action UI behavior (ETA screens)
| Step | Behavior |
|---|---|
| 1 | Action runs through API (/{id}/validate, /submit, /cancel, /decline-rejection, /reject, /decline-cancellation, /return, /sync-status, …) |
| 2 | ETA actions stay disabled for the round trip and the refresh that follows, so nothing can be launched against a row already known to be out of date |
| 3 | The row and the status counters above it are re-read from the server on every outcome — accepted, refused, or failed |
| 4 | If composer/details open: refetch invoice/receipt/received invoice by id |
| 5 | Gate(row, action) / DeclineRejectionGate / DeclineCancellationGate re-run on the new StatusId, UUID, and ETA date flags |
| 6 | A multi-row selection is re-pointed at the reloaded rows, so bulk buttons gate on the new statuses; rows the reload no longer returns drop out of the selection |
| 7 | Disabled items remain visible with localized denial tooltips |
A refused action refreshes the same as an accepted one. A refusal usually means the document had already moved on — cancelled by someone else, or past its window — which is exactly the case where the row on screen is the stale thing that offered the action. A refused submission also still writes to the document: the reason ETA gave, and the attempt in the audit trail. So the screen after a refusal shows the authority's current answer rather than the state you clicked from, and no manual refresh is needed to find out what is still possible.
Validate is included in this, despite the read-only name: a draft that passes validation is promoted to Validated and saved, which changes what the Actions menu should offer.
EntityTable CanUpdateEntityFunc / CanDeleteEntityFunc also call EtaActionPolicy so built-in Edit/Delete match the Actions menu.
#Scenario catalog
| Scenario | Resulting status / flags | User next step |
|---|---|---|
| Create | Draft |
Edit lines, Validate, Submit |
| Validate success | Often Validated |
Submit |
| Submit success | Submitted then sync to Valid / Invalid |
Sync Status if needed |
| ETA invalid | Invalid |
Edit, Validate, Submit |
| Cancel accepted | Pending cancel → Cancelled |
Decline not available to issuer |
| Reject accepted | Pending reject → Rejected |
Issuer may Decline Rejection inside window |
| Decline Rejection success | Pending cleared; document reinstated | Continue as Valid |
| Decline Cancellation success | Pending cleared; document reinstated | Recipient keeps invoice |
| Full credit note | Source stays Valid; FullyCredited |
Credit note action disabled |
| Full return | Source → Returned or FullyReturned |
Return action disabled |
| Duplicate | New Draft |
Unrelated to source lock |
#Sequence: submit sent invoice
Submit path — gate → API → ETA → UI sync
sequenceDiagram
autonumber
participant UI as Invoices grid
participant Gate as EtaActionGate
participant API as Invoice API
participant Svc as Submission service
participant ETA as Tax authority
participant DB as Database
UI->>Gate: Evaluate Submit
Gate-->>UI: Allow or deny + tooltip
UI->>API: POST /{id}/submit
API->>Svc: SubmitInvoiceToEta
Svc->>DB: Read invoice
Svc->>ETA: Submit document
ETA-->>Svc: UUID / validation result
Svc->>DB: Persist StatusId + UUID
API-->>UI: Outcome
UI->>UI: Reload grid + re-gate Actions
#Denial reasons (tooltips)
Denial keys resolve through the module localizer. Examples:
- Action Denied Locked By Eta
- Action Denied Already Submitted
- Action Denied Only Valid Can Be Cancelled
- Action Denied Cancellation Already Requested
- Action Denied Decline Window Closed
- Action Denied Decline Rejection Is Sender Only
- Action Denied Decline Cancellation Is Receiver Only
- Action Denied Receipts Use Return
#Official PDF archival
ETA portal PDFs may disappear after a short retention window. EGKits therefore archives the official English and Arabic printouts locally as soon as a document is eligible:
- Sales (E-Invoices) — after status sync once a UUID exists and the document was submitted to ETA
- Received (E-Received Invoices) — after retrieval for Valid / Rejected / Cancelled documents
Work runs on Hangfire (eta-pdf-archive:drain / eta-pdf-archive:reconcile). The invoice UI Documents / Archive panel shows EN/AR status and allows View / Download / Retry without exposing server paths. Details: Document Archive Document Flow.
#ERP integration: AR ↔ ETA bridge and historical back-fill
E-invoices are the fiscal mirror of Accounts Receivable documents, never a second business transaction. Every ETA document raised by the bridge carries PartnerId, ArInvoiceId / ArCreditNoteId and SourceModule = AccountsReceivable; every AR document raised from an ETA document carries SourceModule = EtaBackfill and the key ETA:{uuid}.
Two directions, one document each way
flowchart LR
AR[AR Invoice Sent] -->|bridge, key AR-{invoiceId}| EI[E-invoice draft]
EI -->|AutoValidate / AutoSubmit| ETA((ETA))
ETA -->|Valid / Cancelled / Rejected| AR
H[Historical ETA document Valid] -->|back-fill, key ETA:{uuid}| ARH[AR Invoice / Credit Note]
ARH --> GL[GL journal on issue date]
class AR,ARH,GL egkSuccess
class EI egkDraft
class H egkActive
| Rule | Behavior |
|---|---|
| Bridge opt-in | Per company through ETA bridge settings (Enabled, AutoValidate, AutoSubmit, default activity / country / tax sub-type / EG code, RequireProductEgCode, B2C id threshold). No setting or Enabled = false → AR posts normally, nothing is mirrored. |
| One live document | A redelivered AR event finds the existing non-final e-invoice and stops. Only an Invalid / Rejected / Cancelled mirror leaves room for a Re-issue ({number}-R2, key AR-{id}-R2). |
| Refusals | Business gaps (no tax id on a B2B customer, no EG code, no exchange rate, missing branch) are logged, raised in the ETA inbox and notified. The AR invoice stays valid; fix the data and re-issue. |
| Status sync to AR | Valid → audit row. Cancelled on an unpaid Sent/Overdue invoice → the AR invoice is cancelled. Cancelled with receipts, Rejected, Invalid → the AR invoice is flagged for review and the creator is notified. Credit notes behave the same way. |
| Back-fill | Back-fill page (/ei/backfill): resolve receivers to trading partners (tax id, then name; create missing customers with code C{taxId}), raise AR documents for Valid documents without an AR link in batches with dry run, tolerance and blockers, post the GL journals on the ETA issue dates, import historical receipts from Excel, and reconcile ETA / AR / GL per month. Back-filled AR invoices are never mirrored back to ETA. |
| Withholding | ETA T4 withholding is not part of the AR receivable; it is settled by a receipt with method WHT mapped to a GL account. |
| Currency | Back-filled documents are booked in EGP at the ETA exchange rate; live bridge documents convert foreign AR invoices with the GL rate table. |
Guides: Sales Document Flow (hand-off), Recurring Document Flow (periodic generation).
#E-invoice or e-receipt? One decision for every sale
EGKITS decides the ETA document type once, in the same place, for invoices, POS sales, credit notes and recurring contracts. The e-receipt is a fiscal mirror exactly like the e-invoice: it never posts revenue, tax, receivables or cash on its own.
Document-type decision
flowchart TB
S((Sale)) --> B{Bridge enabled?}
B -->|No| N[No ETA document]
B -->|Yes| C{Channel}
C -->|Point of sale| R[E-receipt]
C -->|Invoice| T{Business with tax id or foreign?}
T -->|Yes| I[E-invoice]
T -->|Individual| P{Consumer document setting}
P -->|E-invoice| I
P -->|E-receipt| R
P -->|None| N
class S egkStart
class I,R egkSuccess
class N egkTerminal
| Rule | Behavior |
|---|---|
| Point of sale | Always an e-receipt, whoever the buyer is. |
| Business or foreign receiver | Always an e-invoice on the invoice channel. |
| Individual | Follows ETA bridge settings → Consumer document (E-invoice, E-receipt, None). A national id stored on an individual is printed on the receipt; it never turns the sale into an e-invoice. |
| Links | Every e-receipt carries the POS transaction or the AR invoice / credit note it mirrors, plus the customer. A receipt belongs to exactly one commercial document; the E-Receipt details page and the POS / AR pages link both ways. |
| Credit notes | A credit note on an invoice mirrored as an e-receipt produces a return receipt (never an ETA credit note); over-returning the source receipt is rejected. |
| Retries | Redelivered events, job retries and restarts find the linked receipt and stop — one business event, one receipt, one validation. |
| Status sync | E-receipt Valid → audit on the AR invoice / recurring occurrence completed; Rejected / Invalid → AR invoice flagged, creator notified, occurrence pending review. |
| Linkage audit | Back-fill → ERP / ETA linkage audit lists, per document, ERP transactions without an ETA document, ETA documents without an ERP link, duplicate UUIDs, wrong document types, E-Received documents without a supplier, tax differences, and missing or duplicate GL postings / stock movements — each with a link to the document to fix. |