Compare commits
2 Commits
bc90cdba79
...
fa17eb2089
| Author | SHA1 | Date | |
|---|---|---|---|
| fa17eb2089 | |||
| a77f67b456 |
@ -149,6 +149,124 @@ class OrderService:
|
||||
|
||||
return enriched
|
||||
|
||||
def _enrich_items_from_db(self, session: Session, items: list) -> list[dict]:
|
||||
"""读取数据库订单项,对 cost_price=0 的项用定价引擎重算。
|
||||
|
||||
解决旧订单在定价引擎集成前创建、cost_price 存为 0 的问题。
|
||||
仅在 cost_price 为 0 且存在对应定价规则时触发计算。
|
||||
|
||||
Args:
|
||||
session: 数据库会话
|
||||
items: SalesOrderItem ORM 对象列表
|
||||
|
||||
Returns:
|
||||
序列化后的订单项字典列表
|
||||
"""
|
||||
from backend.app.models.business import ProductPricingRule, Product
|
||||
|
||||
# 一次性加载所有有定价规则的产品属性和规则
|
||||
product_ids = [item.product_id for item in items if item.product_id]
|
||||
rules_map = {}
|
||||
products_map = {}
|
||||
if product_ids:
|
||||
for rule in session.query(ProductPricingRule).filter(
|
||||
ProductPricingRule.product_id.in_(product_ids),
|
||||
ProductPricingRule.deleted == 0,
|
||||
ProductPricingRule.status == 1,
|
||||
).all():
|
||||
rules_map[rule.product_id] = rule
|
||||
for product in session.query(Product).filter(Product.id.in_(product_ids)).all():
|
||||
products_map[product.id] = product
|
||||
|
||||
result = []
|
||||
for item in items:
|
||||
item_dict = {
|
||||
"item_id": item.id,
|
||||
"product_id": item.product_id,
|
||||
"product_name": item.product_name,
|
||||
"specification": item.specification,
|
||||
"unit": item.unit,
|
||||
"quantity": float(item.quantity or 0),
|
||||
"sale_price": float(item.sale_price or 0),
|
||||
"cost_price": float(item.cost_price or 0),
|
||||
"rebate_amount": float(item.rebate_amount or 0),
|
||||
"freight_amount": float(item.freight_amount or 0),
|
||||
"tax_amount": float(item.tax_amount or 0),
|
||||
"other_fee_amount": float(item.other_fee_amount or 0),
|
||||
"remark": item.remark,
|
||||
"demand_specification": getattr(item, "demand_specification", None),
|
||||
"pricing_type": getattr(item, "pricing_type", None),
|
||||
"length_m": float(item.length_m) if getattr(item, "length_m", None) else None,
|
||||
"width_m": float(item.width_m) if getattr(item, "width_m", None) else None,
|
||||
"area_sqm": float(item.area_sqm) if getattr(item, "area_sqm", None) else None,
|
||||
"surcharge_detail": getattr(item, "surcharge_detail", None),
|
||||
"processing_detail": getattr(item, "processing_detail", None),
|
||||
"supplier_id": getattr(item, "supplier_id", None),
|
||||
"supplier_model": getattr(item, "supplier_model", None),
|
||||
"price_tier": getattr(item, "price_tier", None),
|
||||
}
|
||||
|
||||
# cost_price 为 0 且有定价规则时,尝试用引擎重算
|
||||
if item_dict["cost_price"] == 0 and item.product_id and item.product_id in rules_map:
|
||||
rule = rules_map[item.product_id]
|
||||
product = products_map.get(item.product_id)
|
||||
product_attrs = {}
|
||||
if product:
|
||||
if product.thickness:
|
||||
product_attrs["thickness"] = product.thickness
|
||||
if product.weight_gsm:
|
||||
product_attrs["weight_gsm"] = product.weight_gsm
|
||||
if product.default_width_m:
|
||||
product_attrs["default_width_m"] = float(product.default_width_m)
|
||||
|
||||
user_inputs = {}
|
||||
if getattr(item, "length_m", None):
|
||||
user_inputs["length"] = float(item.length_m)
|
||||
user_inputs["length_unit"] = "m"
|
||||
if getattr(item, "width_m", None):
|
||||
user_inputs["width"] = float(item.width_m)
|
||||
user_inputs["width_unit"] = "m"
|
||||
if item.quantity:
|
||||
user_inputs["quantity"] = float(item.quantity)
|
||||
if getattr(item, "price_tier", None):
|
||||
user_inputs["price_tier"] = item.price_tier
|
||||
surcharge_detail = getattr(item, "surcharge_detail", None)
|
||||
if surcharge_detail:
|
||||
try:
|
||||
surcharges = json.loads(surcharge_detail) if isinstance(surcharge_detail, str) else surcharge_detail
|
||||
if isinstance(surcharges, list):
|
||||
for s in surcharges:
|
||||
key = s.get("key")
|
||||
if key:
|
||||
user_inputs[f"surcharge_{key}"] = True
|
||||
except (json.JSONDecodeError, TypeError):
|
||||
pass
|
||||
|
||||
try:
|
||||
calc_result = pricing_engine.calculate_for_order_item(rule, user_inputs, product_attrs)
|
||||
snapshot = calc_result.get("item_snapshot", {})
|
||||
if snapshot.get("cost_price") is not None and snapshot["cost_price"] > 0:
|
||||
item_dict["cost_price"] = snapshot["cost_price"]
|
||||
item_dict["pricing_type"] = snapshot.get("pricing_type") or item_dict["pricing_type"]
|
||||
if calc_result.get("area_sqm"):
|
||||
item_dict["area_sqm"] = calc_result["area_sqm"]
|
||||
# 回写数据库,避免下次重复计算
|
||||
try:
|
||||
item.cost_price = snapshot["cost_price"]
|
||||
if snapshot.get("pricing_type"):
|
||||
item.pricing_type = snapshot["pricing_type"]
|
||||
if calc_result.get("area_sqm"):
|
||||
item.area_sqm = calc_result["area_sqm"]
|
||||
session.flush()
|
||||
except Exception:
|
||||
pass
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
result.append(item_dict)
|
||||
|
||||
return result
|
||||
|
||||
def list_orders(self, session: Session | None = None, filters: dict | None = None, current_user: dict | None = None) -> dict:
|
||||
"""查询订单列表。
|
||||
|
||||
@ -318,34 +436,7 @@ class OrderService:
|
||||
"order_type": order.order_type,
|
||||
"self_delivery": order.self_delivery or 0,
|
||||
"tracking_number": order.tracking_number,
|
||||
"items": [
|
||||
{
|
||||
"item_id": item.id,
|
||||
"product_id": item.product_id,
|
||||
"product_name": item.product_name,
|
||||
"specification": item.specification,
|
||||
"unit": item.unit,
|
||||
"quantity": float(item.quantity or 0),
|
||||
"sale_price": float(item.sale_price or 0),
|
||||
"cost_price": float(item.cost_price or 0),
|
||||
"rebate_amount": float(item.rebate_amount or 0),
|
||||
"freight_amount": float(item.freight_amount or 0),
|
||||
"tax_amount": float(item.tax_amount or 0),
|
||||
"other_fee_amount": float(item.other_fee_amount or 0),
|
||||
"remark": item.remark,
|
||||
"demand_specification": getattr(item, "demand_specification", None),
|
||||
"pricing_type": getattr(item, "pricing_type", None),
|
||||
"length_m": float(item.length_m) if getattr(item, "length_m", None) else None,
|
||||
"width_m": float(item.width_m) if getattr(item, "width_m", None) else None,
|
||||
"area_sqm": float(item.area_sqm) if getattr(item, "area_sqm", None) else None,
|
||||
"surcharge_detail": getattr(item, "surcharge_detail", None),
|
||||
"processing_detail": getattr(item, "processing_detail", None),
|
||||
"supplier_id": getattr(item, "supplier_id", None),
|
||||
"supplier_model": getattr(item, "supplier_model", None),
|
||||
"price_tier": getattr(item, "price_tier", None),
|
||||
}
|
||||
for item in items
|
||||
],
|
||||
"items": self._enrich_items_from_db(session, items),
|
||||
"approve_logs": [
|
||||
{
|
||||
"log_id": log.id,
|
||||
|
||||
@ -396,6 +396,8 @@ function buildDefaultItem() {
|
||||
product_name: "",
|
||||
specification: "",
|
||||
demand_specification: "",
|
||||
length_m: null,
|
||||
width_m: null,
|
||||
_demandResult: "",
|
||||
_demandCalcHint: "",
|
||||
unit: "",
|
||||
@ -793,6 +795,8 @@ function calcDemandResult(row) {
|
||||
if (!spec) {
|
||||
row._demandResult = "";
|
||||
row._demandCalcHint = "";
|
||||
row.length_m = null;
|
||||
row.width_m = null;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -819,6 +823,9 @@ function calcDemandResult(row) {
|
||||
const area = m1 * m2;
|
||||
row._demandResult = `${area.toFixed(4)} m²`;
|
||||
row._demandCalcHint = `${v1}${u1} × ${v2}${u2} = ${m1}m × ${m2}m = ${area.toFixed(4)}m²`;
|
||||
// 写入尺寸字段供定价引擎使用
|
||||
row.length_m = m2;
|
||||
row.width_m = m1;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -829,12 +836,16 @@ function calcDemandResult(row) {
|
||||
const unit = weightMatch[2];
|
||||
row._demandResult = `${value}${unit}`;
|
||||
row._demandCalcHint = `重量: ${value}${unit}`;
|
||||
row.length_m = null;
|
||||
row.width_m = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// 无法识别
|
||||
row._demandResult = "";
|
||||
row._demandCalcHint = "";
|
||||
row.length_m = null;
|
||||
row.width_m = null;
|
||||
}
|
||||
|
||||
const paymentChannels = ref([]);
|
||||
|
||||
@ -1148,10 +1148,13 @@ function addEditItem() {
|
||||
editItems.value.push({
|
||||
product_name: '',
|
||||
specification: '',
|
||||
demand_specification: '',
|
||||
unit: '',
|
||||
quantity: 1,
|
||||
sale_price: 0,
|
||||
cost_price: 0,
|
||||
length_m: null,
|
||||
width_m: null,
|
||||
});
|
||||
}
|
||||
|
||||
@ -1191,10 +1194,13 @@ async function saveEdit() {
|
||||
product_id: item.product_id || null,
|
||||
product_name: item.product_name.trim(),
|
||||
specification: item.specification?.trim() || '',
|
||||
demand_specification: item.demand_specification?.trim() || '',
|
||||
unit: item.unit?.trim() || '',
|
||||
quantity: Number(item.quantity) || 1,
|
||||
sale_price: Number(item.sale_price) || 0,
|
||||
cost_price: Number(item.cost_price) || 0,
|
||||
length_m: item.length_m != null ? Number(item.length_m) : null,
|
||||
width_m: item.width_m != null ? Number(item.width_m) : null,
|
||||
}));
|
||||
|
||||
const rawStatus = detail.order.rawStatus;
|
||||
|
||||
@ -139,18 +139,18 @@
|
||||
<tr>
|
||||
<th>产品名称</th>
|
||||
<th>规格</th>
|
||||
<th>需求规格</th>
|
||||
<th>数量</th>
|
||||
<th>计价方式</th>
|
||||
<th>成本单价</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="item in orderItems" :key="item.id">
|
||||
<td>{{ item.productName }}</td>
|
||||
<td>{{ item.specification }}</td>
|
||||
<td>{{ item.demandSpecification || '-' }}</td>
|
||||
<td>{{ item.quantity }}{{ item.unit }}</td>
|
||||
<td>{{ getPricingTypeLabel(item.pricingType) }}</td>
|
||||
<td>¥{{ item.costPrice }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user