"""Persistencia a disco (JSON) de resultados y drafts.""" import os import re import json from datetime import datetime import sunat_schema import pdf_utilities from pipeline_utils import log # ============================================================================ def guardar_resultado(documentos: list[dict], datos_pdf: list[dict], output_folder: str) -> list[str]: """ Guarda los resultados XML y PDF en archivos JSON separados. El nombre del archivo sigue el patrón ``{tipo}_resultado_{serie}-{numero}_{timestamp}.json`` y se escribe en ``output_folder``. Args: documentos: Lista de diccionarios con datos extraídos de XML (producida por ``procesar_xmls``). datos_pdf: Lista de diccionarios con datos extraídos de PDF (producida por ``procesar_pdfs``). Example: Si ``documentos[0]`` tiene ``serie="F002", numero="486"``:: guardar_resultado(documentos, pdf_datos, output_folder) # → Crea xml_resultado_F002-486_2026-07-08_12-00-00.json # → Crea pdf_resultado_F002-486_2026-07-08_12-00-00.json """ if not documentos: return [] if not os.path.exists(output_folder): os.makedirs(output_folder, exist_ok=True) ts = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") serie = documentos[0].get("serie", "unknown") numero = documentos[0].get("numero", "unknown") base = f"{serie}-{numero}_{ts}" saved: list[str] = [] # XML result xml_name = f"xml_resultado_{base}.json" xml_path = os.path.join(output_folder, xml_name) with open(xml_path, "w", encoding="utf-8") as f: json.dump(documentos, f, ensure_ascii=False, indent=4) log(f"💾 XML guardado: {xml_name}") saved.append(xml_path) # PDF result (normalizado al mismo schema que XML) pdf_docs = [] for raw in datos_pdf: if raw.get("error"): continue td = (raw.get("tipo_documento") or "").upper() tipo = pdf_utilities.TIPO_MAP.get(td) if not tipo: continue sn = raw.get("serie_numero") or "" serie_p, num_p = sunat_schema.split_serie_numero(sn) if not serie_p: continue doc = { "ruc_emisor": raw.get("ruc_emisor"), "razon_social_emisor": None, # el PDF no extrae la razón social del emisor "fecha_emision": raw.get("fecha_emision"), "tipo": tipo, "serie": serie_p, "numero": num_p, "ruc_receptor": raw.get("ruc_receptor"), "razon_social_receptor": raw.get("nombre_receptor"), "total_factura": raw.get("monto_total"), "documentos_referencia": [], "tiene_detraccion": False, "codigo_detraccion": None, "porcentaje_detraccion": None, "monto_detraccion": None, "lineas": [], "ruta_pdf": raw.get("archivo"), } det = raw.get("detraccion") or {} if det.get("tiene"): doc["tiene_detraccion"] = True doc["porcentaje_detraccion"] = det.get("porcentaje") doc["monto_detraccion"] = det.get("monto") for item in raw.get("items") or []: linea = { "descripcion": item.get("descripcion", ""), "cantidad": item.get("cantidad"), "precio_unitario": item.get("precio_unitario"), "total_linea": item.get("total"), "guias_remision": [], "guia_transportista": None, } for g in item.get("guias_remision") or []: if isinstance(g, str): m = re.search(r"\b([TVE][A-Z0-9]{3,4})\s*-?\s*(\d+)", g) if m: linea["guias_remision"].append({ "tipo": "09", "serie": m.group(1).upper(), "numero": m.group(2).lstrip("0") or "0", }) elif isinstance(g, dict): linea["guias_remision"].append(dict(g)) gt = item.get("guia_transportista") if gt: if isinstance(gt, str): m = re.search(r"\b([TVE][A-Z0-9]{3,4})\s*-?\s*(\d+)", gt) if m: linea["guia_transportista"] = { "tipo": "31", "serie": m.group(1).upper(), "numero": m.group(2).lstrip("0") or "0", } elif isinstance(gt, dict): linea["guia_transportista"] = dict(gt) doc["lineas"].append(linea) pdf_docs.append(doc) if pdf_docs: pdf_name = f"pdf_resultado_{base}.json" pdf_path = os.path.join(output_folder, pdf_name) with open(pdf_path, "w", encoding="utf-8") as f: json.dump(pdf_docs, f, ensure_ascii=False, indent=4) log(f"💾 PDF guardado: {pdf_name}") saved.append(pdf_path) return saved # ============================================================================ def guardar_draft_json(draft: dict, serie: str, numero: str, output_folder: str) -> None: """ Guarda el payload del draft SAP B1 en un archivo JSON para depuración. El nombre sigue el patrón ``draft_sapb1_{serie}-{numero}_{timestamp}.json``. Args: draft: Payload del draft SAP B1 (producido por ``build_sap_draft``). serie: Serie de la factura (ej. ``"F002"``). numero: Número de la factura (ej. ``"486"``). Example: Draft de factura F002-486:: guardar_draft_json(draft, "F002", "486", output_folder) # → Crea draft_sapb1_F002-486_2026-07-08_12-00-00.json """ if not os.path.exists(output_folder): os.makedirs(output_folder, exist_ok=True) ts = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") filename = f"draft_sapb1_{serie}-{numero}_{ts}.json" with open(os.path.join(output_folder, filename), "w", encoding="utf-8") as f: json.dump(draft, f, ensure_ascii=False, indent=4) log(f"💾 Draft guardado: {filename}")