SDK Python - itzenata-efact
SDK officiel EFact pour Python ≥ 3.9, typé avec Pydantic v2. Toutes les opérations sur les factures sont exposées via efact.invoices.*.
Installation
Installation
pip install itzenata-efactInitialisation
Basique
Basic Initialization
import os
from efact import EFact
# La clé secrète identifie ET authentifie votre organisation.
# Ne la committez jamais : chargez-la depuis l'environnement.
efact = EFact(os.environ["EFACT_SECRET_KEY"])efact.invoices.create(data)
Signature
Method Signature
efact.invoices.create(data: CreateInvoiceRequest | dict) -> InvoiceResponseExemple complet
Complete Example
invoice = efact.invoices.create({
"invoiceNumber": "INV-2026-001",
"invoiceDate": "2026-06-24",
"currency": "MAD",
"paymentMethod": "BANK_TRANSFER",
"buyerIce": "001234567890123",
"buyerName": "Client SARL",
"buyerAddress": "Casablanca",
"lines": [
{
"lineNumber": 1,
"description": "Prestation de service",
"quantity": 1,
"unitPrice": 1000,
"vatRate": 20,
},
],
})
print(invoice.id, invoice.status)efact.invoices.retrieve(invoice_id)
Retrieve Invoice
invoice = efact.invoices.retrieve("inv_01H8XYZABC")
print(invoice.status, invoice.invoice_number)Lève EFactNotFoundError (HTTP 404) si la facture n’existe pas ou n’appartient pas au tenant.
efact.invoices.retrieve_status(invoice_id)
Retrieve Status
status = efact.invoices.retrieve_status("inv_01H8XYZABC")
print(status.status, status.dgi_reference)efact.invoices.list(...)
List Invoices
page = efact.invoices.list(page=0, size=20, status="ACCEPTED")
for inv in page.content:
print(inv.invoice_number, inv.status)Pagination par page/size, pas par curseur.
efact.invoices.update(invoice_id, data)
Update Invoice
# Uniquement pour une facture au statut PENDING — sinon 409
updated = efact.invoices.update("inv_01H8XYZABC", {
"dueDate": "2026-07-31",
"buyerAddress": "Nouvelle adresse, Casablanca",
})efact.invoices.cancel(invoice_id)
Cancel Invoice
# Uniquement pour une facture au statut PENDING
cancelled = efact.invoices.cancel("inv_01H8XYZABC")
print(cancelled.status) # 'CANCELLED'Gestion des erreurs
Le SDK lève des erreurs typées, toutes héritées de EFactError. Toujours envelopper les appels dans un try/except :
Error Handling
from efact import (
EFactApiError,
EFactAuthError,
EFactValidationError,
EFactNotFoundError,
EFactConflictError,
EFactConnectionError,
EFactTimeoutError,
)
try:
efact.invoices.create(data)
except EFactValidationError as e:
print("Champs invalides :", e.errors)
except EFactAuthError:
print("Clé API manquante ou invalide.")
except EFactConflictError:
print("Facture non modifiable (statut ≠ PENDING).")
except EFactApiError as e:
print(e.code, e.http_status, e)
except EFactTimeoutError:
print("Délai dépassé.")
except EFactConnectionError:
print("Échec réseau.")Hiérarchie des erreurs
| Classe | Code HTTP | Quand |
|---|---|---|
EFactConfigError | - | Mauvaise configuration (avant tout appel réseau) |
EFactAuthError | 401 / 403 | Clé API manquante ou invalide |
EFactNotFoundError | 404 | Ressource introuvable (y compris hors tenant) |
EFactValidationError | 400 / 422 | Payload invalide |
EFactConflictError | 409 | Ex. modification/annulation d’une facture non PENDING |
EFactApiError | 5xx | Erreur côté EFact |
EFactTimeoutError | - | Délai dépassé avant réponse |
EFactConnectionError | - | Échec réseau |
Les requêtes GET, PUT et DELETE sont rejouées automatiquement (back-off exponentiel) sur timeout, erreur réseau, ou statuts HTTP 408/425/429/500/502/503/504, jusqu’à max_retries fois. POST (création) n’est jamais rejoué.
Intégration Django
views.py
# views.py
import os
from django.http import JsonResponse
from django.views.decorators.http import require_POST
from efact import EFact
efact = EFact(os.environ["EFACT_SECRET_KEY"])
@require_POST
def create_invoice(request):
import json
body = json.loads(request.body)
invoice = efact.invoices.create({
"invoiceNumber": body["invoiceNumber"],
"invoiceDate": body["invoiceDate"],
"currency": "MAD",
"paymentMethod": "BANK_TRANSFER",
"buyerIce": body.get("buyerIce"),
"buyerName": body.get("buyerName"),
"buyerAddress": body.get("buyerAddress"),
"lines": body["lines"],
})
return JsonResponse({"id": invoice.id, "status": invoice.status})Statuts de facture
Le statut reflète le cycle de vie complet, aligné avec le backend :
| Statut | Signification |
|---|---|
PENDING | Créée, modifiable, pas encore transmise |
SIGNED | Signée (XAdES), en attente de transmission |
TRANSMITTED | Envoyée à la plateforme DGI, en attente de clairance |
ACCEPTED | Validée par la DGI — référence et QR code disponibles |
REJECTED | Rejetée par la DGI — voir errorCode / errorMessage |
FAILED | Échec technique de traitement |
CANCELLED | Annulée (uniquement possible depuis PENDING) |