feat(dashboard): supporting schema, seed, catalog links, funnel/PM labels and API contract (merge resolution)
This commit is contained in:
@@ -38,13 +38,14 @@ from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from app.auth import UserContext, get_optional_user
|
||||
from app.config import settings
|
||||
from app.db.engine import get_db
|
||||
from app.models.catalog import BrandMaterial, Material, Unit
|
||||
from app.models.catalog import AttributeDefinition, Brand, BrandMaterial, Material, Unit
|
||||
from app.models.inventory import InventoryItem, InventoryTransaction, Warehouse
|
||||
from app.models.procurement import (
|
||||
ItemDecision,
|
||||
ItemReceipt,
|
||||
ItemReceiptAllocation,
|
||||
LineItem,
|
||||
LineItemSpec,
|
||||
Project,
|
||||
Quote,
|
||||
Supplier,
|
||||
@@ -182,16 +183,24 @@ def arrival_state(ordered: Decimal, received: Decimal | None) -> str:
|
||||
async def pm_on_the_way(
|
||||
ctx: ArrivalScope = Depends(arrivals_scope),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
material_id: uuid.UUID | None = Query(default=None, description="فیلتر متریال (داشبورد بازرگانی)"),
|
||||
brand_id: uuid.UUID | None = Query(default=None, description="فیلتر برند (داشبورد بازرگانی)"),
|
||||
):
|
||||
stmt = (
|
||||
select(ItemDecision, LineItem, Quote, Material, Supplier, Project)
|
||||
select(ItemDecision, LineItem, Quote, Material, Supplier, Project, BrandMaterial, Brand)
|
||||
.join(LineItem, LineItem.id == ItemDecision.line_item_id)
|
||||
.join(Quote, Quote.id == LineItem.quote_id)
|
||||
.join(Material, Material.id == LineItem.material_id)
|
||||
.join(Supplier, Supplier.id == Quote.supplier_id)
|
||||
.outerjoin(Project, Project.id == ItemDecision.project_id)
|
||||
.outerjoin(BrandMaterial, BrandMaterial.id == LineItem.brand_material_id)
|
||||
.outerjoin(Brand, Brand.id == BrandMaterial.brand_id)
|
||||
.where(ItemDecision.status == "chosen")
|
||||
)
|
||||
if material_id is not None:
|
||||
stmt = stmt.where(Material.id == material_id)
|
||||
if brand_id is not None:
|
||||
stmt = stmt.where(BrandMaterial.brand_id == brand_id)
|
||||
if ctx.role == "pm":
|
||||
allowed = ctx.project_ids or ([ctx.project_id] if ctx.project_id else [])
|
||||
if not allowed:
|
||||
@@ -211,8 +220,29 @@ async def pm_on_the_way(
|
||||
# unit / category lookups for display
|
||||
unit_ids = {d.LineItem.unit_id for d in decisions}
|
||||
units = {u.id: u for u in (await db.execute(select(Unit).where(Unit.id.in_(unit_ids)))).scalars()}
|
||||
# frozen spec snapshot per line item (key = attribute label)
|
||||
li_ids = [d.LineItem.id for d in decisions]
|
||||
specs_by_line: dict = {}
|
||||
if li_ids:
|
||||
spec_rows = (
|
||||
await db.execute(
|
||||
select(LineItemSpec, AttributeDefinition)
|
||||
.join(AttributeDefinition, AttributeDefinition.id == LineItemSpec.attribute_definition_id)
|
||||
.where(LineItemSpec.line_item_id.in_(li_ids))
|
||||
)
|
||||
).all()
|
||||
for spec, ad in spec_rows:
|
||||
value = spec.value_string
|
||||
if value is None and spec.value_number is not None:
|
||||
value = str(spec.value_number)
|
||||
if value is None and spec.value_boolean is not None:
|
||||
value = str(spec.value_boolean)
|
||||
if value is None and spec.value_date is not None:
|
||||
value = spec.value_date.isoformat()
|
||||
if value is not None:
|
||||
specs_by_line.setdefault(spec.line_item_id, {})[ad.label or ad.attribute_key] = value
|
||||
out = []
|
||||
for d, li, q, m, sup, proj in decisions:
|
||||
for d, li, q, m, sup, proj, bm, br in decisions:
|
||||
received = received_map.get(d.id)
|
||||
out.append({
|
||||
"item_decision_id": str(d.id),
|
||||
@@ -224,6 +254,8 @@ async def pm_on_the_way(
|
||||
"project": {"id": str(proj.id), "name": proj.name} if proj else None,
|
||||
"decided_at": to_iso(d.decided_at),
|
||||
"material": {"id": str(m.id), "name": m.name},
|
||||
"brand": {"id": str(br.id), "name": br.name} if br else None,
|
||||
"specs": specs_by_line.get(li.id, {}),
|
||||
"unit": {"id": units[li.unit_id].id, "code": units[li.unit_id].code,
|
||||
"name_fa": units[li.unit_id].name_fa} if li.unit_id in units else None,
|
||||
"ordered_qty": str(li.quantity),
|
||||
|
||||
Reference in New Issue
Block a user