Update UI and logic for supplier name and call button
This commit is contained in:
@@ -7,7 +7,7 @@ GET /materials/{id}/suppliers — suppliers linked to a material.
|
||||
from uuid import UUID
|
||||
|
||||
from fastapi import APIRouter, Depends, Query
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy import func, select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.db.engine import get_db
|
||||
@@ -28,7 +28,11 @@ def link_to_dict(
|
||||
category: MaterialCategory | None = None,
|
||||
supplier: Supplier | None = None,
|
||||
) -> dict:
|
||||
<<<<<<< Updated upstream
|
||||
"""Serialize a SupplierMaterial ORM row to a plain dict for the API envelope."""
|
||||
=======
|
||||
"""Serialize a SupplierMaterial ORM row to a plain dict."""
|
||||
>>>>>>> Stashed changes
|
||||
return {
|
||||
"id": str(link.id),
|
||||
"supplier_id": str(link.supplier_id),
|
||||
@@ -111,6 +115,7 @@ async def create_supplier_material(
|
||||
async def delete_supplier_material(
|
||||
supplier_id: UUID,
|
||||
material_id: UUID,
|
||||
<<<<<<< Updated upstream
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
async with db.begin():
|
||||
@@ -161,12 +166,61 @@ async def list_material_suppliers(
|
||||
"location": "تهران" | null
|
||||
}
|
||||
]
|
||||
=======
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
async with db.begin():
|
||||
await unlink_supplier_material(db, supplier_id, material_id)
|
||||
return {
|
||||
"data": {
|
||||
"supplier_id": str(supplier_id),
|
||||
"material_id": str(material_id),
|
||||
"deleted": True,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# GET /materials/{material_id}/suppliers
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
@router.get(
|
||||
"/materials/{material_id}/suppliers",
|
||||
summary="List all suppliers linked to a material",
|
||||
)
|
||||
async def list_material_suppliers(
|
||||
material_id: UUID,
|
||||
page: int = Query(1, ge=1),
|
||||
page_size: int = Query(20, ge=1, le=100),
|
||||
db: AsyncSession = Depends(get_db),
|
||||
):
|
||||
"""Return a paginated list of suppliers for a given material.
|
||||
|
||||
Uses explicit column-level SELECT with named labels so SQLAlchemy returns
|
||||
plain Row tuples — no ORM identity-map involvement — eliminating the
|
||||
string-vs-UUID comparison bug and the stale-entity collision that occurred
|
||||
when selecting two mapped entities in the same session.
|
||||
|
||||
Response shape (matched by the frontend RawMaterialSupplier interface):
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "<supplier_uuid>",
|
||||
"name": "فولاد ناب آریا",
|
||||
"phone": "021-..." | null,
|
||||
"contact_person": "علی محمدی" | null,
|
||||
"location": "تهران" | null
|
||||
}
|
||||
],
|
||||
"meta": {"total": 1, "page": 1, "page_size": 20}
|
||||
>>>>>>> Stashed changes
|
||||
}
|
||||
"""
|
||||
material = await db.get(Material, material_id)
|
||||
if material is None:
|
||||
raise NotFoundError("متریال یافت نشد")
|
||||
|
||||
<<<<<<< Updated upstream
|
||||
stmt = (
|
||||
select(Supplier)
|
||||
.join(SupplierMaterial, Supplier.id == SupplierMaterial.supplier_id)
|
||||
@@ -186,4 +240,50 @@ async def list_material_suppliers(
|
||||
}
|
||||
for supplier in suppliers
|
||||
]
|
||||
=======
|
||||
total: int = (
|
||||
await db.scalar(
|
||||
select(func.count(SupplierMaterial.id)).where(
|
||||
SupplierMaterial.material_id == material_id
|
||||
)
|
||||
)
|
||||
) or 0
|
||||
|
||||
if total == 0:
|
||||
return {"data": [], "meta": {"total": 0, "page": page, "page_size": page_size}}
|
||||
|
||||
# Explicit column-level SELECT — returns plain Row objects keyed by label,
|
||||
# never ORM entities, so no identity-map collision is possible.
|
||||
stmt = (
|
||||
select(
|
||||
Supplier.id.label("sup_id"),
|
||||
Supplier.name.label("sup_name"),
|
||||
Supplier.phone.label("sup_phone"),
|
||||
Supplier.contact_person.label("sup_contact_person"),
|
||||
Supplier.location.label("sup_location"),
|
||||
)
|
||||
.join(SupplierMaterial, Supplier.id == SupplierMaterial.supplier_id)
|
||||
.where(SupplierMaterial.material_id == material_id)
|
||||
.order_by(SupplierMaterial.last_quoted_at.desc().nulls_last())
|
||||
.offset((page - 1) * page_size)
|
||||
.limit(page_size)
|
||||
)
|
||||
|
||||
rows = (await db.execute(stmt)).all()
|
||||
|
||||
data = [
|
||||
{
|
||||
"id": str(row.sup_id),
|
||||
"name": row.sup_name,
|
||||
"phone": row.sup_phone,
|
||||
"contact_person": row.sup_contact_person,
|
||||
"location": row.sup_location,
|
||||
}
|
||||
for row in rows
|
||||
]
|
||||
|
||||
return {
|
||||
"data": data,
|
||||
"meta": {"total": total, "page": page, "page_size": page_size},
|
||||
>>>>>>> Stashed changes
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user