fix(facturapi): configure legal data after org creation/reuse to enable Live mode
Some checks failed
CI / lint-and-test (3.11) (push) Has been cancelled
CI / lint-and-test (3.13) (push) Has been cancelled

This commit is contained in:
2026-07-09 07:25:07 +00:00
parent 9b88b8ba4c
commit b2f23ef3cf
4 changed files with 110 additions and 31 deletions

View File

@@ -206,7 +206,7 @@ def find_organization_by_rfc(tenant_config: dict) -> dict | None:
def create_organization(tenant_config: dict) -> dict:
"""Create a new Facturapi organization for the tenant and return live key.
"""Create or reuse a Facturapi organization, configure legal data and return a live key.
Requires FACTURAPI_USER_KEY env or a user key (sk_user_*) in tenant_config.
Uses tenant RFC/razon_social if available.
@@ -227,13 +227,56 @@ def create_organization(tenant_config: dict) -> dict:
if not org_id:
raise FacturapiError("Could not create organization: no id returned")
# Configure fiscal/legal data (required before Live mode can be used)
try:
update_organization_legal(tenant_config, org_id)
except FacturapiError:
# If legal update fails, still return org info so the caller can surface it,
# but do not generate a live key because it would be unusable.
return {"org_id": org_id, "api_key": None, "legal_updated": False}
# Generate live secret key
key_resp = _request("PUT", f"/organizations/{org_id}/apikeys/live", user_key, json_payload={}, timeout=60)
live_key = key_resp.get("key") if isinstance(key_resp, dict) else str(key_resp)
if not live_key:
raise FacturapiError(f"Could not generate live key for org {org_id}")
return {"org_id": org_id, "api_key": live_key}
return {"org_id": org_id, "api_key": live_key, "legal_updated": True}
def _build_legal_payload(tenant_config: dict) -> dict:
"""Build Facturapi /organizations/{id}/legal payload from tenant config."""
rfc = (tenant_config.get("rfc") or "").upper().strip()
legal_name = (tenant_config.get("razon_social") or "").strip()
tax_system = (tenant_config.get("regimen_fiscal") or "").strip() or "601"
zip_code = (tenant_config.get("cp") or tenant_config.get("tenant_cp") or "").strip() or "00000"
if not rfc or not legal_name:
raise FacturapiError("RFC y Razón Social son obligatorios para configurar la organización en Facturapi")
return {
"tax_id": rfc,
"legal_name": legal_name,
"tax_system": tax_system,
"address": {
"zip": zip_code,
"street": (tenant_config.get("direccion") or "").strip(),
"exterior": "",
"interior": "",
"neighborhood": "",
"city": "",
"municipality": "",
"state": "",
},
}
def update_organization_legal(tenant_config: dict, org_id: str) -> dict:
"""Update the legal/fiscal data of a Facturapi organization.
Uses the user key (admin) because the org may not have a live key yet.
"""
user_key = _get_user_key_for_tenant(tenant_config)
payload = _build_legal_payload(tenant_config)
return _request("PUT", f"/organizations/{org_id}/legal", user_key, json_payload=payload, timeout=60)
def get_org_status(tenant_config: dict) -> dict:
@@ -264,21 +307,31 @@ def get_org_status(tenant_config: dict) -> dict:
result["has_org_id"] = True
result["org_id"] = org_id
try:
def _fetch():
org = get_organization(org_id, api_key)
legal = org.get("legal", {})
cert = org.get("certificate", {})
result.update(
{
"configured": True,
"has_csd": bool(cert.get("has_certificate")),
"legal_name": legal.get("name") or legal.get("legal_name"),
"tax_id": legal.get("tax_id"),
"pending_steps": org.get("pending_steps", []),
}
)
return {
"configured": True,
"has_csd": bool(cert.get("has_certificate")),
"legal_name": legal.get("name") or legal.get("legal_name"),
"tax_id": legal.get("tax_id"),
"pending_steps": org.get("pending_steps", []),
}
try:
result.update(_fetch())
except FacturapiError as e:
result["error"] = str(e)
# If the org reports "not configured for Live" (401) and we have a user key,
# try to backfill legal data and retry.
if e.status_code == 401 and _get_user_key_for_tenant(tenant_config):
try:
update_organization_legal(tenant_config, org_id)
result.update(_fetch())
except FacturapiError as e2:
result["error"] = str(e2)
else:
result["error"] = str(e)
return result