Facturapi: fix legal payload (name/legal_name/tax_system, address, no tax_id), reuse local org, status via user key; add fiscal address fields; embed Carta Manifiesto iframe
This commit is contained in:
@@ -216,11 +216,27 @@ def create_organization(tenant_config: dict) -> dict:
|
||||
rfc = (tenant_config.get("rfc") or "").upper().strip()
|
||||
name = tenant_config.get("razon_social") or tenant_config.get("name") or rfc or "Nexus"
|
||||
|
||||
# First try to find existing org by RFC
|
||||
existing = find_organization_by_rfc(tenant_config) if rfc else None
|
||||
if existing:
|
||||
org_id = existing["id"]
|
||||
else:
|
||||
# 1) Reuse the organization already stored locally, if it still exists in Facturapi.
|
||||
local_org_id = _get_org_id(tenant_config)
|
||||
if local_org_id:
|
||||
try:
|
||||
get_organization(local_org_id, user_key)
|
||||
org_id = local_org_id
|
||||
except FacturapiError:
|
||||
local_org_id = None
|
||||
|
||||
# 2) Try to find an existing organization by RFC (legacy / Horux-style lookup).
|
||||
if not local_org_id and rfc:
|
||||
existing = find_organization_by_rfc(tenant_config)
|
||||
if existing:
|
||||
org_id = existing["id"]
|
||||
else:
|
||||
payload = {"name": name}
|
||||
org = _request("POST", "/organizations", user_key, json_payload=payload, timeout=60)
|
||||
org_id = org.get("id")
|
||||
if not org_id:
|
||||
raise FacturapiError("Could not create organization: no id returned")
|
||||
elif not local_org_id:
|
||||
payload = {"name": name}
|
||||
org = _request("POST", "/organizations", user_key, json_payload=payload, timeout=60)
|
||||
org_id = org.get("id")
|
||||
@@ -245,27 +261,49 @@ def create_organization(tenant_config: dict) -> dict:
|
||||
|
||||
|
||||
def _build_legal_payload(tenant_config: dict) -> dict:
|
||||
"""Build Facturapi /organizations/{id}/legal payload from tenant config."""
|
||||
"""Build Facturapi /organizations/{id}/legal payload from tenant config.
|
||||
|
||||
Facturapi expects `name`, `legal_name`, `tax_system` and an `address` object
|
||||
with at least `street` and `exterior` non-empty. `tax_id` is not accepted
|
||||
by this endpoint (it is set from the CSD or from the organization profile).
|
||||
"""
|
||||
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"
|
||||
zip_code = (tenant_config.get("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")
|
||||
|
||||
name = legal_name or rfc or "Nexus"
|
||||
street = (tenant_config.get("direccion") or "").strip() or "No especificada"
|
||||
exterior = (
|
||||
(tenant_config.get("exterior") or "").strip()
|
||||
or (tenant_config.get("numero_exterior") or "").strip()
|
||||
or "S/N"
|
||||
)
|
||||
|
||||
address = {
|
||||
"zip": zip_code,
|
||||
"street": street,
|
||||
"exterior": exterior,
|
||||
}
|
||||
|
||||
optional_address_fields = {
|
||||
"interior": (tenant_config.get("interior") or tenant_config.get("numero_interior") or "").strip(),
|
||||
"neighborhood": (tenant_config.get("colonia") or "").strip(),
|
||||
"city": (tenant_config.get("ciudad") or "").strip(),
|
||||
"municipality": (tenant_config.get("municipio") or "").strip(),
|
||||
"state": (tenant_config.get("estado") or "").strip(),
|
||||
}
|
||||
for key, value in optional_address_fields.items():
|
||||
if value:
|
||||
address[key] = value
|
||||
|
||||
return {
|
||||
"tax_id": rfc,
|
||||
"name": name,
|
||||
"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": "",
|
||||
},
|
||||
"address": address,
|
||||
}
|
||||
|
||||
|
||||
@@ -279,6 +317,22 @@ def update_organization_legal(tenant_config: dict, org_id: str) -> dict:
|
||||
return _request("PUT", f"/organizations/{org_id}/legal", user_key, json_payload=payload, timeout=60)
|
||||
|
||||
|
||||
def _get_status_key(tenant_config: dict) -> str | None:
|
||||
"""Return the best key for read-only organization status checks.
|
||||
|
||||
Prefer the Facturapi user key because the live secret key cannot read
|
||||
organization metadata until the org is production-ready.
|
||||
"""
|
||||
user = _get_user_key()
|
||||
if user:
|
||||
return user
|
||||
for key in ("facturapi_key", "cfdi_facturapi_key"):
|
||||
tenant_key = (tenant_config.get(key) or "").strip()
|
||||
if tenant_key.startswith("sk_user_"):
|
||||
return tenant_key
|
||||
return _get_secret_key(tenant_config)
|
||||
|
||||
|
||||
def get_org_status(tenant_config: dict) -> dict:
|
||||
result = {
|
||||
"configured": False,
|
||||
@@ -292,12 +346,12 @@ def get_org_status(tenant_config: dict) -> dict:
|
||||
"error": None,
|
||||
}
|
||||
|
||||
try:
|
||||
api_key = get_api_key(tenant_config)
|
||||
result["has_key"] = True
|
||||
except FacturapiError as e:
|
||||
result["error"] = str(e)
|
||||
has_secret = bool(_get_secret_key(tenant_config))
|
||||
has_user = bool(_get_user_key_for_tenant(tenant_config))
|
||||
if not has_secret and not has_user:
|
||||
result["error"] = "Facturapi not configured. Set FACTURAPI_USER_KEY env or tenant_config.facturapi_secret_key"
|
||||
return result
|
||||
result["has_key"] = True
|
||||
|
||||
org_id = _get_org_id(tenant_config)
|
||||
if not org_id:
|
||||
@@ -307,6 +361,11 @@ def get_org_status(tenant_config: dict) -> dict:
|
||||
result["has_org_id"] = True
|
||||
result["org_id"] = org_id
|
||||
|
||||
api_key = _get_status_key(tenant_config)
|
||||
if not api_key:
|
||||
result["error"] = "No Facturapi key available"
|
||||
return result
|
||||
|
||||
def _fetch():
|
||||
org = get_organization(org_id, api_key)
|
||||
legal = org.get("legal", {})
|
||||
|
||||
Reference in New Issue
Block a user