Import ClientFlow production v4928.1.5.132.4
This commit is contained in:
@@ -53,6 +53,27 @@ def normalize_fiscal_name(value: Any) -> str:
|
||||
return re.sub(r"\s+", " ", name).strip()
|
||||
|
||||
|
||||
def normalize_email(value: Any) -> str:
|
||||
"""Normaliza email para identidade lógica do cliente.
|
||||
|
||||
A criação de clientes deve tratar ``CLIENTE@EXEMPLO.COM`` e
|
||||
`` cliente@exemplo.com `` como a mesma ficha comercial.
|
||||
"""
|
||||
email = _clean(value).casefold()
|
||||
email = re.sub(r"\s+", "", email)
|
||||
return email
|
||||
|
||||
|
||||
def _validate_email_or_empty(value: Any) -> str:
|
||||
email = normalize_email(value)
|
||||
if not email:
|
||||
return ""
|
||||
# Validação deliberadamente simples: evita lixo óbvio sem bloquear emails válidos raros.
|
||||
if not re.match(r"^[^@\s]+@[^@\s]+\.[^@\s]+$", email):
|
||||
raise ValueError("Email inválido.")
|
||||
return email
|
||||
|
||||
|
||||
def ensure_commercial_schema() -> None:
|
||||
global _SCHEMA_READY
|
||||
if _SCHEMA_READY:
|
||||
@@ -83,6 +104,7 @@ def ensure_commercial_schema() -> None:
|
||||
WHERE tax_id IS NOT NULL AND tax_id <> ''
|
||||
"""))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_customers_name ON customers(name)"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_customers_email_normalized ON customers((lower(trim(email)))) WHERE email IS NOT NULL AND trim(email) <> ''"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_customers_jasmin_key ON customers(jasmin_customer_party_key)"))
|
||||
conn.execute(text("CREATE INDEX IF NOT EXISTS idx_customers_external_identity ON customers USING gin (metadata)"))
|
||||
|
||||
@@ -327,7 +349,7 @@ def upsert_customer(data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
params = {
|
||||
"name": name,
|
||||
"tax_id": tax_id or None,
|
||||
"email": _clean(data.get("email") or data.get("electronicMail")) or None,
|
||||
"email": _validate_email_or_empty(data.get("email") or data.get("electronicMail")) or None,
|
||||
"phone": _clean(data.get("phone") or data.get("telephone")) or None,
|
||||
"street_name": _clean(data.get("street_name") or data.get("streetName")) or None,
|
||||
"postal_zone": _clean(data.get("postal_zone") or data.get("postalZone")) or None,
|
||||
@@ -338,6 +360,37 @@ def upsert_customer(data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"metadata": _json(data.get("metadata") or {}),
|
||||
}
|
||||
with engine.begin() as conn:
|
||||
# Identidade forte por email quando não há NIF. Evita fichas duplicadas
|
||||
# por maiúsculas/espaços no email. O NIF continua a ter prioridade.
|
||||
if not params["tax_id"] and params["email"]:
|
||||
existing_by_email = conn.execute(text("""
|
||||
SELECT id::text, name, tax_id, email, phone, street_name, postal_zone,
|
||||
city_name, country, jasmin_customer_party_key, jasmin_customer_id,
|
||||
metadata, created_at, updated_at
|
||||
FROM customers
|
||||
WHERE lower(trim(email)) = :email
|
||||
ORDER BY updated_at DESC
|
||||
LIMIT 1
|
||||
"""), {"email": params["email"]}).mappings().first()
|
||||
if existing_by_email:
|
||||
row = conn.execute(text("""
|
||||
UPDATE customers
|
||||
SET name = COALESCE(NULLIF(:name, ''), name),
|
||||
phone = COALESCE(:phone, phone),
|
||||
street_name = COALESCE(:street_name, street_name),
|
||||
postal_zone = COALESCE(:postal_zone, postal_zone),
|
||||
city_name = COALESCE(:city_name, city_name),
|
||||
country = COALESCE(:country, country),
|
||||
jasmin_customer_party_key = COALESCE(:jasmin_customer_party_key, jasmin_customer_party_key),
|
||||
jasmin_customer_id = COALESCE(:jasmin_customer_id, jasmin_customer_id),
|
||||
metadata = COALESCE(metadata, '{}'::jsonb) || CAST(:metadata AS JSONB),
|
||||
updated_at = now()
|
||||
WHERE id = CAST(:id AS UUID)
|
||||
RETURNING id::text, name, tax_id, email, phone, street_name, postal_zone,
|
||||
city_name, country, jasmin_customer_party_key, jasmin_customer_id, metadata, created_at, updated_at
|
||||
"""), {**params, "id": existing_by_email["id"]}).mappings().first()
|
||||
return dict(row or {})
|
||||
|
||||
if params["tax_id"]:
|
||||
row = conn.execute(text("""
|
||||
INSERT INTO customers (
|
||||
@@ -758,7 +811,7 @@ def update_customer(customer_id: str, data: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"id": customer_id,
|
||||
"name": _clean(data.get("name")),
|
||||
"tax_id": normalize_tax_id(data.get("tax_id")),
|
||||
"email": _clean(data.get("email")) or None,
|
||||
"email": _validate_email_or_empty(data.get("email")) or None,
|
||||
"phone": _clean(data.get("phone")) or None,
|
||||
"street_name": _clean(data.get("street_name")) or None,
|
||||
"postal_zone": _clean(data.get("postal_zone")) or None,
|
||||
|
||||
Reference in New Issue
Block a user