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
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:
@@ -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),
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user