fix: wire Velocity-OS live CRM and inventory data
Some checks failed
Velocity-OS Deployment Pipeline / lint (push) Has been cancelled
Velocity-OS Deployment Pipeline / build-and-push (agents) (push) Has been cancelled
Velocity-OS Deployment Pipeline / build-and-push (core) (push) Has been cancelled
Velocity-OS Deployment Pipeline / build-and-push (media-engine) (push) Has been cancelled
Velocity-OS Deployment Pipeline / build-and-push (webos) (push) Has been cancelled
Velocity-OS Deployment Pipeline / sign-images (agents) (push) Has been cancelled
Velocity-OS Deployment Pipeline / sign-images (core) (push) Has been cancelled
Velocity-OS Deployment Pipeline / sign-images (media-engine) (push) Has been cancelled
Velocity-OS Deployment Pipeline / sign-images (webos) (push) Has been cancelled
Velocity-OS Deployment Pipeline / notify-ingress (push) Has been cancelled

This commit is contained in:
2026-05-01 18:54:00 +05:30
parent 103900c58a
commit 2666b59327
2 changed files with 94 additions and 0 deletions

View File

@@ -1356,6 +1356,7 @@ async def get_kanban_board(
@crm_router.get("/pipeline/kanban")
@crm_router.get("/crm/pipeline/kanban")
async def get_pipeline_kanban(
request: Request,
user: UserPrincipal = Depends(get_current_user),

View File

@@ -47,6 +47,91 @@ def _tenant_scope(user) -> str:
return user.tenant_id
def _as_float(value: Any) -> float | None:
return float(value) if value is not None else None
def _canonical_project_payload(row: dict[str, Any]) -> dict[str, Any]:
location = row.get("location_json")
amenities = row.get("amenities_json")
unit_mix = row.get("unit_mix")
price_min = row.get("price_min")
price_max = row.get("price_max")
return {
"property_id": str(row["project_id"]),
"project_name": row["project_name"],
"developer_name": row["developer_name"],
"property_type": "project",
"location": {
"city": row.get("city"),
"district": row.get("micro_market"),
"area": row.get("micro_market"),
"address": row.get("address"),
**(location if isinstance(location, dict) else {}),
},
"price_bands": [
{
"label": "Current inventory",
"min": _as_float(price_min),
"max": _as_float(price_max),
"currency": "INR lakh",
}
] if price_min is not None or price_max is not None else [],
"unit_mix": unit_mix if isinstance(unit_mix, list) else [],
"amenities": amenities if isinstance(amenities, list) else [],
"status": row.get("project_status"),
"ingested_at": row.get("created_at"),
"created_at": row.get("created_at"),
}
async def _fetch_canonical_project_rows(conn: Any, limit: int, offset: int, project_id: str | None = None) -> list[dict[str, Any]]:
filters = ""
params: list[Any] = []
if project_id is not None:
filters = "WHERE p.project_id = $1"
params.append(project_id)
rows = await conn.fetch(
f"""
SELECT
p.project_id,
p.project_name,
p.developer_name,
p.city,
p.micro_market,
p.address,
p.project_status,
p.location_json,
p.amenities_json,
p.created_at,
MIN(u.price_current) AS price_min,
MAX(u.price_current) AS price_max,
COALESCE(
jsonb_agg(
DISTINCT jsonb_build_object(
'configuration', u.configuration,
'count', 1,
'available', CASE WHEN u.status = 'available' THEN 1 ELSE 0 END,
'area', CASE WHEN u.area_sqft IS NULL THEN NULL ELSE concat(u.area_sqft::text, ' sqft') END,
'price', CASE WHEN u.price_current IS NULL THEN NULL ELSE concat('INR ', u.price_current::text, ' lakh') END
)
) FILTER (WHERE u.unit_id IS NOT NULL),
'[]'::jsonb
) AS unit_mix
FROM inventory_projects p
LEFT JOIN inventory_units u ON u.project_id = p.project_id
{filters}
GROUP BY p.project_id
ORDER BY p.project_name ASC
LIMIT ${len(params) + 1} OFFSET ${len(params) + 2}
""",
*params,
limit,
offset,
)
return [_canonical_project_payload(dict(row)) for row in rows]
# ── Pydantic Models ───────────────────────────────────────────────────────────
VALID_SOURCE_TYPES = {"csv", "json", "api_push", "manual"}
@@ -237,6 +322,10 @@ async def list_properties(
total = await conn.fetchval(
f"SELECT COUNT(*) FROM inventory_properties {where_clause}", *params,
)
if total == 0 and not status_filter and not property_type:
canonical_rows = await _fetch_canonical_project_rows(conn, limit, offset)
canonical_total = await conn.fetchval("SELECT COUNT(*) FROM inventory_projects")
return {"total": canonical_total, "limit": limit, "offset": offset, "properties": canonical_rows}
return {"total": total, "limit": limit, "offset": offset, "properties": [dict(r) for r in rows]}
@@ -252,6 +341,10 @@ async def get_property(
"SELECT * FROM inventory_properties WHERE property_id=$1 AND tenant_id=$2",
property_id, _tenant_scope(user),
)
if not row:
canonical_rows = await _fetch_canonical_project_rows(conn, limit=1, offset=0, project_id=property_id)
if canonical_rows:
return canonical_rows[0]
if not row:
raise HTTPException(404, "Property not found")
return dict(row)