Files

117 lines
4.3 KiB
Python
Raw Permalink Normal View History

import re
from typing import Any, Dict, Optional
from core.errors import SAPServiceLayerError
from core.sap import SAPServiceLayerClient
from domain.rut import formatear_rut, normalizar_rut, validar_rut
RUT_PATTERN = re.compile(r"^\d{1,2}\.?\d{3}\.?\d{3}-?[\dkK]$")
BUSINESS_PARTNER_GROUP_CODE = 100 # "Clientes", mismo grupo que usa el CardCode genérico
def _nombre_completo(persona: Dict[str, Any]) -> str:
nombre = f"{persona.get('first_name', '') or ''} {persona.get('last_name', '') or ''}".strip()
return nombre or persona.get("name", "") or ""
def buscar_rut_en_order(order: Dict[str, Any]) -> Optional[str]:
"""
Busca el RUT en note_attributes de la orden, donde algunas tiendas lo
capturan en el checkout (ej. {"name": "rut", "value": "11111109-K"}).
No requiere llamada extra a Shopify: ya viene en el detalle de la orden.
"""
for attr in order.get("note_attributes") or []:
nombre = str(attr.get("name", "")).strip().lower()
if "rut" not in nombre:
continue
valor = str(attr.get("value", "")).strip()
if RUT_PATTERN.match(valor) and validar_rut(valor):
return normalizar_rut(valor)
return None
def _bp_address(tipo: str, nombre: str, direccion: Dict[str, Any]) -> Dict[str, Any]:
address = {
"AddressName": nombre,
"AddressType": tipo,
"Street": direccion.get("address1"),
"Block": direccion.get("address2"),
"City": direccion.get("city"),
"ZipCode": direccion.get("zip"),
"County": direccion.get("province"),
}
if direccion.get("country_code") == "CL":
address["Country"] = "CL"
if tipo == "bo_ShipTo":
address["TaxCode"] = "IVA"
return address
def crear_business_partner(sap: SAPServiceLayerClient, rut_sap: str, order: Dict[str, Any]) -> str:
"""
Crea un BusinessPartner (cliente) nuevo para este RUT. CardCode = "C" +
rut_sap. Devuelve el CardCode creado.
"""
billing = order.get("billing_address") or {}
shipping = order.get("shipping_address") or {}
customer = order.get("customer") or {}
card_code = f"C{rut_sap}"
payload = {
"CardCode": card_code,
"CardName": _nombre_completo(billing) or _nombre_completo(customer) or "Cliente Shopify",
"CardType": "cCustomer",
"GroupCode": BUSINESS_PARTNER_GROUP_CODE,
"FederalTaxID": rut_sap,
"Phone1": billing.get("phone") or customer.get("phone"),
"EmailAddress": order.get("email") or order.get("contact_email"),
"BPAddresses": [
_bp_address("bo_BillTo", "BillTo", billing),
_bp_address("bo_ShipTo", "ShipTo", shipping or billing),
],
}
sap.forward("POST", "BusinessPartners", json_data=payload)
return card_code
def resolver_card_code(
sap: SAPServiceLayerClient,
store_config: Dict[str, Any],
rut: Optional[str],
order: Optional[Dict[str, Any]] = None,
) -> Dict[str, Any]:
"""
Si hay un RUT válido, busca el BusinessPartner en SAP por FederalTaxID
(con guión, que es el formato que usa SAP: "NNNNNNNN-D"). Si existe,
devuelve su CardCode. Si no existe, crea uno nuevo (CardCode "C"+rut).
Si no hay RUT, o falla la creación, usa el CardCode genérico
configurado para la tienda.
Siempre devuelve "rut_sap" (el RUT en formato SAP) cuando hay RUT, para
poder sobrescribir el FederalTaxID del documento aunque se use el
CardCode genérico.
"""
generic_card_code = store_config["sap"]["generic_card_code"]
if not rut:
return {"card_code": generic_card_code, "rut": None, "rut_sap": None, "encontrado_por_rut": False}
rut_sap = formatear_rut(rut)
resp = sap.forward(
"GET",
"BusinessPartners",
params={"$select": "CardCode", "$filter": f"FederalTaxID eq '{rut_sap}'"},
)
resultados = resp.json().get("value", [])
if resultados:
return {"card_code": resultados[0]["CardCode"], "rut": rut, "rut_sap": rut_sap, "encontrado_por_rut": True}
try:
card_code = crear_business_partner(sap, rut_sap, order or {})
return {"card_code": card_code, "rut": rut, "rut_sap": rut_sap, "encontrado_por_rut": True}
except SAPServiceLayerError:
return {"card_code": generic_card_code, "rut": rut, "rut_sap": rut_sap, "encontrado_por_rut": False}