Fix drill-down logic and structure in dashboard

This commit is contained in:
fatemeh sane
2026-10-08 15:28:53 +03:30
parent 7bb432e715
commit 34b987398a
3 changed files with 427 additions and 184 deletions
+46 -18
View File
@@ -176,14 +176,17 @@ async def inventory_drilldown(
brand_id=None,
specs_hash=None,
) -> dict:
"""Stock quantity as a three-level tree; each aggregate keyed by unit code
so incompatible units never merge into one number."""
"""Stock quantity as a three-level tree: Material -> Project -> Warehouse."""
stmt = (
select(
Project.id,
Project.name,
Warehouse.id,
Warehouse.name,
Material.id.label("mid"),
Material.name.label("mname"),
Brand.name.label("bname"),
InventoryItem.specs,
Project.id.label("pid"),
Project.name.label("pname"),
Warehouse.id.label("wid"),
Warehouse.name.label("wname"),
Unit.code,
Unit.name_fa,
func.sum(InventoryItem.quantity).label("qty"),
@@ -194,9 +197,8 @@ async def inventory_drilldown(
.join(Project, Project.id == Warehouse.project_id)
.join(Material, Material.id == InventoryItem.material_id)
.join(Unit, Unit.id == InventoryItem.unit_id)
.where(InventoryItem.quantity > 0)
.group_by(Project.id, Project.name, Warehouse.id, Warehouse.name, Unit.code, Unit.name_fa)
.order_by(Project.name, Warehouse.name)
.outerjoin(Brand, Brand.id == InventoryItem.brand_id)
.group_by(Material.id, Material.name, Brand.name, InventoryItem.specs, Project.id, Project.name, Warehouse.id, Warehouse.name, Unit.code, Unit.name_fa)
)
stmt = _stock_filters(
stmt, category_id=category_id, material_id=material_id, brand_id=brand_id, specs_hash=specs_hash
@@ -207,33 +209,59 @@ async def inventory_drilldown(
units_legend: dict[str, str] = {}
total_by_unit: dict[str, float] = {}
total_rows = 0
projects_tree: dict[uuid.UUID, dict] = {}
for pid, pname, wid, wname, ucode, uname_fa, qty, row_count in rows:
materials_tree: dict[str, dict] = {}
for mid, mname, bname, specs, pid, pname, wid, wname, ucode, uname_fa, qty, row_count in rows:
q = _qty(qty)
units_legend[ucode] = uname_fa
total_by_unit[ucode] = round(total_by_unit.get(ucode, 0.0) + q, 4)
total_rows += row_count
proj = projects_tree.setdefault(
# Build full material name
parts = [mname]
if bname:
parts.append(bname)
if specs:
parts.extend(str(v) for v in specs.values() if v)
full_name = " ".join(parts)
# Use full_name + unit as unique key for material level, or just full_name
mat_key = f"{mid}_{full_name}"
mat = materials_tree.setdefault(
mat_key,
{"id": mat_key, "name": full_name, "qty_by_unit": {}, "rows": 0, "_projects": {}},
)
mat["qty_by_unit"][ucode] = round(mat["qty_by_unit"].get(ucode, 0.0) + q, 4)
mat["rows"] += row_count
proj = mat["_projects"].setdefault(
pid,
{"id": str(pid), "name": pname, "qty_by_unit": {}, "rows": 0, "_warehouses": {}},
)
proj["qty_by_unit"][ucode] = round(proj["qty_by_unit"].get(ucode, 0.0) + q, 4)
proj["rows"] += row_count
wh = proj["_warehouses"].setdefault(wid, {"id": str(wid), "name": wname, "qty_by_unit": {}, "rows": 0})
wh["qty_by_unit"][ucode] = round(wh["qty_by_unit"].get(ucode, 0.0) + q, 4)
wh["rows"] += row_count
projects = []
for proj in projects_tree.values():
warehouses = sorted(proj.pop("_warehouses").values(), key=lambda w: w["name"])
projects.append({**proj, "warehouses": warehouses})
projects.sort(key=lambda p: p["name"])
materials = []
for mat in materials_tree.values():
projects_list = []
for proj in mat.pop("_projects").values():
warehouses = sorted(proj.pop("_warehouses").values(), key=lambda w: w["name"])
projects_list.append({**proj, "warehouses": warehouses})
projects_list.sort(key=lambda p: p["name"])
materials.append({**mat, "projects": projects_list})
materials.sort(key=lambda m: m["name"])
return {
"data": {
"units": units_legend,
"total": {"qty_by_unit": total_by_unit, "rows": total_rows},
"projects": projects,
"materials": materials,
}
}