Django¶
Serviço + view para emitir faturas e guardar o ID localmente para futuras NCs.
Serviço¶
# billing/services.py
from decimal import Decimal
from vendus import ClientData, DocumentItem, TaxCategory, VendusClient
_vendus = VendusClient.from_env()
def issue_invoice_for_order(order):
"""Emite fatura para uma Order do teu domínio.
Guarda o id da Vendus em order.vendus_document_id para uso futuro
(cancelamento, nota de crédito, etc.).
"""
client_data = None
if order.customer.name:
client_data = ClientData(
name=order.customer.name,
fiscal_id=order.customer.nif or None,
email=order.customer.email,
)
items = [
DocumentItem(
description=line.description,
quantity=Decimal(str(line.quantity)),
unit_price=Decimal(str(line.gross_unit_price)),
tax_category=TaxCategory(line.tax_category),
)
for line in order.lines.all()
]
invoice = _vendus.documents.create_invoice(
register_id=order.register_id,
client=client_data,
items=items,
external_reference=f"order-{order.id}",
)
order.vendus_document_id = invoice.id
order.vendus_document_number = invoice.number
order.vendus_atcud = invoice.atcud
order.save(update_fields=["vendus_document_id", "vendus_document_number", "vendus_atcud"])
return invoice
View¶
# billing/views.py
from django.http import JsonResponse
from django.views.decorators.http import require_POST
from django.shortcuts import get_object_or_404
from vendus import ValidationError, APIError
from .models import Order
from .services import issue_invoice_for_order
@require_POST
def issue_invoice(request, order_id):
order = get_object_or_404(Order, pk=order_id)
try:
invoice = issue_invoice_for_order(order)
except ValidationError as e:
return JsonResponse({"error": str(e)}, status=400)
except APIError as e:
return JsonResponse({"error": str(e)}, status=502)
return JsonResponse({
"id": invoice.id,
"number": invoice.number,
"atcud": invoice.atcud,
})
Notas¶
- O
VendusClienté thread-safe para reutilização — instanciado uma vez ao import do módulo - A política de retries é gerida internamente; o
external_reference=f"order-{order.id}"garante que retentar é seguro - Se preferes async com Django ASGI, troca para
create_invoice_asynceasync defna view