fix: correct BOM cost calculation by using raw SQL and manual_cost

Co-authored-by: aider (openai/DeepSeek-V3.2-Thinking) <aider@aider.chat>
This commit is contained in:
dxc
2026-03-02 11:47:44 +08:00
parent 7b0082c6e0
commit d3510b0261

View File

@ -399,33 +399,45 @@ class ProductInboundService:
def calculate_bom_cost(bom_no, bom_version): def calculate_bom_cost(bom_no, bom_version):
""" """
根据 BOM 编号和版本计算原材料总成本 根据 BOM 编号和版本计算原材料总成本
遍历 BOM 子件,取每个子件在采购、半成品、成品三个表中的最高单价,乘以用量后累加 遍历 BOM 子件,使用原生 SQL 查物理表 bom_table取每个子件在采购、半成品、成品三个表中的最高单价,乘以用量后累加
""" """
from app.models.bom import BomLine
from app.models.inbound.buy import StockBuy from app.models.inbound.buy import StockBuy
from app.models.inbound.semi import StockSemi from app.models.inbound.semi import StockSemi
from app.models.inbound.product import StockProduct from app.models.inbound.product import StockProduct
from sqlalchemy import func from sqlalchemy import func, text
try: try:
bom_lines = BomLine.query.filter( # 使用原生 SQL 精准查询 bom_table避免模型映射错误
BomLine.bom_no == bom_no, sql = text("""
BomLine.bom_version == bom_version SELECT child_id, dosage
).all() FROM bom_table
WHERE bom_no = :bom_no AND version = :version AND is_enabled = true
""")
bom_lines = db.session.execute(sql, {'bom_no': bom_no, 'version': bom_version}).fetchall()
total_cost = 0.0 total_cost = 0.0
for line in bom_lines: for line in bom_lines:
component_base_id = line.component_id component_base_id = line[0] # child_id
usage_qty = float(line.usage_quantity or 1.0) usage_qty = float(line[1] or 1.0) # dosage
# 1. 查采购表最高价 (不含税)
buy_price = db.session.query(func.max(StockBuy.pre_tax_unit_price)).filter( buy_price = db.session.query(func.max(StockBuy.pre_tax_unit_price)).filter(
StockBuy.base_id == component_base_id).scalar() or 0.0 StockBuy.base_id == component_base_id
semi_price = db.session.query(func.max(StockSemi.unit_total_cost)).filter( ).scalar() or 0.0
StockSemi.base_id == component_base_id).scalar() or 0.0
product_price = db.session.query(func.max(StockProduct.unit_total_cost)).filter(
StockProduct.base_id == component_base_id).scalar() or 0.0
max_price = max(buy_price, semi_price, product_price) # 2. 查半成品表最高价 (单件成本映射存在 manual_cost 里了)
semi_price = db.session.query(func.max(StockSemi.manual_cost)).filter(
StockSemi.base_id == component_base_id
).scalar() or 0.0
# 3. 查成品表最高价 (同样存储在 manual_cost 字段里)
product_price = db.session.query(func.max(StockProduct.manual_cost)).filter(
StockProduct.base_id == component_base_id
).scalar() or 0.0
# 4. 取三个表中的最大值,乘以用量 (dosage)
max_price = max(float(buy_price), float(semi_price), float(product_price))
total_cost += max_price * usage_qty total_cost += max_price * usage_qty
return round(total_cost, 2) return round(total_cost, 2)
except Exception as e: except Exception as e:
traceback.print_exc() traceback.print_exc()