""" Demo VeriFactu — flujo desde el front ====================================== Simula lo que haría el front al pulsar "Registrar": 1. Login en el backend (dolibarr-bff) para obtener JWT 2. Consulta el token VeriFactu almacenado en el backend 3. Envía una factura en formato Dolibarr directamente a VerifactuMidAPI Uso: python demo.py python demo.py --backend http://localhost:5269 --api http://localhost:6789 python demo.py --usuario admin --password 12345678 """ import sys import json import argparse import urllib.request import urllib.error # ── Configuración por defecto ────────────────────────────────────────────────── BACKEND_URL = "http://localhost:5269" API_URL = "http://localhost:6789" USUARIO = "admin" PASSWORD = "12345678" # ── Factura de ejemplo ───────────────────────────────────────────────────────── # Mismo formato que el front recibe del backend (formato Dolibarr). # Los campos emisor.nif y emisor.nombre los rellena la API desde el certificado. FACTURA = { "format": "dolibarr", "invoice": { "number": "FAC-DEMO-001", "date": "2026-05-29", "notePublic": "Servicios de consultoría tecnológica", "lines": [ { "description": "Desarrollo integración VeriFactu", "quantity": 1, "unitPrice": 121.00, "taxRate": 21 }, { "description": "Soporte técnico mensual", "quantity": 2, "unitPrice": 60.50, "taxRate": 21 } ] }, "client": { "name": "Empresa Cliente S.L.", "vatNumber": "B12345678" }, "emisor": { "nif": "", "nombre": "" }, "sistema": { "nombre": "BYolivia Suite", "nif_proveedor": "B87654321", "version": "1.0.0" } } # ── Helpers ──────────────────────────────────────────────────────────────────── def _color(text, code): return f"\033[{code}m{text}\033[0m" def ok(msg): print(_color(f" ✓ {msg}", "32")) def err(msg): print(_color(f" ✗ {msg}", "31")) def info(msg): print(_color(f" → {msg}", "36")) def titulo(msg): print(_color(f"\n{'─'*60}\n {msg}\n{'─'*60}", "34")) def hacer_request(method, url, headers=None, body=None): data = json.dumps(body).encode() if body else None req = urllib.request.Request(url, data=data, headers=headers or {}, method=method) if data: req.add_header("Content-Type", "application/json") try: with urllib.request.urlopen(req, timeout=10) as r: return r.status, json.loads(r.read()) except urllib.error.HTTPError as e: try: body = json.loads(e.read()) except Exception: body = {"error": e.reason} return e.code, body except Exception as e: return None, str(e) # ── Pasos ────────────────────────────────────────────────────────────────────── def paso1_login(backend, usuario, password): titulo("PASO 1 — Login en el backend (dolibarr-bff)") url = f"{backend}/api/auth/login" info(f"POST {url}") info(f"Usuario: {usuario}") status, data = hacer_request("POST", url, body={"username": usuario, "password": password}) if status is None: err(f"Error de red: {data}") return None print(f"\n HTTP {status}") print(" " + json.dumps(data, indent=4, ensure_ascii=False).replace("\n", "\n ")) token = data.get("token") or data.get("Token") if status != 200 or not token: err("Login fallido. Comprueba usuario, contraseña y que el backend esté arrancado.") return None ok("Login correcto. JWT obtenido.") return token def paso2_obtener_token_verifactu(backend, jwt): titulo("PASO 2 — Consultar token VeriFactu al backend") url = f"{backend}/api/verifactu/token" info(f"GET {url}") status, data = hacer_request("GET", url, headers={"Authorization": f"Bearer {jwt}"}) if status is None: err(f"Error de red: {data}") return None print(f"\n HTTP {status}") print(" " + json.dumps(data, indent=4, ensure_ascii=False).replace("\n", "\n ")) token = data.get("token") or data.get("Token") if status != 200 or not token: err("No se pudo obtener el token VeriFactu. ¿Está el certificado registrado?") return None ok("Token VeriFactu obtenido.") return token def paso3_mostrar_factura(): titulo("PASO 3 — Factura preparada (formato Dolibarr)") info("Misma estructura que el front recibe del backend.") print() print(" " + json.dumps(FACTURA, indent=4, ensure_ascii=False).replace("\n", "\n ")) def paso4_enviar_factura(api_url, token): titulo("PASO 4 — Enviar factura a VerifactuMidAPI") url = f"{api_url}/api/v1/facturas" info(f"POST {url}") info(f"Token: {token[:40]}…" if len(token) > 40 else f"Token: {token}") status, data = hacer_request( "POST", url, headers={"Authorization": f"Bearer {token}"}, body=FACTURA ) if status is None: err(f"Error de red: {data}") return print(f"\n HTTP {status}") if data.get("success"): ok("Factura procesada correctamente por la API.") if data.get("csv"): ok(f"Hash de cadena: {data['csv']}") else: err(f"La API devolvió error: {data.get('error', 'desconocido')}") # ── Main ─────────────────────────────────────────────────────────────────────── def main(): parser = argparse.ArgumentParser(description="Demo flujo VeriFactu") parser.add_argument("--backend", default=BACKEND_URL, help="URL del backend dolibarr-bff") parser.add_argument("--api", default=API_URL, help="URL de VerifactuMidAPI") parser.add_argument("--usuario", default=USUARIO, help="Usuario del backend") parser.add_argument("--password", default=PASSWORD, help="Contraseña del backend") args = parser.parse_args() print(_color("\n VeriFactu Demo — flujo front → API", "1;37")) print(_color(" Simula lo que haría el front al pulsar 'Registrar'\n", "2;37")) # Paso 1: login → JWT jwt = paso1_login(args.backend, args.usuario, args.password) if not jwt: sys.exit(1) # Paso 2: token VeriFactu del backend token_verifactu = paso2_obtener_token_verifactu(args.backend, jwt) if not token_verifactu: sys.exit(1) # Paso 3: mostrar la factura paso3_mostrar_factura() # Paso 4: enviar a la API paso4_enviar_factura(args.api, token_verifactu) print() if __name__ == "__main__": main()