refactor: use highest unit price per material base in export
Co-authored-by: aider (openai/DeepSeek-V3.2-Thinking) <aider@aider.chat>
This commit is contained in:
@ -14,6 +14,7 @@ import datetime
|
|||||||
# 需要 pip install openpyxl
|
# 需要 pip install openpyxl
|
||||||
from openpyxl import Workbook
|
from openpyxl import Workbook
|
||||||
from openpyxl.styles import Font, Alignment, Border, Side, PatternFill
|
from openpyxl.styles import Font, Alignment, Border, Side, PatternFill
|
||||||
|
from collections import defaultdict
|
||||||
|
|
||||||
|
|
||||||
class MaterialBaseService:
|
class MaterialBaseService:
|
||||||
@ -411,15 +412,59 @@ class MaterialBaseService:
|
|||||||
query_product = query_product.filter(cond)
|
query_product = query_product.filter(cond)
|
||||||
list_product = query_product.all()
|
list_product = query_product.all()
|
||||||
|
|
||||||
|
# 2.4 计算每个物料基础的最高单价/成本
|
||||||
|
buy_max = defaultdict(float)
|
||||||
|
for stock, base in list_buy:
|
||||||
|
p = float(stock.pre_tax_unit_price or 0)
|
||||||
|
if p > buy_max[base.id]:
|
||||||
|
buy_max[base.id] = p
|
||||||
|
|
||||||
|
semi_max = defaultdict(float)
|
||||||
|
for stock, base in list_semi:
|
||||||
|
p = float(stock.raw_material_cost or 0) + float(stock.manual_cost or 0)
|
||||||
|
if p > semi_max[base.id]:
|
||||||
|
semi_max[base.id] = p
|
||||||
|
|
||||||
|
product_max = defaultdict(float)
|
||||||
|
for stock, base in list_product:
|
||||||
|
p = float(stock.raw_material_cost or 0) + float(stock.manual_cost or 0)
|
||||||
|
if p > product_max[base.id]:
|
||||||
|
product_max[base.id] = p
|
||||||
|
|
||||||
|
highest_price = {}
|
||||||
|
all_base_ids = set()
|
||||||
|
for stock, base in list_buy:
|
||||||
|
all_base_ids.add(base.id)
|
||||||
|
for stock, base in list_semi:
|
||||||
|
all_base_ids.add(base.id)
|
||||||
|
for stock, base in list_product:
|
||||||
|
all_base_ids.add(base.id)
|
||||||
|
|
||||||
|
for base_id in all_base_ids:
|
||||||
|
price = None
|
||||||
|
buy_val = buy_max.get(base_id)
|
||||||
|
if buy_val is not None and buy_val > 0:
|
||||||
|
price = buy_val
|
||||||
|
else:
|
||||||
|
semi_val = semi_max.get(base_id)
|
||||||
|
if semi_val is not None and semi_val > 0:
|
||||||
|
price = semi_val
|
||||||
|
else:
|
||||||
|
prod_val = product_max.get(base_id)
|
||||||
|
if prod_val is not None and prod_val > 0:
|
||||||
|
price = prod_val
|
||||||
|
highest_price[base_id] = price or 0.0
|
||||||
|
|
||||||
# 3. 数据整合
|
# 3. 数据整合
|
||||||
all_rows = []
|
all_rows = []
|
||||||
|
|
||||||
# 处理采购件
|
# 处理采购件
|
||||||
for stock, base in list_buy:
|
for stock, base in list_buy:
|
||||||
# 价格计算
|
# 价格计算:使用当前物料基础的最高单价
|
||||||
unit_price = float(stock.pre_tax_unit_price or 0)
|
unit_price = highest_price.get(base.id, 0.0)
|
||||||
tax_rate = float(stock.tax_rate or 0)
|
tax_rate = float(stock.tax_rate or 0)
|
||||||
price_incl = float(stock.post_tax_unit_price or (unit_price * (1 + tax_rate / 100.0)))
|
# 根据新单价重新计算含税单价
|
||||||
|
price_incl = unit_price * (1 + tax_rate / 100.0)
|
||||||
qty = float(stock.stock_quantity or 0)
|
qty = float(stock.stock_quantity or 0)
|
||||||
|
|
||||||
# 计算不含税总价 = 数量 * 不含税单价
|
# 计算不含税总价 = 数量 * 不含税单价
|
||||||
@ -447,13 +492,14 @@ class MaterialBaseService:
|
|||||||
|
|
||||||
# 处理半成品
|
# 处理半成品
|
||||||
for stock, base in list_semi:
|
for stock, base in list_semi:
|
||||||
cost = float(stock.raw_material_cost or 0) + float(stock.manual_cost or 0)
|
# 使用当前物料基础的最高单价
|
||||||
|
unit_price = highest_price.get(base.id, 0.0)
|
||||||
qty = float(stock.stock_quantity or 0)
|
qty = float(stock.stock_quantity or 0)
|
||||||
|
|
||||||
# 半成品不含税总价 = 数量 * 成本
|
# 半成品不含税总价 = 数量 * 单价
|
||||||
total_val_excl = qty * cost
|
total_val_excl = qty * unit_price
|
||||||
# 含税总价同上 (税率0)
|
# 含税总价同上 (税率0)
|
||||||
total_val_incl = qty * cost
|
total_val_incl = qty * unit_price
|
||||||
|
|
||||||
ident = stock.batch_number or stock.serial_number or stock.barcode or stock.sku
|
ident = stock.batch_number or stock.serial_number or stock.barcode or stock.sku
|
||||||
|
|
||||||
@ -466,20 +512,21 @@ class MaterialBaseService:
|
|||||||
"date": stock.production_date,
|
"date": stock.production_date,
|
||||||
"qty": qty,
|
"qty": qty,
|
||||||
"avail": float(stock.available_quantity or 0),
|
"avail": float(stock.available_quantity or 0),
|
||||||
"price_excl": cost,
|
"price_excl": unit_price,
|
||||||
"total_val_excl": total_val_excl,
|
"total_val_excl": total_val_excl,
|
||||||
"tax": 0.0,
|
"tax": 0.0,
|
||||||
"price_incl": cost,
|
"price_incl": unit_price,
|
||||||
"total_val": total_val_incl
|
"total_val": total_val_incl
|
||||||
})
|
})
|
||||||
|
|
||||||
# 处理成品
|
# 处理成品
|
||||||
for stock, base in list_product:
|
for stock, base in list_product:
|
||||||
cost = float(stock.raw_material_cost or 0) + float(stock.manual_cost or 0)
|
# 使用当前物料基础的最高单价
|
||||||
|
unit_price = highest_price.get(base.id, 0.0)
|
||||||
qty = float(stock.stock_quantity or 0)
|
qty = float(stock.stock_quantity or 0)
|
||||||
|
|
||||||
total_val_excl = qty * cost
|
total_val_excl = qty * unit_price
|
||||||
total_val_incl = qty * cost
|
total_val_incl = qty * unit_price
|
||||||
|
|
||||||
ident = stock.serial_number or stock.barcode or stock.sku
|
ident = stock.serial_number or stock.barcode or stock.sku
|
||||||
|
|
||||||
@ -492,10 +539,10 @@ class MaterialBaseService:
|
|||||||
"date": stock.production_date,
|
"date": stock.production_date,
|
||||||
"qty": qty,
|
"qty": qty,
|
||||||
"avail": float(stock.available_quantity or 0),
|
"avail": float(stock.available_quantity or 0),
|
||||||
"price_excl": cost,
|
"price_excl": unit_price,
|
||||||
"total_val_excl": total_val_excl,
|
"total_val_excl": total_val_excl,
|
||||||
"tax": 0.0,
|
"tax": 0.0,
|
||||||
"price_incl": cost,
|
"price_incl": unit_price,
|
||||||
"total_val": total_val_incl
|
"total_val": total_val_incl
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user