Compare commits
2 Commits
bc90cdba79
...
fa17eb2089
| Author | SHA1 | Date | |
|---|---|---|---|
| fa17eb2089 | |||
| a77f67b456 |
@ -149,6 +149,124 @@ class OrderService:
|
|||||||
|
|
||||||
return enriched
|
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:
|
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,
|
"order_type": order.order_type,
|
||||||
"self_delivery": order.self_delivery or 0,
|
"self_delivery": order.self_delivery or 0,
|
||||||
"tracking_number": order.tracking_number,
|
"tracking_number": order.tracking_number,
|
||||||
"items": [
|
"items": self._enrich_items_from_db(session, 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
|
|
||||||
],
|
|
||||||
"approve_logs": [
|
"approve_logs": [
|
||||||
{
|
{
|
||||||
"log_id": log.id,
|
"log_id": log.id,
|
||||||
|
|||||||
@ -396,6 +396,8 @@ function buildDefaultItem() {
|
|||||||
product_name: "",
|
product_name: "",
|
||||||
specification: "",
|
specification: "",
|
||||||
demand_specification: "",
|
demand_specification: "",
|
||||||
|
length_m: null,
|
||||||
|
width_m: null,
|
||||||
_demandResult: "",
|
_demandResult: "",
|
||||||
_demandCalcHint: "",
|
_demandCalcHint: "",
|
||||||
unit: "",
|
unit: "",
|
||||||
@ -793,6 +795,8 @@ function calcDemandResult(row) {
|
|||||||
if (!spec) {
|
if (!spec) {
|
||||||
row._demandResult = "";
|
row._demandResult = "";
|
||||||
row._demandCalcHint = "";
|
row._demandCalcHint = "";
|
||||||
|
row.length_m = null;
|
||||||
|
row.width_m = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -819,6 +823,9 @@ function calcDemandResult(row) {
|
|||||||
const area = m1 * m2;
|
const area = m1 * m2;
|
||||||
row._demandResult = `${area.toFixed(4)} m²`;
|
row._demandResult = `${area.toFixed(4)} m²`;
|
||||||
row._demandCalcHint = `${v1}${u1} × ${v2}${u2} = ${m1}m × ${m2}m = ${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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -829,12 +836,16 @@ function calcDemandResult(row) {
|
|||||||
const unit = weightMatch[2];
|
const unit = weightMatch[2];
|
||||||
row._demandResult = `${value}${unit}`;
|
row._demandResult = `${value}${unit}`;
|
||||||
row._demandCalcHint = `重量: ${value}${unit}`;
|
row._demandCalcHint = `重量: ${value}${unit}`;
|
||||||
|
row.length_m = null;
|
||||||
|
row.width_m = null;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 无法识别
|
// 无法识别
|
||||||
row._demandResult = "";
|
row._demandResult = "";
|
||||||
row._demandCalcHint = "";
|
row._demandCalcHint = "";
|
||||||
|
row.length_m = null;
|
||||||
|
row.width_m = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const paymentChannels = ref([]);
|
const paymentChannels = ref([]);
|
||||||
|
|||||||
@ -1148,10 +1148,13 @@ function addEditItem() {
|
|||||||
editItems.value.push({
|
editItems.value.push({
|
||||||
product_name: '',
|
product_name: '',
|
||||||
specification: '',
|
specification: '',
|
||||||
|
demand_specification: '',
|
||||||
unit: '',
|
unit: '',
|
||||||
quantity: 1,
|
quantity: 1,
|
||||||
sale_price: 0,
|
sale_price: 0,
|
||||||
cost_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_id: item.product_id || null,
|
||||||
product_name: item.product_name.trim(),
|
product_name: item.product_name.trim(),
|
||||||
specification: item.specification?.trim() || '',
|
specification: item.specification?.trim() || '',
|
||||||
|
demand_specification: item.demand_specification?.trim() || '',
|
||||||
unit: item.unit?.trim() || '',
|
unit: item.unit?.trim() || '',
|
||||||
quantity: Number(item.quantity) || 1,
|
quantity: Number(item.quantity) || 1,
|
||||||
sale_price: Number(item.sale_price) || 0,
|
sale_price: Number(item.sale_price) || 0,
|
||||||
cost_price: Number(item.cost_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;
|
const rawStatus = detail.order.rawStatus;
|
||||||
|
|||||||
@ -139,18 +139,18 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>产品名称</th>
|
<th>产品名称</th>
|
||||||
<th>规格</th>
|
<th>规格</th>
|
||||||
|
<th>需求规格</th>
|
||||||
<th>数量</th>
|
<th>数量</th>
|
||||||
<th>计价方式</th>
|
<th>计价方式</th>
|
||||||
<th>成本单价</th>
|
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
<tr v-for="item in orderItems" :key="item.id">
|
<tr v-for="item in orderItems" :key="item.id">
|
||||||
<td>{{ item.productName }}</td>
|
<td>{{ item.productName }}</td>
|
||||||
<td>{{ item.specification }}</td>
|
<td>{{ item.specification }}</td>
|
||||||
|
<td>{{ item.demandSpecification || '-' }}</td>
|
||||||
<td>{{ item.quantity }}{{ item.unit }}</td>
|
<td>{{ item.quantity }}{{ item.unit }}</td>
|
||||||
<td>{{ getPricingTypeLabel(item.pricingType) }}</td>
|
<td>{{ getPricingTypeLabel(item.pricingType) }}</td>
|
||||||
<td>¥{{ item.costPrice }}</td>
|
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user