- 新增 smart_read_excel 工具函数,统一 Excel 读取逻辑并自动选择引擎 - 重构 ConfigManager.get_path 方法,使用 pathlib 提升路径处理可靠性 - 将 GUI 日志处理改为异步队列模式,避免 UI 阻塞 - 优化 ExcelProcessor 的表头识别逻辑,避免重复读取文件 - 更新配置文件中的版本号
146 lines
7.5 KiB
Python
146 lines
7.5 KiB
Python
#!/usr/bin/env python
|
||
# -*- coding: utf-8 -*-
|
||
|
||
import os
|
||
import re
|
||
import time
|
||
import pandas as pd
|
||
import logging
|
||
from typing import Optional, Callable
|
||
from app.services.order_service import OrderService
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
class SpecialSuppliersService:
|
||
"""
|
||
处理特殊供应商逻辑的服务类,如蓉城易购等
|
||
"""
|
||
|
||
def __init__(self, config_manager=None):
|
||
self.config_manager = config_manager
|
||
self.order_service = OrderService(config_manager)
|
||
|
||
def process_rongcheng_yigou(self, src_path: str, progress_cb: Optional[Callable[[int, str], None]] = None) -> Optional[str]:
|
||
"""
|
||
处理蓉城易购订单
|
||
"""
|
||
try:
|
||
if progress_cb: progress_cb(10, "正在处理蓉城易购...")
|
||
|
||
def _pick_col(df, exact_list=None, contains_list=None):
|
||
cols = list(df.columns)
|
||
if exact_list:
|
||
for name in exact_list:
|
||
for c in cols:
|
||
if str(c).strip() == str(name).strip():
|
||
return c
|
||
if contains_list:
|
||
for kw in contains_list:
|
||
for c in cols:
|
||
if kw in str(c):
|
||
return c
|
||
return None
|
||
|
||
from app.core.utils.file_utils import smart_read_excel
|
||
try:
|
||
df_raw = smart_read_excel(src_path, header=2)
|
||
except Exception:
|
||
df_raw = smart_read_excel(src_path)
|
||
df_raw = df_raw.iloc[2:].reset_index(drop=True)
|
||
|
||
# 去除全空列与行
|
||
df_raw = df_raw.dropna(how='all', axis=1).dropna(how='all', axis=0)
|
||
|
||
# 选择关键列
|
||
col_no = _pick_col(df_raw, contains_list=['序号'])
|
||
col_name = _pick_col(df_raw, contains_list=['商品名称','品名','名称'])
|
||
col_bc = _pick_col(df_raw, contains_list=['商品条码','条码'])
|
||
col_unit = _pick_col(df_raw, exact_list=['单位(订购单位)'], contains_list=['订购单位','小单位','单位'])
|
||
col_qty = _pick_col(df_raw, contains_list=['订购数量','订货数量','数量'])
|
||
col_price= _pick_col(df_raw, exact_list=['优惠后金额(小单位)'], contains_list=['单价','销售价','进货价','优惠后金额'])
|
||
col_amt = _pick_col(df_raw, exact_list=['出库小计(元)'], contains_list=['金额','优惠后金额','小计','合计','出库小计'])
|
||
|
||
selected = [c for c in [col_no,col_name,col_bc,col_unit,col_qty,col_price,col_amt] if c]
|
||
|
||
if not selected or len(selected) < 4:
|
||
df = pd.read_excel(src_path)
|
||
df = df.iloc[2:].reset_index(drop=True)
|
||
keep_idx = [0, 2, 3, 9, 12, 15, 17]
|
||
keep_idx = [i for i in keep_idx if i < df.shape[1]]
|
||
df2 = df.iloc[:, keep_idx].copy()
|
||
target_cols = ['序号','商品名称','商品条码','单位','数量','单价','金额']
|
||
df2.columns = target_cols[:len(df2.columns)]
|
||
else:
|
||
df2 = df_raw[selected].copy()
|
||
rename_map = {}
|
||
if col_no: rename_map[col_no] = '序号'
|
||
if col_name: rename_map[col_name] = '商品名称'
|
||
if col_bc: rename_map[col_bc] = '商品条码(小条码)'
|
||
if col_unit: rename_map[col_unit] = '单位'
|
||
if col_qty: rename_map[col_qty] = '订购数量(小单位)'
|
||
if col_price: rename_map[col_price] = '单价(小单位)'
|
||
if col_amt: rename_map[col_amt] = '优惠后金额(小单位)'
|
||
df2 = df2.rename(columns=rename_map)
|
||
|
||
if '单位' in df2.columns:
|
||
df2['单位'] = df2['单位'].astype(str).str.strip().replace({'件':'份'})
|
||
|
||
# 分裂多条码行并均分数量
|
||
bc_col = '商品条码(小条码)' if '商品条码(小条码)' in df2.columns else ('商品条码' if '商品条码' in df2.columns else ('条码' if '条码' in df2.columns else None))
|
||
qty_col = '订购数量(小单位)' if '订购数量(小单位)' in df2.columns else ('订购数量' if '订购数量' in df2.columns else ('数量' if '数量' in df2.columns else None))
|
||
up_col = '单价(小单位)' if '单价(小单位)' in df2.columns else ('单价' if '单价' in df2.columns else ('销售价' if '销售价' in df2.columns else None))
|
||
amt_col = '优惠后金额(小单位)' if '优惠后金额(小单位)' in df2.columns else ('金额' if '金额' in df2.columns else ('小计' if '小计' in df2.columns else None))
|
||
|
||
if bc_col and qty_col:
|
||
rows = []
|
||
for _, row in df2.iterrows():
|
||
bc_val = str(row.get(bc_col, '')).strip()
|
||
if bc_val and any(sep in bc_val for sep in [',',',','、','/',' ']):
|
||
parts = []
|
||
temp_bc = bc_val
|
||
for sep in [',',',','、','/',' ']:
|
||
temp_bc = temp_bc.replace(sep, ' ')
|
||
for token in temp_bc.split():
|
||
tok = ''.join([ch for ch in token if ch.isdigit()])
|
||
if tok: parts.append(tok)
|
||
parts = [p for p in parts if p]
|
||
if len(parts) >= 2:
|
||
try:
|
||
q_total = float(row.get(qty_col, 0) or 0)
|
||
except Exception:
|
||
q_total = 0
|
||
if q_total > 0:
|
||
n = len(parts)
|
||
base = int(q_total) // n if q_total.is_integer() else q_total / n
|
||
remainder = int(q_total) % n if q_total.is_integer() else 0
|
||
for i, bc in enumerate(parts):
|
||
new_row = row.copy()
|
||
new_row[bc_col] = bc
|
||
q_each = base + (1 if remainder > 0 and i < remainder else 0)
|
||
new_row[qty_col] = q_each
|
||
if up_col and amt_col:
|
||
try:
|
||
upv = float(new_row.get(up_col, 0) or 0)
|
||
new_row[amt_col] = upv * float(q_each)
|
||
except Exception: pass
|
||
rows.append(new_row)
|
||
else: rows.append(row)
|
||
else: rows.append(row)
|
||
else: rows.append(row)
|
||
df2 = pd.DataFrame(rows)
|
||
|
||
out_dir = os.path.dirname(src_path)
|
||
base = os.path.basename(src_path)
|
||
final_name = f"蓉城易购预处理-{base}"
|
||
final_path = os.path.join(out_dir, final_name)
|
||
df2.to_excel(final_path, index=False)
|
||
|
||
if progress_cb: progress_cb(60, "预处理完成,开始标准流程...")
|
||
|
||
result = self.order_service.process_excel(final_path, progress_cb=lambda p: progress_cb(60 + int(p*0.4), "Excel处理中...") if progress_cb else None)
|
||
return result
|
||
|
||
except Exception as e:
|
||
logger.error(f"处理蓉城易购订单出错: {e}")
|
||
return None
|