Every invoice request handled by DIAN REST API — regardless of whether the DIAN accepts, rejects, or times out on the submission — is recorded in PostgreSQL. This audit trail is a legal and operational necessity: Colombian e-invoicing regulations require issuers to demonstrate the full lifecycle of every electronic document, and support teams need structured logs to diagnose failures without re-submitting invoices. The logging architecture is defined as a port interface so that the PostgreSQL adapter can be implemented and injected independently of the business logic.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/farojas85/fast-rest-api/llms.txt
Use this file to discover all available pages before exploring further.
The IInvoiceLogRepositoryPort Interface
The contract for all log operations lives insrc/application/ports/log_repository.py:
save_log method is intentionally generic. Every lifecycle transition — initial recording, success update, and failure recording — flows through the same signature:
| Parameter | Type | Description |
|---|---|---|
status | str | Lifecycle state of the transaction: "PROCESANDO", "SUCCESS", or "FAILED". |
payload_in | Dict[str, Any] | The complete incoming request payload from the POS or client, serialized as a dictionary. |
response_out | Dict[str, Any] | The DIAN response dictionary on success, or an error dictionary on failure. Defaults to None for the initial PROCESANDO entry. |
| return value | int | The auto-assigned primary key (id) of the persisted log record, useful for correlating subsequent status updates. |
Log Lifecycle in a Request
Each invoice submission results in exactly twosave_log calls under normal conditions: one before the DIAN call and one after.
Record PROCESANDO before the DIAN call
Before the adapter dispatches the SOAP request, the use case calls:This creates a durable record of the intent to submit. If the process crashes mid-flight, the
PROCESANDO row with no matching SUCCESS or FAILED sibling signals an incomplete submission that requires investigation.Record SUCCESS after a valid DIAN response
When
DianSoapAdapter.send_invoice() returns {"success": True, ...}, the use case records the outcome including the DIAN-assigned track_id, the CUFE, and the processing status:Record FAILED on any exception or DIAN rejection
If the adapter returns The
{"success": False, ...} or the use case raises an unhandled exception, the failure is logged before propagating:response_out field captures the error type and message so support staff can distinguish a DIAN SOAP Fault from a local validation ValueError or a network Timeout.PostgreSQL Schema (Planned)
The full SQLAlchemy model and Alembic migration script are the next implementation milestone. Based on theIInvoiceLogRepositoryPort interface, the target table structure is:
| Column | Type | Notes |
|---|---|---|
id | SERIAL PRIMARY KEY | Auto-increment primary key returned by save_log. |
status | VARCHAR(20) | One of PROCESANDO, SUCCESS, or FAILED. Consider a PostgreSQL ENUM type for constraint enforcement. |
payload_in | JSONB | Full invoice request dictionary. JSONB enables indexed queries on individual fields (e.g., payload_in->>'id_pedido_origen'). |
response_out | JSONB | DIAN response or error dictionary. Nullable for PROCESANDO entries. |
created_at | TIMESTAMPTZ | UTC timestamp set automatically on insert via DEFAULT now(). |
Alembic is already configured in the project (
alembic.ini is present and alembic is listed as a production dependency in pyproject.toml). Once the SQLAlchemy InvoiceLog model is defined, generating the migration is a single command: uv run alembic revision --autogenerate -m "add invoice_log table".Current Development State
While the PostgreSQL adapter is pending, the controller injects aMockLogRepo placeholder. You can see this in src/infrastructure/controllers/pedido_local_controller.py:
MockLogRepo. Do not deploy this to a production environment — every invoice submission will be untracked and unrecoverable from the application’s own records.
Implementing Your Own Log Adapter
To replaceMockLogRepo with a real PostgreSQL adapter, create a class that implements IInvoiceLogRepositoryPort using SQLAlchemy’s async session:
MockLogRepo block in get_use_case():
IInvoiceLogRepositoryPort is a structural Protocol, Python’s duck typing will verify compatibility at runtime — no explicit inheritance from the Protocol is required.