feat: v0.3 报价算料引擎与仓库库存模块

后端:
- 报价算料: 从drawing_data几何提取型材清单(外框/中梃/扇框投影长度)
  与玻璃面积(鞋带公式), 套价格本计价, 支持样板/订单/自定义图纸三入口
- 仓库库存: 材料CRUD/出入库/流水/负库存拦截/预警线
- SQLite默认路径锚定项目根目录(修复cwd依赖)
前端:
- 仓库库存页(筛选/出入库对话框/预警高亮)
- 算料报价弹窗(样板库与订单详情入口, 支持打印)
- 路由与导航更新(8页面)
回归: 9个API端点全部200, 报价/优化/SPA路由验证通过
This commit is contained in:
HHY
2026-09-20 08:58:14 -07:00
parent f7a555dd6c
commit 065d387399
16 changed files with 630 additions and 8 deletions
+45
View File
@@ -0,0 +1,45 @@
"""报价路由:从 drawing_data 算料计价(对齐目标站 offer-price 语义,自研实现)。"""
from fastapi import APIRouter, Depends
from pydantic import BaseModel, Field
from huamenchuang.core.deps import CurrentUser, DbSession, require_module
from huamenchuang.core.response import fail, ok
from huamenchuang.services.quote_service import quote as calc_quote
router = APIRouter(prefix="/offer-price", tags=["报价"],
dependencies=[Depends(require_module("quote"))])
class QuoteRequest(BaseModel):
drawing_data: str | dict | None = Field(default=None, description="画布 drawing_data(与 template_id/order_id 三选一)")
template_id: int | None = Field(default=None, description="或直接指定样板")
order_id: int | None = Field(default=None, description="或对已有订单报价")
prices: dict | None = Field(default=None, description="价格本覆盖 {frame,mullion,sash,glass,hardware}")
async def _resolve_drawing(db, body: QuoteRequest) -> str | dict | None:
if body.drawing_data:
return body.drawing_data
if body.template_id:
from sqlalchemy import select
from huamenchuang.models.windoor_template import WindoorTemplate
t = (await db.execute(select(WindoorTemplate).where(WindoorTemplate.id == body.template_id))).scalar_one_or_none()
return t.drawing_data if t else None
if body.order_id:
from huamenchuang.models.order import Order
o = await db.get(Order, body.order_id)
return o.drawing_data if o else None
return None
@router.post("")
async def offer_price(body: QuoteRequest, user: CurrentUser, db: DbSession):
"""算料报价:型材清单 + 玻璃面积 + 五金 + 总价。"""
drawing = await _resolve_drawing(db, body)
if drawing is None:
return fail("未找到图纸数据")
try:
result = calc_quote(drawing, body.prices)
except Exception:
return fail("图纸数据解析失败")
return ok(data=result, message="报价成功")