194 lines
6.1 KiB
Python
194 lines
6.1 KiB
Python
"""Push export market entities into Foodlinkk CRM (clients + deals)."""
|
|
from __future__ import annotations
|
|
|
|
from typing import Any, Optional
|
|
|
|
from app.db import execute, execute_returning, fetch_one
|
|
from app.export_halal_scoring import score_entity
|
|
|
|
SECTOR_BY_TYPE: dict[str, str] = {
|
|
"distributor": "Export · Distributie",
|
|
"wholesaler": "Export · Groothandel",
|
|
"importer": "Export · Import",
|
|
"logistics": "Export · Logistiek",
|
|
"contract_caterer": "Export · Catering",
|
|
"restaurant": "Export · Horeca",
|
|
"doner_shoarma": "Export · Horeca",
|
|
"butcher": "Export · Slagerij",
|
|
"foodservice": "Export · Foodservice",
|
|
}
|
|
|
|
|
|
def _sector(entity_type: str) -> str:
|
|
return SECTOR_BY_TYPE.get(entity_type or "", "Export · B2B")
|
|
|
|
|
|
def _contact_name(contacts: list[dict[str, Any]]) -> Optional[str]:
|
|
for c in contacts:
|
|
if c.get("name"):
|
|
return c["name"]
|
|
return None
|
|
|
|
|
|
def _build_notes(ent: dict[str, Any], contacts: list[dict[str, Any]], halal_score: float) -> str:
|
|
lines = [
|
|
f"Wereldexport entity #{ent['id']}",
|
|
f"Type: {ent.get('entity_type')}",
|
|
f"Land: {ent.get('country_iso2')}",
|
|
]
|
|
if ent.get("city"):
|
|
lines.append(f"Stad: {ent['city']}")
|
|
if ent.get("address_line"):
|
|
lines.append(f"Adres: {ent['address_line']}")
|
|
if ent.get("website"):
|
|
lines.append(f"Website: {ent['website']}")
|
|
if ent.get("phone"):
|
|
lines.append(f"Telefoon: {ent['phone']}")
|
|
lines.append(f"Halal-kans score: {halal_score}/100")
|
|
lines.append(f"OSM bron: {ent.get('source') or '—'}")
|
|
if contacts:
|
|
lines.append(f"Contacten in registry: {len(contacts)}")
|
|
lines.append("—")
|
|
lines.append("Aangemaakt vanuit Wereldexport · Foodlinkk B2B")
|
|
return "\n".join(lines)
|
|
|
|
|
|
def push_entity_to_crm(
|
|
entity_id: int,
|
|
*,
|
|
create_deal: bool = True,
|
|
client_stage: str = "intake",
|
|
deal_stage: str = "lead",
|
|
) -> dict[str, Any]:
|
|
ent = fetch_one("SELECT * FROM export_market_entities WHERE id = %s", (entity_id,))
|
|
if not ent:
|
|
return {"entity_id": entity_id, "status": "not_found"}
|
|
|
|
if ent.get("client_id"):
|
|
client = fetch_one("SELECT id, name FROM clients WHERE id = %s", (ent["client_id"],))
|
|
return {
|
|
"entity_id": entity_id,
|
|
"status": "already_linked",
|
|
"client_id": ent["client_id"],
|
|
"deal_id": ent.get("deal_id"),
|
|
"client_name": (client or {}).get("name"),
|
|
}
|
|
|
|
from app.db import fetch_all
|
|
|
|
contacts = fetch_all(
|
|
"SELECT * FROM export_entity_contacts WHERE entity_id = %s ORDER BY is_primary DESC, confidence DESC",
|
|
(entity_id,),
|
|
)
|
|
email = ent.get("email")
|
|
phone = ent.get("phone")
|
|
for c in contacts:
|
|
if not email and c.get("email"):
|
|
email = c["email"]
|
|
if not phone:
|
|
phone = c.get("phone") or c.get("mobile")
|
|
|
|
client_id: Optional[int] = None
|
|
status = "created"
|
|
|
|
if email:
|
|
existing = fetch_one(
|
|
"SELECT id, name FROM clients WHERE LOWER(email) = LOWER(%s) LIMIT 1",
|
|
(email,),
|
|
)
|
|
if existing:
|
|
client_id = existing["id"]
|
|
status = "linked_existing_email"
|
|
|
|
if client_id is None:
|
|
contact_label = _contact_name(contacts) or phone or email
|
|
halal_score, _ = score_entity({**ent, "contact_count": len(contacts)})
|
|
notes = _build_notes(ent, contacts, halal_score)
|
|
row = execute_returning(
|
|
"""
|
|
INSERT INTO clients (name, contact, email, stage, sector, notes, updated_at)
|
|
VALUES (%s, %s, %s, %s, %s, %s, NOW())
|
|
RETURNING id
|
|
""",
|
|
(
|
|
ent["name"][:255],
|
|
(contact_label or "")[:255] or None,
|
|
(email or "")[:255] or None,
|
|
client_stage,
|
|
_sector(ent.get("entity_type") or ""),
|
|
notes,
|
|
),
|
|
)
|
|
if not row:
|
|
return {"entity_id": entity_id, "status": "error", "error": "client_insert_failed"}
|
|
client_id = row["id"]
|
|
|
|
deal_id: Optional[int] = None
|
|
if create_deal and not ent.get("deal_id"):
|
|
country = ent.get("country_iso2") or ""
|
|
title = f"Export · {ent['name'][:180]} ({country})"
|
|
deal_row = execute_returning(
|
|
"""
|
|
INSERT INTO deals (client_id, title, value, stage, agent_owner, next_action, updated_at)
|
|
VALUES (%s, %s, 0, %s, 'herman', %s, NOW())
|
|
RETURNING id
|
|
""",
|
|
(
|
|
client_id,
|
|
title[:255],
|
|
deal_stage,
|
|
f"Eerste outreach — Wereldexport entity #{entity_id}",
|
|
),
|
|
)
|
|
if deal_row:
|
|
deal_id = deal_row["id"]
|
|
|
|
execute(
|
|
"""
|
|
UPDATE export_market_entities
|
|
SET client_id = %s,
|
|
deal_id = COALESCE(%s, deal_id),
|
|
pipeline_stage = 'crm_linked',
|
|
crm_pushed_at = NOW(),
|
|
updated_at = NOW()
|
|
WHERE id = %s
|
|
""",
|
|
(client_id, deal_id, entity_id),
|
|
)
|
|
|
|
client = fetch_one("SELECT id, name FROM clients WHERE id = %s", (client_id,))
|
|
return {
|
|
"entity_id": entity_id,
|
|
"status": status,
|
|
"client_id": client_id,
|
|
"deal_id": deal_id,
|
|
"client_name": (client or {}).get("name"),
|
|
}
|
|
|
|
|
|
def push_entities_to_crm(
|
|
entity_ids: list[int],
|
|
*,
|
|
create_deals: bool = True,
|
|
client_stage: str = "intake",
|
|
deal_stage: str = "lead",
|
|
) -> dict[str, Any]:
|
|
results = [
|
|
push_entity_to_crm(
|
|
eid,
|
|
create_deal=create_deals,
|
|
client_stage=client_stage,
|
|
deal_stage=deal_stage,
|
|
)
|
|
for eid in entity_ids
|
|
]
|
|
created = sum(1 for r in results if r.get("status") == "created")
|
|
linked = sum(1 for r in results if r.get("status") in ("linked_existing_email", "already_linked"))
|
|
return {
|
|
"results": results,
|
|
"total": len(results),
|
|
"created": created,
|
|
"linked_existing": linked,
|
|
"pushed": created + sum(1 for r in results if r.get("status") == "linked_existing_email"),
|
|
}
|