youhua
This commit is contained in:
109
web_ui/app.py
109
web_ui/app.py
@@ -40,6 +40,20 @@ DB_PATH.parent.mkdir(parents=True, exist_ok=True)
|
||||
# 抓取脚本目录 (属于代码文件,从 BASE_DIR 加载)
|
||||
BROWSER_LOGIN_DIR = BASE_DIR / "browser_login"
|
||||
|
||||
def auto_init_db():
|
||||
"""如果是新环境首次运行,自动初始化数据库表结构"""
|
||||
if str(BROWSER_LOGIN_DIR) not in sys.path:
|
||||
sys.path.insert(0, str(BROWSER_LOGIN_DIR))
|
||||
try:
|
||||
from import_to_sqlite import init_db
|
||||
conn = init_db()
|
||||
conn.close()
|
||||
except Exception as e:
|
||||
print(f"⚠️ 初始化数据库表结构失败: {e}")
|
||||
|
||||
# 在应用启动前执行初始化
|
||||
auto_init_db()
|
||||
|
||||
def background_sync_job():
|
||||
"""APScheduler 后台定时任务执行增量抓取"""
|
||||
print("[定时任务] 正在执行后台增量数据同步...")
|
||||
@@ -284,15 +298,19 @@ def get_bom_tree(parent_code):
|
||||
if material_codes:
|
||||
# 使用 IN 语句批量查询,并按物料分组取最新时间的价格
|
||||
placeholders = ','.join(['?'] * len(material_codes))
|
||||
# SQLite 分组取最新记录的经典写法
|
||||
# SQLite 分组取最新记录的安全写法(使用窗口函数 ROW_NUMBER)
|
||||
price_records = conn.execute(f'''
|
||||
SELECT material_code,
|
||||
ROUND(COALESCE(total_amount / NULLIF(receive_qty, 0), receive_price, 0), 2) AS receive_price,
|
||||
receipt_time, purchase_order_code, supplier_name
|
||||
FROM receipt_details
|
||||
WHERE material_code IN ({placeholders})
|
||||
GROUP BY material_code
|
||||
HAVING receipt_time = MAX(receipt_time)
|
||||
WITH RankedRecords AS (
|
||||
SELECT material_code,
|
||||
ROUND(COALESCE(total_amount / NULLIF(receive_qty, 0), receive_price, 0), 2) AS receive_price,
|
||||
receipt_time, purchase_order_code, supplier_name,
|
||||
ROW_NUMBER() OVER(PARTITION BY material_code ORDER BY receipt_time DESC, id DESC) as rn
|
||||
FROM receipt_details
|
||||
WHERE material_code IN ({placeholders})
|
||||
)
|
||||
SELECT material_code, receive_price, receipt_time, purchase_order_code, supplier_name
|
||||
FROM RankedRecords
|
||||
WHERE rn = 1
|
||||
''', material_codes).fetchall()
|
||||
|
||||
for r in price_records:
|
||||
@@ -478,15 +496,19 @@ def get_bom_tree_compare(parent_code):
|
||||
|
||||
# --- A. 查询历史最新价格及铸件重量/单位 ---
|
||||
latest_records = conn.execute(f'''
|
||||
SELECT material_code,
|
||||
unit_name,
|
||||
purchase_qty,
|
||||
receive_qty,
|
||||
ROUND(COALESCE(total_amount / NULLIF(receive_qty, 0), receive_price, 0), 2) AS receive_price
|
||||
FROM receipt_details
|
||||
WHERE material_code IN ({placeholders})
|
||||
GROUP BY material_code
|
||||
HAVING receipt_time = MAX(receipt_time)
|
||||
WITH RankedRecords AS (
|
||||
SELECT material_code,
|
||||
unit_name,
|
||||
purchase_qty,
|
||||
receive_qty,
|
||||
ROUND(COALESCE(total_amount / NULLIF(receive_qty, 0), receive_price, 0), 2) AS receive_price,
|
||||
ROW_NUMBER() OVER(PARTITION BY material_code ORDER BY receipt_time DESC, id DESC) as rn
|
||||
FROM receipt_details
|
||||
WHERE material_code IN ({placeholders})
|
||||
)
|
||||
SELECT material_code, unit_name, purchase_qty, receive_qty, receive_price
|
||||
FROM RankedRecords
|
||||
WHERE rn = 1
|
||||
''', unique_codes).fetchall()
|
||||
|
||||
for r in latest_records:
|
||||
@@ -510,13 +532,17 @@ def get_bom_tree_compare(parent_code):
|
||||
# 1. 先查询该期间内的最新收货价
|
||||
params = unique_codes + [start_date, end_date_full]
|
||||
period_latest_records = conn.execute(f'''
|
||||
SELECT material_code,
|
||||
ROUND(COALESCE(total_amount / NULLIF(receive_qty, 0), receive_price, 0), 2) AS receive_price
|
||||
FROM receipt_details
|
||||
WHERE material_code IN ({placeholders})
|
||||
AND receipt_time >= ? AND receipt_time <= ?
|
||||
GROUP BY material_code
|
||||
HAVING receipt_time = MAX(receipt_time)
|
||||
WITH RankedRecords AS (
|
||||
SELECT material_code,
|
||||
ROUND(COALESCE(total_amount / NULLIF(receive_qty, 0), receive_price, 0), 2) AS receive_price,
|
||||
ROW_NUMBER() OVER(PARTITION BY material_code ORDER BY receipt_time DESC, id DESC) as rn
|
||||
FROM receipt_details
|
||||
WHERE material_code IN ({placeholders})
|
||||
AND receipt_time >= ? AND receipt_time <= ?
|
||||
)
|
||||
SELECT material_code, receive_price
|
||||
FROM RankedRecords
|
||||
WHERE rn = 1
|
||||
''', params).fetchall()
|
||||
|
||||
found_codes = set()
|
||||
@@ -532,13 +558,17 @@ def get_bom_tree_compare(parent_code):
|
||||
missing_ph = ','.join(['?'] * len(missing_codes))
|
||||
fallback_params = missing_codes + [start_date]
|
||||
fallback_records = conn.execute(f'''
|
||||
SELECT material_code,
|
||||
ROUND(COALESCE(total_amount / NULLIF(receive_qty, 0), receive_price, 0), 2) AS receive_price
|
||||
FROM receipt_details
|
||||
WHERE material_code IN ({missing_ph})
|
||||
AND receipt_time < ?
|
||||
GROUP BY material_code
|
||||
HAVING receipt_time = MAX(receipt_time)
|
||||
WITH RankedRecords AS (
|
||||
SELECT material_code,
|
||||
ROUND(COALESCE(total_amount / NULLIF(receive_qty, 0), receive_price, 0), 2) AS receive_price,
|
||||
ROW_NUMBER() OVER(PARTITION BY material_code ORDER BY receipt_time DESC, id DESC) as rn
|
||||
FROM receipt_details
|
||||
WHERE material_code IN ({missing_ph})
|
||||
AND receipt_time < ?
|
||||
)
|
||||
SELECT material_code, receive_price
|
||||
FROM RankedRecords
|
||||
WHERE rn = 1
|
||||
''', fallback_params).fetchall()
|
||||
|
||||
fallback_found = set()
|
||||
@@ -631,10 +661,19 @@ def get_bom_tree_compare(parent_code):
|
||||
is_real_purchased_leaf = (not has_children) and (node['latestUnitPrice'] > 0 or node['periodAStatus'] != 'missing' or node['periodBStatus'] != 'missing')
|
||||
|
||||
if not has_children:
|
||||
# 叶子节点,总成本 = 单价 * 耗用量
|
||||
node['totalLatest'] = node['latestUnitPrice'] * usage_qty
|
||||
node['totalPeriodA'] = node['periodAUnitPrice'] * usage_qty
|
||||
node['totalPeriodB'] = node['periodBUnitPrice'] * usage_qty
|
||||
# 叶子节点,核心逻辑修改:区分是否是 1PZJ 铸件
|
||||
if code and code.startswith('1PZJ') and node.get('castingWeight'):
|
||||
# 铸件:总成本 = BOM 用量(件) * 单件重量(KG/件) * 采购单价(元/KG)
|
||||
multiplier = usage_qty * float(node['castingWeight'])
|
||||
node['calcType'] = 'casting'
|
||||
else:
|
||||
# 普通件:总成本 = BOM 用量 * 采购单价
|
||||
multiplier = usage_qty
|
||||
node['calcType'] = 'normal'
|
||||
|
||||
node['totalLatest'] = node['latestUnitPrice'] * multiplier
|
||||
node['totalPeriodA'] = node['periodAUnitPrice'] * multiplier
|
||||
node['totalPeriodB'] = node['periodBUnitPrice'] * multiplier
|
||||
|
||||
# 只要它是叶子节点,我们就给它标颜色。
|
||||
# 如果它真的是虚拟件(基准价是 0,且 A B 都是 missing),那就大大方方给它标红点
|
||||
|
||||
Reference in New Issue
Block a user