feat(crm): el expediente pide su pedimento provisional a EFC al nacer el folio
Primera mitad del pegamento del carril (fase 7). El reflejo en EFC se pide en
create_case, en el instante en que se mintea el folio, porque el folio es la llave
con la que las dos mitades se reconocen: EFC no recibe ids del CRM como handle.
- crm.cases nace con efc_storage_token = CRM-{company}-{folio}, fijado al nacer y
nunca reescrito: es la carpeta de MinIO del lado de EFC, y que sea inmutable es
lo que permite completar el provisional con la data aduanera real sin mover un
solo archivo.
- replicate_expediente_best_effort corre en la MISMA transaccion que el
expediente. Con EFC_API_URL vacia es no-op; si el encolado o el despacho fallan
no se propaga el error y el barrido del beat recoge lo pendiente. Un sistema de
terceros caido no puede romper un alta.
- El import del carril es diferido para no acoplar el arranque del modulo del
expediente, que es de otra rama, a la integracion.
- Se registra el tablero de ops del carril (outbox, metricas, reintento manual)
en el router del CRM.
test_el_formato_del_folio_es_el_del_contrato se re-apunta a crm/common/folios.py y
queda VERDE: afirma contra la implementacion real que el folio del CRM tiene la forma
que EFC espera, que era el riesgo de haber rebasado sobre otro expediente.
Verificado en la base: create_case produce EXP2026-08-002 con storage_token
CRM-2-EXP2026-08-002 y link_state PENDING, 0 filas encoladas por carril apagado, y la
transaccion reversada NO deja hueco en el contador -- el with_for_update de
crm/common/folios.py revierte limpio.
PENDIENTE de la fase 7: documentos (subida de un paso, proxy de descarga, listado y
desvinculacion) y los 8 archivos del frontend. Por eso siguen rojas
test_efc_outbox, test_gateway_rutas, test_efc_entrega_documento, test_uploads_alcance
y dos de test_contrato_efc.
Ref: T2026-08-046
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,27 @@ def create_case(
|
||||
)
|
||||
db.add(case)
|
||||
db.flush()
|
||||
|
||||
# ── Carril hacia EFC (T2026-08-046) ──────────────────────────────────────────────────
|
||||
# EFC es la fuente única de los documentos del expediente. El reflejo allá —un pedimento
|
||||
# provisional— se pide AQUÍ, en el instante en que nace el folio, porque el folio es
|
||||
# precisamente la llave con la que las dos mitades se reconocen.
|
||||
#
|
||||
# El token de almacenamiento se fija al nacer y NO cambia nunca: es la carpeta de MinIO
|
||||
# del lado de EFC. Que sea inmutable es lo que permite completar el provisional con la
|
||||
# data aduanera real sin mover un solo archivo.
|
||||
#
|
||||
# Todo es best-effort y va en la MISMA transacción que el expediente:
|
||||
# - con ``EFC_API_URL`` vacía es un no-op y el expediente vive igual, solo en el CRM;
|
||||
# - si el encolado o el despacho fallan, no se propaga el error: el barrido del beat
|
||||
# recoge lo pendiente. Un sistema de terceros caído no puede romper un alta.
|
||||
# El import es diferido para no acoplar el arranque del módulo del expediente al carril.
|
||||
from ..expediente_gateway import service as gateway
|
||||
from ..expediente_gateway.storage import storage_token
|
||||
|
||||
case.efc_storage_token = storage_token(company_id, case.reference)
|
||||
db.flush()
|
||||
gateway.replicate_expediente_best_effort(db, case)
|
||||
return case
|
||||
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ from .cases.routes import router as cases_router
|
||||
from .catalogs.routes import router as catalogs_router
|
||||
from .contacts.routes import router as contacts_router
|
||||
from .documents.routes import router as documents_router
|
||||
from .expediente_gateway.routes import router as expediente_gateway_router
|
||||
from .leads.routes import router as leads_router
|
||||
from .metrics.routes import router as metrics_router
|
||||
from .opportunities.routes import router as opportunities_router
|
||||
@@ -50,3 +51,7 @@ router.include_router(catalogs_router)
|
||||
router.include_router(uploads_router)
|
||||
router.include_router(rates_router)
|
||||
router.include_router(rates_cost_router)
|
||||
# Tablero de operación del carril hacia EFC: cola pendiente, métricas y reintento manual.
|
||||
# No es una ruta de negocio; existe para que una persona vea y desatore la entrega sin
|
||||
# entrar a la base. Hereda el enforcement de crm.access del router agregador.
|
||||
router.include_router(expediente_gateway_router)
|
||||
|
||||
Reference in New Issue
Block a user