Add live CEO briefing, Wereldexport CRM, halal engine, and realtime cockpit.
Ship export intel with contact filters and CRM push, Herman live digest with Telegram briefing, halal recommendation engine, revenue cockpit, and dashboard polling/WebSocket fixes.
This commit is contained in:
@@ -285,37 +285,41 @@ def get_entity(entity_id: int) -> dict[str, Any]:
|
||||
@router.get("/contacts")
|
||||
def list_contacts(
|
||||
country: Optional[str] = Query(None),
|
||||
region: Optional[str] = Query(None),
|
||||
entity_type: Optional[str] = Query(None),
|
||||
has_email: Optional[bool] = Query(None),
|
||||
crm_linked: Optional[bool] = Query(None),
|
||||
q: Optional[str] = Query(None),
|
||||
limit: int = Query(200, ge=1, le=500),
|
||||
offset: int = Query(0, ge=0),
|
||||
) -> dict[str, Any]:
|
||||
clauses = ["1=1"]
|
||||
params: list[Any] = []
|
||||
if country:
|
||||
clauses.append("e.country_iso2 = %s")
|
||||
params.append(country.upper())
|
||||
if entity_type:
|
||||
clauses.append("e.entity_type = %s")
|
||||
params.append(entity_type)
|
||||
if has_email:
|
||||
clauses.append("c.email IS NOT NULL AND c.email <> ''")
|
||||
join = ""
|
||||
if region:
|
||||
join = "JOIN export_territories t ON e.territory_code = t.code"
|
||||
where_sql, params = _entity_filters(country, region, entity_type, None, False, crm_linked)
|
||||
if has_email is True:
|
||||
where_sql += " AND c.email IS NOT NULL AND c.email <> ''"
|
||||
elif has_email is False:
|
||||
where_sql += " AND (c.email IS NULL OR c.email = '')"
|
||||
if q:
|
||||
clauses.append(
|
||||
"(e.name ILIKE %s OR c.email ILIKE %s OR c.name ILIKE %s OR e.city ILIKE %s)"
|
||||
)
|
||||
like = f"%{q}%"
|
||||
params.extend([like, like, like, like])
|
||||
where_sql += (
|
||||
" AND (e.name ILIKE %s OR e.city ILIKE %s OR c.email ILIKE %s"
|
||||
" OR c.name ILIKE %s OR c.phone ILIKE %s OR c.mobile ILIKE %s)"
|
||||
)
|
||||
params.extend([like, like, like, like, like, like])
|
||||
|
||||
where = " AND ".join(clauses)
|
||||
rows = fetch_all(
|
||||
f"""
|
||||
SELECT c.*, e.name AS entity_name, e.entity_type, e.country_iso2, e.city AS entity_city
|
||||
SELECT c.*, e.name AS entity_name, e.entity_type, e.country_iso2,
|
||||
e.city AS entity_city, e.client_id, e.deal_id,
|
||||
cl.name AS crm_client_name
|
||||
FROM export_entity_contacts c
|
||||
JOIN export_market_entities e ON e.id = c.entity_id
|
||||
WHERE {where}
|
||||
ORDER BY e.name, c.is_primary DESC
|
||||
LEFT JOIN clients cl ON cl.id = e.client_id
|
||||
{join}
|
||||
WHERE {where_sql}
|
||||
ORDER BY e.name, e.city NULLS LAST, c.is_primary DESC, c.email NULLS LAST
|
||||
LIMIT %s OFFSET %s
|
||||
""",
|
||||
tuple(params) + (limit, offset),
|
||||
@@ -324,19 +328,33 @@ def list_contacts(
|
||||
f"""
|
||||
SELECT COUNT(*) AS n FROM export_entity_contacts c
|
||||
JOIN export_market_entities e ON e.id = c.entity_id
|
||||
WHERE {where}
|
||||
{join}
|
||||
WHERE {where_sql}
|
||||
""",
|
||||
tuple(params),
|
||||
)
|
||||
return {"items": rows, "total": int(total["n"] or 0)}
|
||||
return {"items": rows, "total": int(total["n"] or 0), "limit": limit, "offset": offset}
|
||||
|
||||
|
||||
@router.get("/contacts/export.csv")
|
||||
def export_contacts_csv(
|
||||
country: Optional[str] = Query(None),
|
||||
region: Optional[str] = Query(None),
|
||||
entity_type: Optional[str] = Query(None),
|
||||
has_email: Optional[bool] = Query(None),
|
||||
crm_linked: Optional[bool] = Query(None),
|
||||
q: Optional[str] = Query(None),
|
||||
) -> StreamingResponse:
|
||||
data = list_contacts(country=country, entity_type=entity_type, limit=5000, offset=0)
|
||||
data = list_contacts(
|
||||
country=country,
|
||||
region=region,
|
||||
entity_type=entity_type,
|
||||
has_email=has_email,
|
||||
crm_linked=crm_linked,
|
||||
q=q,
|
||||
limit=5000,
|
||||
offset=0,
|
||||
)
|
||||
buf = io.StringIO()
|
||||
writer = csv.writer(buf)
|
||||
writer.writerow(
|
||||
|
||||
Reference in New Issue
Block a user