contaperu 1.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (205) hide show
  1. contaperu-1.1.0/.gitignore +19 -0
  2. contaperu-1.1.0/ARQUITECTURA.md +301 -0
  3. contaperu-1.1.0/CHANGELOG.md +940 -0
  4. contaperu-1.1.0/CODE_OF_CONDUCT.md +34 -0
  5. contaperu-1.1.0/CONTRIBUTING.md +163 -0
  6. contaperu-1.1.0/HOJA-DE-RUTA.md +477 -0
  7. contaperu-1.1.0/INTEGRAR.md +216 -0
  8. contaperu-1.1.0/INTEROPERABILIDAD.md +1805 -0
  9. contaperu-1.1.0/LICENSE +21 -0
  10. contaperu-1.1.0/PKG-INFO +543 -0
  11. contaperu-1.1.0/README.md +474 -0
  12. contaperu-1.1.0/REFERENCIAS.md +258 -0
  13. contaperu-1.1.0/SECURITY.md +44 -0
  14. contaperu-1.1.0/contaperu/__init__.py +55 -0
  15. contaperu-1.1.0/contaperu/_compat/__init__.py +6 -0
  16. contaperu-1.1.0/contaperu/_compat/concar.py +48 -0
  17. contaperu-1.1.0/contaperu/_compat/generar.py +34 -0
  18. contaperu-1.1.0/contaperu/_compat/operaciones.py +63 -0
  19. contaperu-1.1.0/contaperu/_datos.py +86 -0
  20. contaperu-1.1.0/contaperu/_obsoleto.py +73 -0
  21. contaperu-1.1.0/contaperu/_version.py +30 -0
  22. contaperu-1.1.0/contaperu/api/__init__.py +55 -0
  23. contaperu-1.1.0/contaperu/api/documento.py +18 -0
  24. contaperu-1.1.0/contaperu/api/errores.py +48 -0
  25. contaperu-1.1.0/contaperu/api/esquemas/adaptacion_pcge.schema.json +47 -0
  26. contaperu-1.1.0/contaperu/api/esquemas/asiento.schema.json +62 -0
  27. contaperu-1.1.0/contaperu/api/esquemas/catalogos_del_estandar.schema.json +33 -0
  28. contaperu-1.1.0/contaperu/api/esquemas/catalogos_sunat.schema.json +89 -0
  29. contaperu-1.1.0/contaperu/api/esquemas/claves_previas.schema.json +26 -0
  30. contaperu-1.1.0/contaperu/api/esquemas/comprobante_de_la_respuesta.schema.json +67 -0
  31. contaperu-1.1.0/contaperu/api/esquemas/configuracion.schema.json +7 -0
  32. contaperu-1.1.0/contaperu/api/esquemas/cuadre.schema.json +38 -0
  33. contaperu-1.1.0/contaperu/api/esquemas/cuenta_pcge.schema.json +68 -0
  34. contaperu-1.1.0/contaperu/api/esquemas/diagnostico.schema.json +279 -0
  35. contaperu-1.1.0/contaperu/api/esquemas/documento_anotado.schema.json +134 -0
  36. contaperu-1.1.0/contaperu/api/esquemas/drivers.schema.json +85 -0
  37. contaperu-1.1.0/contaperu/api/esquemas/exportacion.schema.json +85 -0
  38. contaperu-1.1.0/contaperu/api/esquemas/imputacion.schema.json +50 -0
  39. contaperu-1.1.0/contaperu/api/esquemas/problema.schema.json +35 -0
  40. contaperu-1.1.0/contaperu/api/openconta.json +2824 -0
  41. contaperu-1.1.0/contaperu/api/openconta.py +141 -0
  42. contaperu-1.1.0/contaperu/api/operaciones.py +246 -0
  43. contaperu-1.1.0/contaperu/api/tabla.py +127 -0
  44. contaperu-1.1.0/contaperu/asiento/__init__.py +79 -0
  45. contaperu-1.1.0/contaperu/asiento/configuracion.py +70 -0
  46. contaperu-1.1.0/contaperu/asiento/faltas.py +156 -0
  47. contaperu-1.1.0/contaperu/asiento/huella.py +77 -0
  48. contaperu-1.1.0/contaperu/asiento/imputacion.py +87 -0
  49. contaperu-1.1.0/contaperu/asiento/indice.py +65 -0
  50. contaperu-1.1.0/contaperu/asiento/lineas.py +84 -0
  51. contaperu-1.1.0/contaperu/asiento/motor.py +304 -0
  52. contaperu-1.1.0/contaperu/asiento/resolucion.py +394 -0
  53. contaperu-1.1.0/contaperu/catalogos.py +89 -0
  54. contaperu-1.1.0/contaperu/cli.py +43 -0
  55. contaperu-1.1.0/contaperu/comparar_sire.py +181 -0
  56. contaperu-1.1.0/contaperu/configuracion.py +321 -0
  57. contaperu-1.1.0/contaperu/datos/sunat/catalogos.json +50 -0
  58. contaperu-1.1.0/contaperu/datos/sunat/detracciones.json +20 -0
  59. contaperu-1.1.0/contaperu/detracciones.py +155 -0
  60. contaperu-1.1.0/contaperu/drivers/__init__.py +166 -0
  61. contaperu-1.1.0/contaperu/drivers/asiento_neutral/__init__.py +44 -0
  62. contaperu-1.1.0/contaperu/drivers/concar/__init__.py +19 -0
  63. contaperu-1.1.0/contaperu/drivers/concar/datos.py +152 -0
  64. contaperu-1.1.0/contaperu/drivers/concar/proyeccion.py +218 -0
  65. contaperu-1.1.0/contaperu/drivers/concar/xlsx.py +151 -0
  66. contaperu-1.1.0/contaperu/drivers/contasis/__init__.py +39 -0
  67. contaperu-1.1.0/contaperu/drivers/contasis/datos.py +208 -0
  68. contaperu-1.1.0/contaperu/drivers/contasis/proyeccion.py +172 -0
  69. contaperu-1.1.0/contaperu/drivers/contasis/xlsx.py +39 -0
  70. contaperu-1.1.0/contaperu/drivers/contrato.py +472 -0
  71. contaperu-1.1.0/contaperu/drivers/csv/__init__.py +95 -0
  72. contaperu-1.1.0/contaperu/drivers/kit/__init__.py +18 -0
  73. contaperu-1.1.0/contaperu/drivers/kit/celdas.py +42 -0
  74. contaperu-1.1.0/contaperu/drivers/kit/columnas.py +98 -0
  75. contaperu-1.1.0/contaperu/drivers/kit/opciones.py +46 -0
  76. contaperu-1.1.0/contaperu/drivers/kit/texto.py +68 -0
  77. contaperu-1.1.0/contaperu/drivers/kit/xlsx.py +21 -0
  78. contaperu-1.1.0/contaperu/drivers/sire/__init__.py +9 -0
  79. contaperu-1.1.0/contaperu/drivers/sire/txt.py +185 -0
  80. contaperu-1.1.0/contaperu/errores.py +39 -0
  81. contaperu-1.1.0/contaperu/formato.py +24 -0
  82. contaperu-1.1.0/contaperu/generar.py +44 -0
  83. contaperu-1.1.0/contaperu/igv.py +193 -0
  84. contaperu-1.1.0/contaperu/lectores/__init__.py +13 -0
  85. contaperu-1.1.0/contaperu/lectores/_zip.py +44 -0
  86. contaperu-1.1.0/contaperu/lectores/archivos.py +163 -0
  87. contaperu-1.1.0/contaperu/lectores/sire_txt.py +206 -0
  88. contaperu-1.1.0/contaperu/lectores/xml_ubl.py +297 -0
  89. contaperu-1.1.0/contaperu/modelo.py +360 -0
  90. contaperu-1.1.0/contaperu/operaciones.py +80 -0
  91. contaperu-1.1.0/contaperu/partida_doble.py +100 -0
  92. contaperu-1.1.0/contaperu/pcge/__init__.py +22 -0
  93. contaperu-1.1.0/contaperu/pcge/adaptar.py +183 -0
  94. contaperu-1.1.0/contaperu/pcge/catalogo.py +91 -0
  95. contaperu-1.1.0/contaperu/pcge/catalogo2026.json +6467 -0
  96. contaperu-1.1.0/contaperu/pcge/clases.py +53 -0
  97. contaperu-1.1.0/contaperu/pcge/pcge2026.json +6 -0
  98. contaperu-1.1.0/contaperu/pipeline/__init__.py +14 -0
  99. contaperu-1.1.0/contaperu/pipeline/armado.py +138 -0
  100. contaperu-1.1.0/contaperu/pipeline/diagnostico.py +239 -0
  101. contaperu-1.1.0/contaperu/pipeline/lectura.py +76 -0
  102. contaperu-1.1.0/contaperu/pipeline/preparacion.py +369 -0
  103. contaperu-1.1.0/contaperu/pipeline/salida.py +194 -0
  104. contaperu-1.1.0/contaperu/pipeline/seleccion.py +35 -0
  105. contaperu-1.1.0/contaperu/puertas/__init__.py +10 -0
  106. contaperu-1.1.0/contaperu/puertas/cli.py +355 -0
  107. contaperu-1.1.0/contaperu/puertas/comun.py +37 -0
  108. contaperu-1.1.0/contaperu/puertas/servidor_http.py +225 -0
  109. contaperu-1.1.0/contaperu/puertas/servidor_mcp.py +441 -0
  110. contaperu-1.1.0/contaperu/servidor_mcp.py +54 -0
  111. contaperu-1.1.0/contaperu/validar.py +260 -0
  112. contaperu-1.1.0/contaperu/vocabulario.py +53 -0
  113. contaperu-1.1.0/estandar/LEEME.md +458 -0
  114. contaperu-1.1.0/estandar/MIGRAR-A-1.0.md +105 -0
  115. contaperu-1.1.0/estandar/catalogos.json +37 -0
  116. contaperu-1.1.0/estandar/conformidad/diagnosticar.json +580 -0
  117. contaperu-1.1.0/estandar/conformidad/esquema.json +623 -0
  118. contaperu-1.1.0/estandar/enmiendas/0001-id-en-destino.md +32 -0
  119. contaperu-1.1.0/estandar/enmiendas/0002-dimensiones.md +27 -0
  120. contaperu-1.1.0/estandar/enmiendas/0003-estado-de-la-linea.md +29 -0
  121. contaperu-1.1.0/estandar/enmiendas/0004-medio-de-pago.md +25 -0
  122. contaperu-1.1.0/estandar/enmiendas/0005-retencion-de-igv.md +30 -0
  123. contaperu-1.1.0/estandar/enmiendas/0006-percepcion.md +26 -0
  124. contaperu-1.1.0/estandar/enmiendas/0007-no-domiciliado.md +25 -0
  125. contaperu-1.1.0/estandar/enmiendas/0008-imputaciones-en-el-documento.md +34 -0
  126. contaperu-1.1.0/estandar/enmiendas/0009-id-externo-en-la-linea.md +40 -0
  127. contaperu-1.1.0/estandar/enmiendas/0010-clase-en-la-linea.md +43 -0
  128. contaperu-1.1.0/estandar/enmiendas/0011-rol-y-tipo-de-libro-en-catalogo.md +38 -0
  129. contaperu-1.1.0/estandar/enmiendas/LEEME.md +57 -0
  130. contaperu-1.1.0/estandar/open-accounting.schema.json +307 -0
  131. contaperu-1.1.0/pyproject.toml +77 -0
  132. contaperu-1.1.0/tests/conftest.py +55 -0
  133. contaperu-1.1.0/tests/drivers_de_prueba/__init__.py +1 -0
  134. contaperu-1.1.0/tests/drivers_de_prueba/diario_json.py +41 -0
  135. contaperu-1.1.0/tests/fixtures/capas/acoplamiento_pe.json +66 -0
  136. contaperu-1.1.0/tests/fixtures/caracterizacion/casos_202608.json +20201 -0
  137. contaperu-1.1.0/tests/fixtures/caracterizacion/compras_202601.json +7097 -0
  138. contaperu-1.1.0/tests/fixtures/caracterizacion/ventas_202512.json +10386 -0
  139. contaperu-1.1.0/tests/fixtures/compat/superficie_0_10.json +831 -0
  140. contaperu-1.1.0/tests/fixtures/golden/casos_202608.json +60 -0
  141. contaperu-1.1.0/tests/fixtures/golden/compras_202601.json +15 -0
  142. contaperu-1.1.0/tests/fixtures/golden/ventas_202512.json +39 -0
  143. contaperu-1.1.0/tests/fixtures/snapshot/concar_filas.json +29156 -0
  144. contaperu-1.1.0/tests/fixtures/snapshot/concar_formato.json +9220 -0
  145. contaperu-1.1.0/tests/fixtures/snapshot/contasis_filas.json +5336 -0
  146. contaperu-1.1.0/tests/fixtures/snapshot/contasis_formato.json +4722 -0
  147. contaperu-1.1.0/tests/fixtures/snapshot/lineas_neutrales.json +4112 -0
  148. contaperu-1.1.0/tests/fixtures/superficie/1.0.json +499 -0
  149. contaperu-1.1.0/tests/fixtures/xml/20131312955-01-F001-123.xml +109 -0
  150. contaperu-1.1.0/tests/fixtures/xml/20131312955-01-F001-124.xml +50 -0
  151. contaperu-1.1.0/tests/fixtures/xml/20131312955-03-B001-55.xml +48 -0
  152. contaperu-1.1.0/tests/fixtures/xml/20131312955-07-FC01-7.xml +54 -0
  153. contaperu-1.1.0/tests/fixtures/xml/R-20131312955-01-F001-123.xml +13 -0
  154. contaperu-1.1.0/tests/fixtures/xml/ubl20-antiguo.xml +17 -0
  155. contaperu-1.1.0/tests/test_api.py +140 -0
  156. contaperu-1.1.0/tests/test_asiento_concar.py +676 -0
  157. contaperu-1.1.0/tests/test_capas.py +261 -0
  158. contaperu-1.1.0/tests/test_caracterizacion.py +200 -0
  159. contaperu-1.1.0/tests/test_catalogos.py +40 -0
  160. contaperu-1.1.0/tests/test_clases.py +234 -0
  161. contaperu-1.1.0/tests/test_claves_previas.py +99 -0
  162. contaperu-1.1.0/tests/test_cli.py +164 -0
  163. contaperu-1.1.0/tests/test_comparar_sire.py +134 -0
  164. contaperu-1.1.0/tests/test_compat_firmas.py +130 -0
  165. contaperu-1.1.0/tests/test_compat_superficie.py +86 -0
  166. contaperu-1.1.0/tests/test_configuracion.py +259 -0
  167. contaperu-1.1.0/tests/test_conformidad.py +100 -0
  168. contaperu-1.1.0/tests/test_contrato_drivers.py +709 -0
  169. contaperu-1.1.0/tests/test_detracciones.py +171 -0
  170. contaperu-1.1.0/tests/test_diagnosticar.py +252 -0
  171. contaperu-1.1.0/tests/test_documentacion.py +100 -0
  172. contaperu-1.1.0/tests/test_driver_asiento_neutral.py +97 -0
  173. contaperu-1.1.0/tests/test_driver_contasis.py +102 -0
  174. contaperu-1.1.0/tests/test_driver_diario_json.py +71 -0
  175. contaperu-1.1.0/tests/test_driver_sire.py +176 -0
  176. contaperu-1.1.0/tests/test_enmiendas.py +84 -0
  177. contaperu-1.1.0/tests/test_estandar.py +167 -0
  178. contaperu-1.1.0/tests/test_exportar_concar_por_caso.py +122 -0
  179. contaperu-1.1.0/tests/test_formato_xlsx.py +107 -0
  180. contaperu-1.1.0/tests/test_frontera.py +208 -0
  181. contaperu-1.1.0/tests/test_huella.py +128 -0
  182. contaperu-1.1.0/tests/test_igv.py +141 -0
  183. contaperu-1.1.0/tests/test_imputaciones_en_el_documento.py +194 -0
  184. contaperu-1.1.0/tests/test_integrar.py +74 -0
  185. contaperu-1.1.0/tests/test_kit_columnas.py +61 -0
  186. contaperu-1.1.0/tests/test_linea_neutral.py +225 -0
  187. contaperu-1.1.0/tests/test_nc_descuento.py +160 -0
  188. contaperu-1.1.0/tests/test_openconta.py +136 -0
  189. contaperu-1.1.0/tests/test_operaciones.py +253 -0
  190. contaperu-1.1.0/tests/test_partida_doble.py +98 -0
  191. contaperu-1.1.0/tests/test_pcge.py +106 -0
  192. contaperu-1.1.0/tests/test_pcge_catalogo.py +86 -0
  193. contaperu-1.1.0/tests/test_plantilla_contasis.py +111 -0
  194. contaperu-1.1.0/tests/test_serializacion.py +95 -0
  195. contaperu-1.1.0/tests/test_servidor_http.py +172 -0
  196. contaperu-1.1.0/tests/test_servidor_mcp.py +310 -0
  197. contaperu-1.1.0/tests/test_sire_txt.py +128 -0
  198. contaperu-1.1.0/tests/test_snapshot_concar.py +242 -0
  199. contaperu-1.1.0/tests/test_snapshot_contasis.py +198 -0
  200. contaperu-1.1.0/tests/test_superficie_publica.py +97 -0
  201. contaperu-1.1.0/tests/test_validar.py +146 -0
  202. contaperu-1.1.0/tests/test_version.py +80 -0
  203. contaperu-1.1.0/tests/test_vocabulario.py +117 -0
  204. contaperu-1.1.0/tests/test_xml_ubl.py +211 -0
  205. contaperu-1.1.0/tests/util.py +100 -0
@@ -0,0 +1,19 @@
1
+ # Entornos y artefactos de Python
2
+ .venv/
3
+ venv/
4
+ __pycache__/
5
+ *.py[cod]
6
+ *.egg-info/
7
+ dist/
8
+ build/
9
+ .pytest_cache/
10
+ .ruff_cache/
11
+
12
+ # Salidas de prueba: nunca al repo
13
+ *.xlsx
14
+ *.zip
15
+ salida/
16
+
17
+ # Datos reales de contribuyentes: NUNCA. Este repo es publico.
18
+ privado/
19
+ *.privado.*
@@ -0,0 +1,301 @@
1
+ # Arquitectura de ContaPerú
2
+
3
+ Este documento explica **cómo está construido** el proyecto y **por qué así**: qué es el núcleo, qué
4
+ es una puerta, por dónde entra un driver nuevo y qué es lo que no se puede tocar. Es la referencia
5
+ para quien vaya a contribuir, y la lista de decisiones que no hay que volver a discutir.
6
+
7
+ ## Tres niveles
8
+
9
+ ```
10
+ ┌─────────────────────────────────────────────────────────────┐
11
+ │ 3 · AGENTES │
12
+ │ servidor MCP · `diagnosticar` · un agente que pregunta │
13
+ │ «¿qué falta?», «¿qué saldría?», «¿qué bloquea?» │
14
+ ├─────────────────────────────────────────────────────────────┤
15
+ │ 2 · DRIVERS │
16
+ │ sire (TXT) · concar y contasis (Excel) · csv · terceros│
17
+ │ cada uno traduce vocabulario; ninguno decide contabilidad│
18
+ ├─────────────────────────────────────────────────────────────┤
19
+ │ 1 · NÚCLEO │
20
+ │ modelo · lectores · validación · asiento · detracciones│
21
+ │ IGV · partida doble · PCGE · el estándar `open-accounting` │
22
+ └─────────────────────────────────────────────────────────────┘
23
+ ```
24
+
25
+ **El núcleo** sabe contabilidad peruana y no sabe nada más: ni de archivos, ni de red, ni de qué
26
+ ERP hay al otro lado. **Los drivers** conocen el formato de un sistema concreto y nada de
27
+ contabilidad: reciben el asiento resuelto y lo escriben. **Los agentes** hablan con el núcleo a
28
+ través de la fachada y de las puertas, y lo usan como riel determinista: la IA lee el PDF; el
29
+ núcleo decide el asiento.
30
+
31
+ La secuencia de construcción es esa, de abajo arriba, y no es casual: primero el núcleo de reglas,
32
+ después los drivers que lo conectan con lo que ya existe, y solo entonces los agentes. Es el orden en
33
+ que se puede confiar en cada capa porque la de abajo ya está probada con archivos reales.
34
+
35
+ ## El flujo de un comprobante
36
+
37
+ ```
38
+ XML UBL / TXT del SIRE / JSON open-accounting
39
+
40
+
41
+ lectores/ ──► Comprobante (modelo.py: Decimal, positivo, fechas date)
42
+
43
+
44
+ validar.py ──► observaciones: error (bloquea) | aviso (se exporta igual)
45
+
46
+
47
+ asiento/motor.py ──► LineaDiario × 2..5 ◄── ESTA ES LA FUENTE
48
+ │ (cuenta, D/H, importe, rol, sub-diario, correlativo,
49
+ │ documento.tipo_cp, glosa entera, tasa exacta)
50
+
51
+ ├──► drivers/concar/proyeccion.py ──► 41 columnas ──► .xlsx
52
+ ├──► drivers/csv ──► una fila por línea
53
+ ├──► un driver de terceros (`desde_lineas`) ──► el formato de su ERP
54
+ └──► operaciones.generar_asiento ──► el bloque `asiento` del estándar
55
+ ```
56
+
57
+ Aparte va la familia **registro**: una fila por comprobante, sin asiento, así que sus drivers no pasan por el
58
+ motor. El SIRE es un registro tributario y se escribe desde el comprobante (`linea(c, libro, idx, opciones)`). Un
59
+ sistema contable que importa su registro de compras o de ventas y arma el asiento él mismo (CONTASIS)
60
+ recibe los comprobantes con la configuración (`desde_comprobantes`) y lleva cuentas, pero no las
61
+ decide: las lee de `asiento.partes_de` y `asiento.cuenta_tercero`, la misma resolución que usa el motor para el
62
+ asiento de CONCAR.
63
+
64
+ ### Un mes, paso a paso
65
+
66
+ ![El recorrido de un mes dentro del motor: leer el XML o la propuesta del SIRE hasta open-accounting; preparar, revisar, seleccionar y exigir lo del destino; armar un asiento, un registro contable o un registro tributario; y responder con el archivo y cada comprobante](diagramas/recorrido-de-un-mes.svg)
67
+
68
+ Lo mismo, visto desde `pipeline/`. Si llegan archivos de SUNAT, el motor primero los **lee** (`pipeline/lectura`) y
69
+ los lleva al documento `open-accounting`: el XML de cada factura, suelto o en ZIP y sin los CDR, o el TXT de la
70
+ propuesta del SIRE. Desde ahí, cada mes recorre seis pasos:
71
+
72
+ 1. **Preparar** (`preparacion`). Separa el libro de los comprobantes, aplica la configuración general y la sección
73
+ del destino, y le pone a cada comprobante su imputación por el `id_externo`. Una imputación que no es de ningún
74
+ comprobante se rechaza.
75
+ 2. **Revisar** (`validar.revisar`, en el núcleo). Valida el RUC, el IGV, el total, la fecha y los duplicados, también
76
+ contra lo ya anotado en otros periodos. Un error detiene la exportación; un aviso deja pasar el comprobante.
77
+ 3. **Seleccionar** (`seleccion`). Quedan fuera los excluidos, los duplicados y los tipos que el destino no lleva
78
+ (`EXCLUYE_TIPOS`), como los recibos por honorarios en el SIRE y en CONTASIS.
79
+ 4. **Exigir lo del destino** (`exigir_requisitos` y `contrato.no_caben`). Si el destino lleva cuentas, el núcleo
80
+ comprueba, antes de armar nada, que cada comprobante tenga su cuenta, su centro de costo y su sigla, y que quepa
81
+ en el formato.
82
+ 5. **Armar, según la forma del driver** (`armado`).
83
+ - **Asiento** (`desde_lineas`: CONCAR, CSV): el núcleo arma las líneas neutrales, las numera por sub-diario, exige
84
+ que cuadren al céntimo y les calcula la huella; el driver solo traduce cada línea.
85
+ - **Registro contable** (`desde_comprobantes`: CONTASIS): el driver recibe los comprobantes y lee las cuentas de
86
+ la misma resolución que usa el asiento.
87
+ - **Registro tributario** (`linea`: el SIRE): una línea por comprobante, sin cuentas, en un TXT con su ZIP.
88
+ 6. **Responder** (`salida`). El archivo, el resumen y `_exportacion`: por cada comprobante, su identidad, su tramo de
89
+ líneas y su huella, con la versión del motor que lo produjo.
90
+
91
+ No todas las operaciones hacen el recorrido completo:
92
+ - `revisar` llega hasta el paso 2.
93
+ - `diagnosticar` recorre hasta el 4 sin detenerse y cuenta lo que bloquea, lo que falta y a quién pedírselo.
94
+ - `generar_asiento` se queda en el asiento, sin escribir el archivo.
95
+ - `exportar` lo recorre entero.
96
+
97
+ ### La decisión que lo ordena todo: la línea neutral es la fuente
98
+
99
+ Hasta la 0.6 el asiento nacía en las columnas del Excel de CONCAR (`'A'..'AO'`) y la línea neutral
100
+ se sacaba después releyéndolas. Funcionaba, pero la línea «neutral» heredaba el vocabulario de un
101
+ ERP —la sigla `FT` en vez del código SUNAT `01`, la glosa cortada a 30 caracteres, la tasa del IGV
102
+ redondeada a entero— y un segundo driver de asientos habría tenido que reinterpretar columnas de
103
+ CONCAR para escribir las suyas.
104
+
105
+ Desde la 0.7 la dirección está invertida. `asiento/motor.py` arma las líneas en el vocabulario de
106
+ `open-accounting` y CONCAR es una proyección más. Lo que hace posible el cambio sin riesgo es
107
+ `tests/test_snapshot_concar.py`: 42 casos con las 41 columnas congeladas celda a celda **antes** del
108
+ refactor (hoy son 52). Ese Excel lleva un año importándose en CONCARs de producción; el snapshot es la garantía de
109
+ que no cambió ni una celda, y la regla para el futuro: **regenerarlo es una decisión contable con
110
+ fuente, nunca un trámite.**
111
+
112
+ ### Lo que lleva cada línea
113
+
114
+ `LineaDiario` (`asiento/lineas.py`) es el bloque `asiento` del estándar. Además de cuenta, sentido e
115
+ importe lleva lo que un driver necesita para traducir **sin adivinar**:
116
+
117
+ | Campo | Para qué |
118
+ |---|---|
119
+ | `rol` | `principal`, `igv`, `retencion_4ta`, `tercero`, `detraccion_tercero`, `detraccion`. Un ERP que pida el IGV en su columna encuentra la línea por su rol, no por su cuenta (que la elige cada empresa). |
120
+ | `documento.tipo_cp`, `referencia.tipo_cp` | El código SUNAT (Tabla 10). `tipo` conserva la sigla del ERP por compatibilidad. |
121
+ | `glosa` | Entera, con su prefijo. El corte lo decide cada ERP. |
122
+ | `tasa_igv` | La del comprobante, como texto exacto (`"10.5"`). Redondear es cosa del que solo admite enteros. |
123
+ | `detraccion.codigo` | El código SUNAT del bien o servicio, al lado del interno del contribuyente. |
124
+
125
+ ## Capas, api y puertas
126
+
127
+ Desde la 1.0 el motor se ordena en capas, y cada una solo importa de las de abajo. No es una convención:
128
+ `tests/test_capas.py` lo comprueba módulo a módulo, y la lista de excepciones está vacía.
129
+
130
+ ```
131
+ ENTRADA SALIDA (drivers, por canal)
132
+ ERP en otro lenguaje ─HTTP/OpenConta─► puertas/servidor_http ─┐ ┌─ legacy: concar · contasis
133
+ Agente de IA ────────MCP────────────► puertas/servidor_mcp ──┼─► api ─► pipeline ─► núcleo ─┼─ tributario: sire
134
+ Contador ────────────CLI────────────► puertas/cli ───────────┘ └─ intercambio: csv
135
+ ```
136
+
137
+ | Capa | Módulos | Qué sabe |
138
+ |---|---|---|
139
+ | base | `_version`, `_obsoleto`, `_datos`, `errores` | la versión, el aviso de las rutas viejas, los datos empaquetados, la base de los errores |
140
+ | núcleo | `modelo`, `catalogos`, `configuracion`, `igv`, `detracciones`, `validar`, `partida_doble`, `asiento`, `pcge`, `lectores`, `comparar_sire` | contabilidad peruana, sin disco, sin red y sin reloj |
141
+ | drivers | `drivers/` (`contrato`, `kit`, cada driver) | el formato de un destino, nada de contabilidad |
142
+ | pipeline | `pipeline/` (`preparacion`, `lectura`, `seleccion`, `armado`, `salida`, `diagnostico`) | cómo se prepara y se orquesta un mes, escrito una vez |
143
+ | api | `api/` (`operaciones`, `tabla`, `documento`, `errores`, `openconta`, `esquemas/`) | lo que una aplicación usa, estable durante la 1.x |
144
+ | puertas | `puertas/` (`cli`, `servidor_mcp`, `servidor_http`, `comun`) | un protocolo; solo hablan con la api |
145
+ | compat | `_compat/` | las rutas de la 0.10; nadie las importa |
146
+
147
+ - **El pipeline** es el único dueño de la preparación: del documento al libro y los comprobantes, la configuración
148
+ aplicada hacia un destino con la imputación dentro, las claves previas, la selección de lo que sale, lo que exige el
149
+ destino y lo que no cabe en su formato, la numeración, las líneas con su índice, el cuadre y la huella. `revisar`,
150
+ `diagnosticar`, `generar_asiento` y `exportar` recorren los mismos pasos, cada una hasta donde le toca;
151
+ `generar_asiento` es `exportar` sin escribir el archivo.
152
+ - **La api** (`contaperu.api`) habla en documentos `open-accounting`: el documento primero, lo demás por su nombre,
153
+ `driver` sin valor por defecto. Su superficie queda congelada con su firma (`tests/test_superficie_publica.py`), y
154
+ debajo queda el nivel de extensión —`modelo`, `asiento`, `drivers.contrato`, `drivers.kit`…— para quien escribe un
155
+ driver. Cada error hereda de `ErrorContaperu` y lleva una `clave` estable; `api.problema` lo dice como RFC 9457.
156
+ - **La tabla de operaciones** (`api.OPERACIONES`) declara de cada operación su entrada (JSON Schema 2020-12, sacada de
157
+ su firma), su salida, su ruta HTTP y su nombre en el MCP. De ella salen las herramientas y recursos del MCP, las
158
+ rutas de la puerta HTTP y **OpenConta**, el contrato en formato OpenAPI 3.1 que se versiona en
159
+ `api/openconta.json`.
160
+ - **Las puertas** traducen un protocolo y delegan. Comparten en `puertas/comun.py` los topes y la defensa del `Host`,
161
+ y `tests/test_frontera.py` impide que el núcleo importe una puerta, el SDK del MCP, la red o el reloj. Las tres dan
162
+ el mismo documento y el mismo diagnóstico.
163
+ - **Las rutas de la 0.10** (`operaciones`, `generar`, `cli`, `servidor_mcp`, `formato`, `drivers.concar.construir`)
164
+ siguen resolviendo al mismo objeto, con sus firmas, y avisan con `RutaObsoleta` al usarse. Se retiran en la 2.0.
165
+ - **El portal** (otro repositorio) usa la api y el nivel de extensión; el `resumen` de cada exportación, que guarda tal
166
+ cual, conserva sus claves.
167
+ - **Lo peruano, a la vista** (hito J0): qué módulos importan uno peruano queda congelado en
168
+ `tests/fixtures/capas/acoplamiento_pe.json`, y un acoplamiento nuevo no entra sin verse. Separar la jurisdicción
169
+ (J2-J6) espera un cliente real fuera del Perú.
170
+
171
+ ## Cómo se enchufa un driver
172
+
173
+ Un driver expone `NOMBRE`, `CANAL`, `FORMATOS`, `OPCIONES`, `nombre()` y **una** forma (`drivers/contrato.py`):
174
+
175
+ | Forma | Recibe | Para qué |
176
+ |---|---|---|
177
+ | `linea(c, libro, idx, opciones) -> str` | un comprobante | un registro tributario línea a línea (el SIRE) |
178
+ | `desde_comprobantes(libro, comprobantes, config, opciones)` | los comprobantes y la configuración, con la imputación | el registro de un sistema contable que arma el asiento él mismo (CONTASIS) |
179
+ | `desde_lineas(libro, lineas, config, opciones, *, indice=())` | las **líneas neutrales**, numeradas y cuadradas, y el índice de cada comprobante | **todo driver de asientos**, CONCAR incluido desde la 1.0 |
180
+ | `construir(libro, comprobantes, config, correlativos, opciones)` | los comprobantes | la forma de CONCAR hasta la 0.10; un tercero que la use sigue funcionando con aviso hasta la 2.0 |
181
+
182
+ Con `desde_lineas` el pipeline arma el asiento, lo numera y exige que cuadre **antes** de llamar al driver; el driver
183
+ solo traduce. Si su firma acepta `indice`, recibe además qué tramo de líneas es de qué comprobante y una **cabecera**
184
+ con sus hechos (`asiento.indice.Cabecera`: identidad, contraparte, glosa, base, IGV y total en texto exacto), fuera de
185
+ las líneas y de la huella. Es lo que permite escribir en cada fila lo que la línea no guarda: CONCAR saca de ahí la
186
+ glosa de su columna F y la tasa entera de la AO, que se redondea una sola vez desde el IGV y la base del comprobante.
187
+
188
+ **El canal dice a quién se entrega** (la familia dice qué se entrega), y el contrato hace cumplir sus reglas:
189
+
190
+ | Canal | Qué es | Reglas | Drivers |
191
+ |---|---|---|---|
192
+ | `legacy` | un sistema contable instalado que importa un archivo | lleva cuentas; declara `EXIGE` | concar, contasis; STARSOFT y SISCONT cuando entren |
193
+ | `tributario` | un registro que se presenta a SUNAT | forma `linea`, sin cuentas ni configuración | sire |
194
+ | `intercambio` | un formato neutral para leer o integrar | forma `desde_lineas` | csv, asiento_neutral |
195
+
196
+ Cada canal se presenta en uno de los tres grupos de destinos del motor (`contrato.GRUPOS`): `tributario` es **SIRE**,
197
+ `legacy` es **Legacy** e `intercambio` es **ERP**.
198
+
199
+ `api_erp` —escribir el cuerpo de la API de un ERP moderno— queda **reservado** (hito A5): el contrato lo rechaza. Un
200
+ driver de terceros sin `CANAL` se registra con un `AvisoDriver` y se trata como `legacy` durante la 1.x.
201
+
202
+ **El vocabulario dice con qué palabras llegan las líneas** (`VOCABULARIO`, 1.1). `legacy`, el de siempre: siglas,
203
+ sub-diarios, correlativos y el documento comodín de la detracción, lo que importan CONCAR y los de su familia.
204
+ `neutral`: las líneas del estándar sin nada de eso, por `rol` y código SUNAT. Es el de `asiento_neutral`, la salida para
205
+ un ERP nuevo, que parte del estándar en vez de reimplementar el IGV. Un driver neutral es de canal `intercambio`, no
206
+ declara claves legacy en su configuración y el núcleo solo le exige la cuenta. La contabilidad es la misma que la de
207
+ CONCAR: `tests/test_driver_asiento_neutral.py` compara cuentas, sentidos, importes y roles línea a línea.
208
+
209
+ Y **declara qué exige** (`EXIGE`): lo que ese ERP no puede importar sin y que el núcleo, si no se lo dicen, deja pasar
210
+ —`centro_costo` en las cuentas que lo llevan, `moneda` con código en el destino; en uno de registro, `centro_costo` y
211
+ `cuenta_unica`—. La cuenta contable la exige el núcleo a todo driver que lleva cuentas, y la equivalencia del tipo, de
212
+ la que sale el sub-diario, a los de asientos. Con eso `diagnosticar` decide si un mes está listo **para ese destino**,
213
+ y el pipeline lo hace cumplir antes de armar nada. Lo que su formato no puede llevar —una moneda, un código más largo
214
+ que su columna— lo declara en `no_caben`: `diagnosticar` lo dice antes y el pipeline se niega con `NoCabe`, en toda
215
+ forma que lleva cuentas.
216
+
217
+ Y **declara lo que se configura**: las claves de su sección (`CONFIGURACION`) y en qué columnas de su archivo puede ir
218
+ un dato (`COLUMNAS_ELEGIBLES`). La configuración se guarda con lo general en la raíz y una sección por sistema, se
219
+ valida entera y el núcleo recibe lo general con la sección del destino encima. Un driver solo lee lo que declara, y el
220
+ núcleo solo lo general, lo del asiento y `MONEDAS_CODIGO`: lo vigila `tests/test_contrato_drivers.py`. Lo que todos los
221
+ drivers comparten para escribir —las opciones, el formato de texto, las celdas y el libro de Excel— está en
222
+ `drivers/kit/`.
223
+
224
+ Se publica de dos maneras:
225
+
226
+ - **Como paquete propio**, por entry points (`[project.entry-points."contaperu.drivers"]`). El registro lo busca la
227
+ primera vez que se consulta; los de serie ganan ante un nombre repetido y uno que no cumple el contrato se ignora con
228
+ un `AvisoDriver` en vez de tumbar el registro.
229
+ - **Dentro del repositorio**, en `drivers/<sistema>/` y en `DE_SERIE`. Para eso hace falta un archivo real que ese ERP
230
+ haya aceptado, salvo en un driver cuyo formato es el propio estándar, como `asiento_neutral`: lo valida su esquema.
231
+
232
+ Esté donde esté, `tests/test_contrato_drivers.py` lo examina: cumple el contrato, declara su canal y exporta el golden
233
+ de compras con el asiento cuadrado.
234
+
235
+ ### STARSOFT: qué se sabe y qué falta
236
+
237
+ - **Qué se sabe.** STARSOFT importa asientos, y su API lista seis endpoints sin esquema publicado. El contrato v1 ya
238
+ cubre lo que un driver así necesita: canal `legacy`, forma `desde_lineas` con el índice y la cabecera de cada
239
+ comprobante, un cuerpo que no es un Excel y un `no_caben` propio. Lo prueba un driver de mentira,
240
+ `tests/drivers_de_prueba/diario_json.py`, enchufado por entry points.
241
+ - **Qué falta.** Un archivo o una respuesta de su API que STARSOFT haya aceptado, y el esquema de su cuerpo; su libro
242
+ «Standar» pediría enmendar `libro.tipo` en el estándar. Hasta entonces no se publica ningún driver ni esqueleto: uno
243
+ dentro del paquete quedaría congelado por SemVer sin haber importado nada.
244
+ - **Si se escribe en su API y no en un archivo**, el envío y los reintentos son de la aplicación, no del motor: la
245
+ identidad y la huella de cada comprobante (`_exportacion.comprobantes`) son la clave de idempotencia. Lo que le falte
246
+ a la línea va en la cabecera, no en la huella.
247
+
248
+ ### Lo que queda preparado, sin símbolo público
249
+
250
+ | Punto | Qué hay en la 1.0 | Hito |
251
+ |---|---|---|
252
+ | Lectores de terceros | nombre reservado `lectores.contrato` y grupo `contaperu.lectores`: identificar, extraer, deduplicar | B7 |
253
+ | El banco | paquete reservado `contaperu/banco/` en la capa núcleo; los conectores, fuera del paquete | D1-D7 |
254
+ | Reglas de SUNAT como datos | `_datos.leer_json("datos/sunat/…")`; `catalogos` las cargará sin cambiar sus nombres | C1-C2 |
255
+ | CDR y no domiciliados | `lectores/cdr.py` reservado; los roles pueden crecer sin romper un driver | C3, C12 |
256
+ | Otra jurisdicción | J0 como test, J1 como efecto de las capas, `jurisdicciones/pe` reservado, `open-accounting` 1.1 para J5 | J0-J6 |
257
+ | La API de un ERP moderno | canal `api_erp` reservado y rechazado | A5 |
258
+
259
+ ## La capa para agentes
260
+
261
+ `diagnosticar` es la operación pensada para que un agente —o una persona con prisa— pregunte **antes**
262
+ de exportar y reciba, en una sola respuesta: si el mes está listo y por qué no, qué bloquea y qué avisa
263
+ por serie-número, qué falta para el destino (cuenta, centro de costo, tipos sin sigla, monedas,
264
+ correlativos, un reparto que no suma la base o que el destino no admite, y lo que no cabe en su formato: la tabla
265
+ es `asiento.FALTAS`), qué detracciones esperan constancia, el resumen por contraparte y desde qué correlativo
266
+ arranca cada sub-diario. No corrige ni inventa: describe. Está en la fachada, en el MCP y en la CLI, y
267
+ no añade ninguna regla contable —reúne comprobaciones que ya existían y las cuenta en vez de lanzarlas.
268
+
269
+ Desde la 0.8 responde además **para qué destino** (`exige`) y **a quién pedir** lo que falta
270
+ (`que_falta[].pedir_a`: `contador` si se resuelve mirando el documento o el plan de cuentas; `sistema`
271
+ si es configuración del destino o un dato público que no está en el papel; `proveedor` reservado). Y
272
+ cada exportación deja su **huella** (`_exportacion.huella`, `asiento/huella.py`): la misma
273
+ exportación repetida lleva la misma, que es lo que permite avisar de que ese contenido ya salió.
274
+
275
+ Esa es la forma en que crecerá esta capa: **cada pregunta de un agente se responde con reglas que ya
276
+ tienen fuente**, nunca con una regla nueva escrita para el agente.
277
+
278
+ ## Lo que no se negocia
279
+
280
+ 1. **Ninguna regla contable sin fuente.** La norma, la resolución o el archivo real que la justifica va
281
+ al lado, en el código. Un refactor mueve reglas; no las escribe.
282
+ 2. **El Excel de CONCAR validado no cambia.** `test_snapshot_concar.py` lo vigila celda a celda.
283
+ 3. **Un tipo sin sigla detiene la exportación.** Nunca se inventa una sigla.
284
+ 4. **Sin red, sin disco, sin estado, sin reloj en el núcleo.** `test_frontera.py` y `conftest.py`.
285
+ 5. **`Decimal` de punta a punta**, `float` solo en el borde de escritura del archivo.
286
+ 6. **Nunca datos reales en el repositorio.** Dos RUC seguros: `20131312955` y `20601234567`.
287
+ 7. **Código y comentarios en español**, porque es el idioma del dominio.
288
+
289
+ ## Hoja de ruta
290
+
291
+ El orden en que crece el motor, y el dato o el código que destraba cada paso, viven en
292
+ [HOJA-DE-RUTA.md](HOJA-DE-RUTA.md): una fase para afinar lo que existe y seis frentes —los drivers de SISCONT y
293
+ STARSOFT, la puerta para que cualquier ERP integre el motor, leer y validar más con las reglas oficiales de SUNAT, el
294
+ banco como punto de partida del proceso contable, un estándar que mejora siempre y la puerta a otra jurisdicción,
295
+ condicionada a un cliente real fuera del Perú—, cada uno con sus hitos y su criterio de salida. Aquí no se repiten. La
296
+ investigación y el diseño de cada pieza, ordenados por el ciclo contable y con el de EE. UU. como referencia, están en
297
+ [INTEROPERABILIDAD.md](INTEROPERABILIDAD.md), y lo tomado de las APIs de EE. UU., en [REFERENCIAS.md](REFERENCIAS.md).
298
+
299
+ La idea que la ordena sigue siendo la de esta arquitectura: primero la compatibilidad con los sistemas que ya existen,
300
+ después un lenguaje común (`open-accounting`, que crece con casos reales detrás) y, encima, más preguntas de agentes
301
+ respondidas con reglas que ya tienen fuente.