feature/COVE-api-email-selector

This commit is contained in:
2026-04-13 14:12:25 -06:00
parent 07dd69a28a
commit 233da55f40
7 changed files with 389 additions and 25 deletions

View File

@@ -59,12 +59,16 @@ def trigger_cove_for_invoice(
task=factura_cove_generate,
tenant_id=tenant_id,
company_id=body.company_id,
# Hardcodeado a petición: siempre registrar esta tarea con el correo de Hugo Reyes.
requested_by_user="hreyes@aduanasoft.com.mx",
requested_by_user=(
current_user.get("email")
or current_user.get("preferred_username")
or current_user.get("username")
or "system"
),
task_name="factura_cove_generate",
task_group="factura_cove",
task_origin="a76/factura_cove/invoices/cove",
args=[invoice_id, int(tenant_id), body.company_id],
args=[invoice_id, int(tenant_id), body.company_id, body.recipient_email],
)
return FacturaCoveResponse(

View File

@@ -105,6 +105,7 @@ class GenerateCoveFromInvoiceRequest(BaseModel):
company_id: int
force_regen: Optional[bool] = False
recipient_email: Optional[EmailStr] = None
class GenerateCoveResult(BaseModel):

View File

@@ -107,6 +107,17 @@ class FacturaCoveDomainService:
"""
vu = ctx.vu
if not vu:
errors.add_error(
field="vu",
message="La factura no tiene configuración VU asociada en el agente aduanal",
solution=[
"Configura los datos VU del agente aduanal y sube certificado (.cer) y llave (.key) antes de generar COVE."
],
code="MISSING_VU_CONFIGURATION",
)
return None
# Determinar usuario efectivo de WebService:
# - Preferimos el usuario configurado en VU (web_service_user)
# - Si no existe, usamos el de DODA-PITA (doda_web_service_user)
@@ -567,7 +578,11 @@ class FacturaCoveDomainService:
return mercancias
def build_factura_cove_request(
self, invoice_id: int, tenant_id: int, company_id: int
self,
invoice_id: int,
tenant_id: int,
company_id: int,
recipient_email: str | None = None,
) -> FacturaCoveRequest:
"""
Construye el FacturaCoveRequest completo a partir de una factura,
@@ -646,6 +661,8 @@ class FacturaCoveDomainService:
# Fallback: recortar a máximo 10 caracteres para cumplir el esquema
tipo_figura = raw_figura[:10]
correo_destino = (recipient_email or (ctx.vu.vu_email if ctx.vu else None) or "").strip() or None
return FacturaCoveRequest(
configuracion_vu=configuracion_vu,
# El RFC de consulta NO debe ser igual al RFC del que registra el comprobante.
@@ -660,8 +677,7 @@ class FacturaCoveDomainService:
patente_aduanal=patente_aduanal,
fecha_expedicion=fecha_expedicion,
observaciones=ctx.invoice.vu_observations or None,
# Correo hardcodeado temporalmente para pruebas de COVE
correo_electronico="hreyes@aduanasoft.com.mx",
correo_electronico=correo_destino,
tiene_subdivision=bool(ctx.invoice.logistics and ctx.invoice.logistics.is_subdivision),
certificado_origen=False,
numero_exportador_autorizado=None,
@@ -675,7 +691,6 @@ class FacturaCoveDomainService:
Versión "ligera" para frontend: evalúa si la factura puede generar COVE
e informa por qué no, sin disparar la tarea Celery.
"""
db = self.db or CoreSessionLocal()
errors = ErrorCollector()
try:

View File

@@ -152,7 +152,13 @@ def _poll_external_status(
@celery_app.task(bind=True, name="factura_cove_generate")
def factura_cove_generate(self: Task, invoice_id: int, tenant_id: int, company_id: int) -> dict:
def factura_cove_generate(
self: Task,
invoice_id: int,
tenant_id: int,
company_id: int,
recipient_email: str | None = None,
) -> dict:
"""
Tarea Celery para preparar (y en el futuro generar) un COVE a partir de una factura.
@@ -173,7 +179,10 @@ def factura_cove_generate(self: Task, invoice_id: int, tenant_id: int, company_i
# Esta llamada valida todo y construye el payload; si algo falla, lanza ValidationException
request_payload = service.build_factura_cove_request(
invoice_id=invoice_id, tenant_id=tenant_id, company_id=company_id
invoice_id=invoice_id,
tenant_id=tenant_id,
company_id=company_id,
recipient_email=recipient_email,
)
_progress(self, 80, "Enviando solicitud al servicio COVE...")