feature/AS-sat-regimenes-2024 #11

Merged
jcedilloAS merged 4 commits from feature/AS-sat-regimenes-2024 into main 2026-08-11 20:48:08 +00:00
Member
No description provided.
jcedilloAS added 4 commits 2026-08-11 20:46:00 +00:00
El catálogo sat.tax_regimes tenía 19 claves: las vigentes hasta 2022. Faltaban las
tres que el SAT publicó con vigencia 01-01-2024, así que un receptor en esos
regímenes no se podía representar y el timbrado habría quedado con clave incorrecta.

- 628 Hidrocarburos (moral)
- 629 De los Regímenes Fiscales Preferentes y de las Empresas Multinacionales (física)
- 630 Enajenación de acciones en bolsa de valores (física)

La migración reusa sync_catalogs (upsert idempotente). El downgrade desactiva las
claves en vez de borrarlas: crm.accounts y fin.issuer_settings las referencian por FK
y un CFDI ya timbrado debe seguir siendo legible.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Al timbrar con éxito, el CFDI transmitido al PAC y el que contestó se encolan hacia
el expediente electrónico. El expediente ya existe: nace con la oportunidad y la
factura lo hereda vía fin.invoices.case_id.

Reusa el carril que ya estaba armado (outbox transaccional + worker con reintentos);
este es su primer consumidor en producción.

Decisiones:
- efc_tipo = factura_venta. La lista de tipos está duplicada a mano en este repo y en
  EFC (TIPOS_DOCUMENTO_CRM), y una clave que solo exista de este lado se rechaza allá.
  Los tres archivos se distinguen por nombre: FAC-*.pdf, CFDI-<uuid>-envio.xml,
  CFDI-<uuid>-respuesta.xml.
- Dos kinds (cfdi_request / cfdi_response) y no uno: la guarda de idempotencia es
  (source_table, source_id, kind) y los dos XML comparten source_id —el id del
  intento—, así que un kind común dejaría el par a medias en silencio.
- delete_local=False, a diferencia de los documentos que sube el usuario: el XML
  timbrado es el comprobante fiscal y el CRM lo sirve por /stamp/xml-url.
- Solo el intento que obtuvo timbre. Los rechazos quedan en el CRM y se consultan por
  /stamp/attempts/{id}/xml-url.

El encolado va en la transacción del timbre y el despacho después del commit. Nada de
esto puede propagar: un CFDI ya válido ante el SAT no se cae porque EFC esté apagado.

Ajusta test_fin_sat_catalogs al nuevo conteo de regímenes (19 -> 22) y fija ahí que el
616 es de persona física.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
tests/test_uploads_alcance.py llegó en el rebase del carril EFC (5c4df59) sin el código
de producción que lo acompañaba: venía de la rama del expediente paralelo que se
descartó. El módulo no importaba —`validar_extension` no existe— así que la suite
nunca corrió y el hueco quedó invisible.

El hueco: ambos endpoints solo comprobaban que la key empezara con
tenants/{tid}/companies/{cid}/. Con el permiso de módulo crm.access eso alcanzaba para
firmar o descargar CUALQUIER objeto de la company, incluidos certificates/*.key — la
llave privada del CSD con la que se sellan los CFDI.

- _validar_alcance: además del aislamiento por tenant/company, la key tiene que ser un
  documento del CRM (crm-docs/ o expedientes/{n}/documents/). Se aplica a los dos
  endpoints: el que entrega bytes no puede ser más laxo que el que firma una URL.
- validar_extension: allowlist de extensiones en la subida, no lista de vetados.

Verificado que el frontend solo pasa file_key de documentos a estos endpoints
(uploads.ts, sus dos únicos llamadores). Se agregan 5 casos para /uploads/download,
que la prueba original no cubría.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
test_efc_outbox.py y test_gateway_rutas.py importaban crm.expedientes, el módulo del
expediente paralelo que 5c4df59 descartó al rebasar sobre crm.cases. Con el
ModuleNotFoundError, pytest ni siquiera las coleccionaba: 28 pruebas de la máquina de
reintentos y del tablero de ops llevaban sin correr, justo las del carril que se está
extendiendo.

Se traducen al modelo vigente preservando cada invariante:

- el expediente es crm.cases y se llega por la liga case_id de la solicitud, en vez de
  find_by_service_request;
- el folio es Case.reference, no .folio;
- la idempotencia que se probaba vía ensure_expediente ahora se ejercita en
  replicate_expediente_best_effort, que es donde vive la guarda _expediente_ya_encolado.

No se relaja ninguna aserción.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
jcedilloAS merged commit abdf1ce790 into main 2026-08-11 20:48:08 +00:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: ADUANASOFT/CRM_AGENTES_CARGA#11
No description provided.