208 lines
7.2 KiB
Python
208 lines
7.2 KiB
Python
"""
|
|
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()
|