Reemplaza logging por consola/archivo, agrega historial de intentos y soporte para notas de crédito

- Nuevo logger_manager.py (reemplaza logger_utilities.py): logging con formato de árbol,
  rotación diaria a sunat.log, y consola por stdout (antes iba por stderr y se duplicaba
  en logs/error.log del launchd).
- Nuevo historial_manager.py: registra cada intento de consulta/descarga en historial.db
  para auditoría, con limpieza automática de registros de más de 180 días.
- consulta_cpe.py: corrige el id de notas de crédito/débito (usa F7/F8, no 07/08) y
  agrega reintentos ante errores 5xx.
- hana_manager.py: suma consulta a ORPC para notas de crédito (Indicator 07) y soporte
  de ObjType para actualizar la tabla correcta (OPCH/ORPC) al marcar PDF/XML descargado.
- gestor_fallas.py / main.py: propagan objtype end-to-end y dejan constancia de cada
  intento en historial_manager.
- Elimina login_api.py y reporte_fallas.py (código no usado / script manual sin uso reciente).
- Actualiza .gitignore (sunat-venv/, historial.db*) y docs/ para reflejar el estado real
  del proyecto.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
GeorgeandClaude Sonnet 5 committed 2026-08-31 12:31:48 -05:00
1 parent 99a587c4da
commit c2503745bf
18 files changed
+647 -862

No files matched your search

+10 -4
View File
@@ -16,12 +16,18 @@ def init_db():
tipo TEXT NOT NULL,
serie TEXT NOT NULL,
numero TEXT NOT NULL,
objtype INTEGER NOT NULL DEFAULT 18,
error TEXT NOT NULL,
intentos INTEGER NOT NULL DEFAULT 1,
proximo_intento TEXT,
fecha_ultimo TEXT NOT NULL
)
""")
columnas = {row[1] for row in conn.execute("PRAGMA table_info(fallas)")}
if "obj_type" in columnas and "objtype" not in columnas:
conn.execute("ALTER TABLE fallas RENAME COLUMN obj_type TO objtype")
elif "objtype" not in columnas:
conn.execute("ALTER TABLE fallas ADD COLUMN objtype INTEGER NOT NULL DEFAULT 18")
# ============================================================================
def _calcular_proximo_intento(intentos):
if intentos >= 20:
@@ -44,7 +50,7 @@ def _calcular_proximo_intento(intentos):
delta = timedelta(hours=12)
return (datetime.now() + delta).isoformat()
# ============================================================================
def registrar_falla(docentry, ruc, tipo_cpe, serie, numero, error, intentos=None):
def registrar_falla(docentry, ruc, tipo_cpe, serie, numero, obj_type, error, intentos=None):
ahora = datetime.now().isoformat()
if intentos is None:
with _conn() as conn:
@@ -56,9 +62,9 @@ def registrar_falla(docentry, ruc, tipo_cpe, serie, numero, error, intentos=None
with _conn() as conn:
conn.execute("""
INSERT OR REPLACE INTO fallas
(docentry, ruc, tipo, serie, numero, error, intentos, proximo_intento, fecha_ultimo)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (docentry, ruc, tipo_cpe, serie, numero, str(error)[:200], intentos, proximo, ahora))
(docentry, ruc, tipo, serie, numero, objtype, error, intentos, proximo_intento, fecha_ultimo)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (docentry, ruc, tipo_cpe, serie, numero, obj_type, str(error)[:200], intentos, proximo, ahora))
# ============================================================================
def eliminar_falla(docentry):
with _conn() as conn: