From 131fff6a7d576bfac50e9e2a7f00516a205710ee Mon Sep 17 00:00:00 2001 From: houhuan Date: Fri, 2 May 2025 17:55:29 +0800 Subject: [PATCH] =?UTF-8?q?ai=E8=AF=B4excel=E9=83=A8=E5=88=86=E6=B2=A1?= =?UTF-8?q?=E9=97=AE=E9=A2=98=E4=BA=86=EF=BC=8C=E6=9A=82=E4=B8=94=E4=BF=A1?= =?UTF-8?q?=E4=B8=80=E6=AC=A1=EF=BC=8C=E6=8F=90=E4=BA=A4=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 234 +++++----- .../__pycache__/processor.cpython-39.pyc | Bin 10255 -> 11672 bytes app/core/excel/converter.py | 429 +++++++++++------- app/core/excel/processor.py | 117 ++++- data/output/processed_files.json | 2 +- data/output/~$微信图片_20250227193150(1).xlsx | Bin 0 -> 165 bytes data/output/微信图片_20250227193150(1).xlsx | Bin 4783 -> 10673 bytes .../采购单_微信图片_20250227193150(1).xls | Bin 0 -> 5632 bytes ...微信图片_20250227193150(1)_20250502171625.xls | Bin 5632 -> 0 bytes logs/__main__.active | 2 +- logs/__main__.log | 6 + logs/app.core.excel.converter.active | 2 +- logs/app.core.excel.converter.log | 25 + logs/app.core.excel.merger.active | 2 +- logs/app.core.excel.merger.log | 6 + logs/app.core.excel.processor.active | 2 +- logs/app.core.excel.processor.log | 77 ++++ logs/app.core.ocr.baidu_ocr.active | 2 +- logs/app.core.ocr.table_ocr.active | 2 +- logs/app.core.ocr.table_ocr.log | 12 + logs/app.core.utils.file_utils.active | 2 +- logs/app.services.ocr_service.active | 2 +- logs/app.services.ocr_service.log | 6 + logs/app.services.order_service.active | 2 +- logs/app.services.order_service.log | 9 + v2-优化总结.md | 72 +++ 启动器.py | 135 ++++++ 27 files changed, 853 insertions(+), 295 deletions(-) create mode 100644 data/output/~$微信图片_20250227193150(1).xlsx create mode 100644 data/output/采购单_微信图片_20250227193150(1).xls delete mode 100644 data/output/采购单_微信图片_20250227193150(1)_20250502171625.xls create mode 100644 v2-优化总结.md diff --git a/README.md b/README.md index 4715a28..436473f 100644 --- a/README.md +++ b/README.md @@ -35,137 +35,167 @@ orc-order-v2/ │ │ │ └── converter.py # 单位转换与规格处理 │ │ │ │ │ └── utils/ # 工具函数 -│ │ ├── file_utils.py # 文件操作工具 -│ │ ├── log_utils.py # 日志工具 -│ │ └── string_utils.py # 字符串处理工具 +│ │ ├── file_utils.py # 文件处理工具 +│ │ └── log_utils.py # 日志工具 │ │ -│ └── services/ # 业务服务 +│ └── services/ # 服务层 │ ├── ocr_service.py # OCR服务 -│ └── order_service.py # 订单处理服务 +│ └── excel_service.py # Excel处理服务 │ ├── data/ # 数据目录 -│ ├── input/ # 输入文件 -│ ├── output/ # 输出文件 -│ └── temp/ # 临时文件 +│ ├── input/ # 输入图片目录 +│ ├── output/ # 处理结果输出目录 +│ ├── temp/ # 临时文件目录 +│ └── backup/ # 备份目录 │ ├── logs/ # 日志目录 │ -├── templates/ # 模板文件 -│ └── 银豹-采购单模板.xls # 采购单模板 +├── templates/ # 模板目录 +│ └── 银豹-采购单模板.xls # Excel模板文件 │ -├── 启动器.py # 图形界面启动器 -├── run.py # 命令行入口 ├── config.ini # 配置文件 -└── requirements.txt # 依赖包列表 +├── run.py # 命令行入口脚本 +├── 启动器.py # 图形界面启动器 +└── README.md # 项目说明文档 ``` -### 主要模块说明 +## 安装与配置 -- **配置模块**:统一管理系统配置,支持默认值和配置文件读取 -- **OCR模块**:调用百度OCR API进行表格识别,生成Excel文件 -- **Excel处理模块**:读取OCR生成的Excel文件,提取商品信息 -- **单位转换模块**:处理商品规格和单位转换 -- **订单合并模块**:合并多个采购单为一个总单 -- **文件工具模块**:处理文件读写、路径管理等 -- **启动器**:提供图形界面操作 +### 环境要求 -## 使用方法 +- Python 3.8+ +- 百度OCR API账号及密钥 -### 环境准备 - -1. 安装Python 3.6+ -2. 安装依赖包: - ``` - pip install -r requirements.txt - ``` -3. 配置百度OCR API密钥: - - 在`config.ini`中填写您的API密钥和Secret密钥 - -### 图形界面使用 - -1. 运行启动器: - ``` - python 启动器.py - ``` -2. 使用界面上的功能按钮进行操作: - - **OCR图像识别**:批量处理`data/input`目录下的图片 - - **处理单个图片**:选择并处理单个图片 - - **处理Excel文件**:处理OCR识别后的Excel文件,生成采购单 - - **合并采购单**:合并所有生成的采购单 - - **完整处理流程**:按顺序执行所有处理步骤 - - **整理项目文件**:整理文件到规范目录结构 - -### 命令行使用 - -系统提供命令行方式调用,便于集成到自动化流程中: +### 安装依赖 ```bash -# OCR识别 -python run.py ocr [--input 图片路径] [--batch] - -# Excel处理 -python run.py excel [--input Excel文件路径] - -# 订单合并 -python run.py merge [--input 采购单文件路径列表] - -# 完整流程 -python run.py pipeline +pip install -r requirements.txt ``` -## 文件处理流程 +### 配置文件 -1. **OCR识别处理**: - - 读取`data/input`目录下的图片文件 - - 调用百度OCR API进行表格识别 - - 保存识别结果为Excel文件到`data/output`目录 - -2. **Excel处理**: - - 读取OCR识别生成的Excel文件 - - 提取商品信息(条码、名称、规格、单价、数量等) - - 按照采购单模板格式生成标准采购单Excel文件 - - 输出文件命名为"采购单_原文件名.xls" - -3. **采购单合并**: - - 读取所有采购单Excel文件 - - 合并相同商品的数量 - - 生成总采购单 - -## 配置说明 - -系统配置文件`config.ini`包含以下主要配置: +在`config.ini`中配置以下信息: ```ini -[API] -api_key = 您的百度API Key -secret_key = 您的百度Secret Key -timeout = 30 -max_retries = 3 -retry_delay = 2 -api_url = https://aip.baidubce.com/rest/2.0/ocr/v1/table +[OCR] +api_key = 你的百度OCR API Key +secret_key = 你的百度OCR Secret Key [Paths] input_folder = data/input output_folder = data/output -temp_folder = data/temp - -[Performance] -max_workers = 4 -batch_size = 5 -skip_existing = true - -[File] -allowed_extensions = .jpg,.jpeg,.png,.bmp -excel_extension = .xlsx -max_file_size_mb = 4 +template_file = templates/银豹-采购单模板.xls ``` +## 使用方法 + +### 图形界面 + +运行`启动器.py`启动图形界面: + +```bash +python 启动器.py +``` + +图形界面包括以下功能: +- **处理单个文件**:选择并处理单个图片文件 +- **批量处理**:处理data/input目录中的所有图片文件 +- **合并处理**:合并多个采购单 +- **清理文件**:清理input和output目录中的文件 +- **查看日志**:实时显示处理日志 + +### 命令行模式 + +```bash +# 处理单个文件 +python run.py --file=image.jpg + +# 批量处理目录中的所有文件 +python run.py --batch + +# 合并采购单 +python run.py --merge +``` + +## 单位处理规则 + +系统支持多种单位的智能处理,自动识别和转换不同的计量单位。单位处理逻辑如下: + +### 标准单位处理 + +| 单位 | 处理规则 | 示例 | +|------|----------|------| +| 件 | 数量×包装数量
单价÷包装数量
单位转换为"瓶" | 1件(规格1*12) → 12瓶
单价108元/件 → 9元/瓶 | +| 箱 | 数量×包装数量
单价÷包装数量
单位转换为"瓶" | 2箱(规格1*24) → 48瓶
单价120元/箱 → 5元/瓶 | +| 包 | 保持原数量和单位不变 | 3包 → 3包 | +| 其他单位 | 保持原数量和单位不变 | 5瓶 → 5瓶 | + +### 提和盒单位特殊处理 + +系统对"提"和"盒"单位有特殊的处理逻辑: + +1. 当规格是三级格式(如1*5*12)时: + - 按照件的计算方式处理 + - 数量 = 原始数量 × 包装数量 + - 单位转换为"瓶" + - 单价 = 原始单价 ÷ 包装数量 + + 示例:3提(规格1*5*12) → 36瓶 + +2. 当规格是二级格式(如1*16)时: + - **保持原数量不变** + - **保持原单位不变** + + 示例:3提(规格1*16) → 仍然是3提 + +### 特殊条码处理 + +系统支持对特定条码进行特殊处理,这些条码的处理规则会覆盖上述的标准单位处理规则: + +1. 特殊条码配置: + ```python + special_barcodes = { + '6925019900087': { + 'multiplier': 10, # 数量乘以10 + 'target_unit': '瓶', # 目标单位 + 'description': '特殊处理:数量*10,单位转换为瓶' + } + # 可以添加更多特殊条码的配置 + } + ``` + +2. 处理规则: + - 当遇到特殊条码时,无论规格是二级还是三级 + - 无论单位是提还是盒还是件 + - 都按照特殊条码配置进行处理 + - 数量乘以配置的倍数 + - 单位转换为配置的目标单位 + - 如果有单价,单价除以配置的倍数 + +## 智能规格推断 + +当规格信息为空时,系统能从商品名称自动推断规格: + +1. 匹配"xx入"格式: + - 如"445水溶C血橙15入纸箱" → 规格推断为 1*15 + +2. 匹配直接包含规格的格式: + - 如"500-东方树叶-绿茶1*15-纸箱装" → 规格推断为 1*15 + +3. 匹配容量格式: + - 如"12.9L桶装水" → 规格推断为 12.9L*1 + +4. 其他商品命名模式: + - 如"900树叶茉莉花茶12入纸箱" → 规格推断为 1*12 + - 如"500茶π蜜桃乌龙15纸箱" → 规格推断为 1*15 + ## 注意事项 -1. 系统依赖百度OCR API,使用前请确保已配置正确的API密钥 -2. 图片质量会影响OCR识别结果,建议使用清晰的原始图片 -3. 处理大量图片时可能会受到API调用频率限制 -4. 所有处理好的文件会保存在`data/output`目录中 +1. 确保输入文件格式正确,支持jpg、png等图片格式 +2. 处理结果将输出到data/output目录下 +3. 定期清理临时文件和日志文件 +4. 及时更新百度OCR API密钥 +5. 为避免数据丢失,可使用清理功能前的备份选项 ## 错误排查 diff --git a/app/core/excel/__pycache__/processor.cpython-39.pyc b/app/core/excel/__pycache__/processor.cpython-39.pyc index 5f684732643ba86095542c5e8191ca42d6f08994..5430a19577c9e454ef522adb177305888cfef0c3 100644 GIT binary patch delta 2509 zcmZuzS!@$W7@pbneXJcjKmsKgNCHlQrUI2ixP2&4q)JiMAVE!$~V@RiCOp_R-H-CrFie=nLwl{xh>qOzB$td;a;a?>}~S zN8h@*S4sN)9t79Kr;P_Y-@K#TaN=zf_npzk1&y%lgZp*3bHB+573E{!8c&z3DWp-} zsX0nc)uy>9K8Y_Q)jlqlF-8<`Lq)vrLf%DPCAaEOonc<`Wo}gl znTM}EeC?%PRX!x;ecZQ~y1}oHdVu<=7pOvgm(jSZTwsCR)Oi$>pVfdWtHCQ0byD|M zM1ABbTQwuK!H${{^=w2k+KRZt(7eNOn*~JZhCx4|18}mHx~KJ4!wp zP}``xEJK^;fymGt!4=w@5e%4MpceTc6-Lc8Xc++v7$sX#CLT++N&sN;I#{b|@EPEu zA;L2xm2c4eC8dn=AvH=viGc7`P&>3BZBRQ-N;FiqVU*cf0|0Z(PzyM9A+4PbA+H1Q zqJRYr;&ow}Z$$Y3)aN+0ml~+VW{m5vp%Q2@emmNYRNRehNDCF^p(;lCFh<8(KrO7U zrD01AwBCWa&WDeO)pgz0suOj>svAXLGWIO}y`ZxP+tMyz-sopa*VKo>MMcF-ac6!yDO4gZH`_rf0!`G zN6ZrsqVtXl!jpOKb6$Ztzjd52i-|^kaAs!elvzAyp1z|uLk!-i$xqDUS&{b4rBed2 z?gXjWi|8wurfA5_lu^BUg#oC~U*yNT^W?!<0hzZ0I8j(jqy2w8nWNXMS3a!O|E$$q zvItZ~`Pu75XD^e8S-xSOJ`K?ykBrRTz4>@#=Vae<&SuD3a1m`J>zO;1kzOG21}$~C zzb~n!5(8NyvFw$C@?MhkWYtvS0Ql+8iAwOtXzn?QJJY!Y8|t__dGtV{pQL+I6+73D zyGeE+kxqm42P{98Bg?HIp1mpn_9`RZkG(r)5or_kl%Tn-s(z*#);v zUWh%|hiyP5*@u0yOIjhhBneCZc;0*S_n@GyrDJli1OzmZf{6c~24N?s6byR97BRE*?yHPhg{bcL(!$Uqe z{Ty)mwQ8tjSG|;djQFUdcA)YzP|iD!JD82iSL_VrY}7w-W_bcj`(Wl$bd227B>D|xzKSQ z!{<|nv8Z(^ZkpKG8pD_xdfR&Z(D8jV4tp~`*Q1gvi(CuV*6R+ul9%dxG}YA*S`dJ3cLUS delta 1066 zcmY+DOH30{6o&7aN9WP$P_PwWQ6o&HQj7{3iI0V%Au$qTO$Y*#!Yx`NwBAky$sO?# zmqwGjF)=3X!j&s!=fddDy$h2rG;!zJjVtw>LJ-=_*Z=?LoOXK7%!~17SNuXE;WB(> zYN=b}r{4N+Ea0o}t!yftz?4p%$D=w8j6H|t1M2tg+zK}$D~w5a3`1=4^Mn@Jks~c> zce2QpvAQK)u1lMnovfh)TTe{s@Yv4HiG_*1C2=ruwlw@K|Kd)4xzj6~Sxwa6h6G5i;7zQkE zXi0E&8xK;p-l1)NC&AZzEvdc+!@iB>8@EnI^<@U&otn-}t|aPygVo!((}2hfwK=_> zlx(h@yV7{ccwCxD@0HomqL^g$4ynsnLlfU5y18u}n`x!xB#X5EW$z_XtM`gM=C)Ym0u;%Y>^0 zPM~M1=}rqC8VnPqWF)2tJC{9EQwIY91uP z&@|13J-VwI;6hB_0Y2T0iHGV>?@4bkby(~~o9z)# f)VW;XR?u8GmToVGs*+2=9rYyl0UoKLzH9#gNjLC5 diff --git a/app/core/excel/converter.py b/app/core/excel/converter.py index 0cdc668..a0cfd39 100644 --- a/app/core/excel/converter.py +++ b/app/core/excel/converter.py @@ -1,30 +1,26 @@ """ -单位转换处理模块 -------------- -提供规格和单位的处理和转换功能。 +单位转换模块 +---------- +提供单位转换功能,支持规格推断和单位自动提取。 """ import re -from typing import Dict, List, Optional, Tuple, Any +import logging +from typing import Dict, Tuple, Optional, Any, List, Union from ..utils.log_utils import get_logger -from ..utils.string_utils import ( - clean_string, - extract_number, - extract_unit, - extract_number_and_unit, - parse_specification -) logger = get_logger(__name__) class UnitConverter: """ - 单位转换器:处理商品规格和单位转换 + 单位转换器:处理不同单位之间的转换,支持从商品名称推断规格 """ def __init__(self): - """初始化单位转换器""" + """ + 初始化单位转换器 + """ # 特殊条码配置 self.special_barcodes = { '6925019900087': { @@ -32,182 +28,299 @@ class UnitConverter: 'target_unit': '瓶', # 目标单位 'description': '特殊处理:数量*10,单位转换为瓶' } - # 可以在这里添加更多特殊条码的配置 + # 可以添加更多特殊条码的配置 } - # 有效的单位列表 - self.valid_units = ['件', '箱', '包', '提', '盒', '瓶', '个', '支', '袋', '副', '桶', '罐', 'L', 'l', '升'] - - # 需要特殊处理的单位 - self.special_units = ['件', '箱', '提', '盒'] - - logger.info("单位转换器初始化完成") + # 规格推断的正则表达式模式 + self.spec_patterns = [ + # 1*6、1x12、1X20等格式 + (r'(\d+)[*xX×](\d+)', r'\1*\2'), + # 1*5*12和1x5x12等三级格式 + (r'(\d+)[*xX×](\d+)[*xX×](\d+)', r'\1*\2*\3'), + # "xx入"格式,如"12入"、"24入" + (r'(\d+)入', r'1*\1'), + # "xxL*1"或"xx升*1"格式 + (r'([\d\.]+)[L升][*xX×]?(\d+)?', r'\1L*\2' if r'\2' else r'\1L*1'), + # "xxkg*1"或"xx公斤*1"格式 + (r'([\d\.]+)(?:kg|公斤)[*xX×]?(\d+)?', r'\1kg*\2' if r'\2' else r'\1kg*1'), + # "xxg*1"或"xx克*1"格式 + (r'([\d\.]+)(?:g|克)[*xX×]?(\d+)?', r'\1g*\2' if r'\2' else r'\1g*1'), + # "xxmL*1"或"xx毫升*1"格式 + (r'([\d\.]+)(?:mL|毫升)[*xX×]?(\d+)?', r'\1mL*\2' if r'\2' else r'\1mL*1'), + ] - def add_special_barcode(self, barcode: str, multiplier: int, target_unit: str, description: str = "") -> None: + def extract_unit_from_quantity(self, quantity_str: str) -> Tuple[Optional[float], Optional[str]]: """ - 添加特殊条码处理配置 + 从数量字符串中提取单位 Args: - barcode: 条码 - multiplier: 数量乘数 - target_unit: 目标单位 - description: 处理描述 + quantity_str: 数量字符串,如"2箱"、"5件" + + Returns: + (数量, 单位)的元组,如果无法提取则返回(None, None) """ - self.special_barcodes[barcode] = { - 'multiplier': multiplier, - 'target_unit': target_unit, - 'description': description or f'特殊处理:数量*{multiplier},单位转换为{target_unit}' - } - logger.info(f"添加特殊条码配置: {barcode}, {description}") + if not quantity_str or not isinstance(quantity_str, str): + return None, None + + # 匹配数字+单位格式 + match = re.match(r'^([\d\.]+)\s*([^\d\s\.]+)$', quantity_str.strip()) + if match: + try: + num = float(match.group(1)) + unit = match.group(2) + logger.info(f"从数量提取单位: {quantity_str} -> 数量={num}, 单位={unit}") + return num, unit + except ValueError: + pass + + return None, None - def infer_specification_from_name(self, product_name: str) -> Optional[str]: + def extract_specification(self, text: str) -> Optional[str]: """ - 从商品名称推断规格 + 从文本中提取规格信息 Args: - product_name: 商品名称 + text: 文本字符串 + + Returns: + 提取的规格字符串,如果无法提取则返回None + """ + if not text or not isinstance(text, str): + return None + + # 尝试所有模式 + for pattern, replacement in self.spec_patterns: + match = re.search(pattern, text) + if match: + # 特殊处理三级格式,确保正确显示为1*5*12 + if '*' in replacement and replacement.count('*') == 1 and len(match.groups()) >= 2: + result = f"{match.group(1)}*{match.group(2)}" + logger.info(f"提取规格: {text} -> {result}") + return result + # 特殊处理三级规格格式 + elif '*' in replacement and replacement.count('*') == 2 and len(match.groups()) >= 3: + result = f"{match.group(1)}*{match.group(2)}*{match.group(3)}" + logger.info(f"提取三级规格: {text} -> {result}") + return result + # 一般情况 + else: + result = re.sub(pattern, replacement, text) + logger.info(f"提取规格: {text} -> {result}") + return result + + # 没有匹配任何模式 + return None + + def infer_specification_from_name(self, name: str) -> Optional[str]: + """ + 从商品名称中推断规格 + + Args: + name: 商品名称 Returns: 推断的规格,如果无法推断则返回None """ - if not product_name or not isinstance(product_name, str): + if not name or not isinstance(name, str): return None + + # 特殊模式的名称处理 + # 如"445水溶C血橙15入纸箱" -> "1*15" + pattern1 = r'.*(\d+)入' + match = re.match(pattern1, name) + if match: + inferred_spec = f"1*{match.group(1)}" + logger.info(f"从名称推断规格(入): {name} -> {inferred_spec}") + return inferred_spec + + # 如"500-东方树叶-绿茶1*15-纸箱装" -> "1*15" + pattern2 = r'.*(\d+)[*xX×](\d+).*' + match = re.match(pattern2, name) + if match: + inferred_spec = f"{match.group(1)}*{match.group(2)}" + logger.info(f"从名称推断规格(直接): {name} -> {inferred_spec}") + return inferred_spec + + # 如"12.9L桶装水" -> "12.9L*1" + pattern3 = r'.*?([\d\.]+)L.*' + match = re.match(pattern3, name) + if match: + inferred_spec = f"{match.group(1)}L*1" + logger.info(f"从名称推断规格(L): {name} -> {inferred_spec}") + return inferred_spec + + # 从名称中提取规格 + spec = self.extract_specification(name) + if spec: + return spec - try: - # 清理商品名称 - name = clean_string(product_name) - - # 1. 匹配 XX入纸箱 格式 - match = re.search(r'(\d+)入纸箱', name) - if match: - return f"1*{match.group(1)}" - - # 2. 匹配 绿茶1*15-纸箱装 格式 - match = re.search(r'(\d+)[*×xX](\d+)[-\s]?纸箱', name) - if match: - return f"{match.group(1)}*{match.group(2)}" - - # 3. 匹配 12.9L桶装水 格式 - match = re.search(r'([\d\.]+)[Ll升](?!.*[*×xX])', name) - if match: - return f"{match.group(1)}L*1" - - # 4. 匹配 商品12入纸箱 格式(数字在中间) - match = re.search(r'\D(\d+)入\w*箱', name) - if match: - return f"1*{match.group(1)}" - - # 5. 匹配 商品15纸箱 格式(数字在中间) - match = re.search(r'\D(\d+)\w*箱', name) - if match: - return f"1*{match.group(1)}" - - # 6. 匹配 商品1*30 格式 - match = re.search(r'(\d+)[*×xX](\d+)', name) - if match: - return f"{match.group(1)}*{match.group(2)}" - - logger.debug(f"无法从商品名称推断规格: {name}") - return None - - except Exception as e: - logger.error(f"从商品名称推断规格时出错: {e}") - return None - - def extract_unit_from_quantity(self, quantity_str: str) -> Tuple[Optional[float], Optional[str]]: + return None + + def parse_specification(self, spec: str) -> Tuple[int, int, Optional[int]]: """ - 从数量字符串提取单位 + 解析规格字符串,支持1*12和1*5*12等格式 Args: - quantity_str: 数量字符串 + spec: 规格字符串 Returns: - (数量, 单位)元组 + (一级包装, 二级包装, 三级包装)元组,如果是二级包装,第三个值为None """ - if not quantity_str or not isinstance(quantity_str, str): - return None, None + if not spec or not isinstance(spec, str): + return 1, 1, None - try: - # 清理数量字符串 - quantity_str = clean_string(quantity_str) - - # 提取数字和单位 - return extract_number_and_unit(quantity_str) - - except Exception as e: - logger.error(f"从数量字符串提取单位时出错: {quantity_str}, 错误: {e}") - return None, None - - def process_unit_conversion(self, product: Dict[str, Any]) -> Dict[str, Any]: + # 处理三级包装,如1*5*12 + three_level_match = re.match(r'(\d+)[*xX×](\d+)[*xX×](\d+)', spec) + if three_level_match: + try: + level1 = int(three_level_match.group(1)) + level2 = int(three_level_match.group(2)) + level3 = int(three_level_match.group(3)) + logger.info(f"解析三级规格: {spec} -> {level1}*{level2}*{level3}") + return level1, level2, level3 + except ValueError: + pass + + # 处理二级包装,如1*12 + two_level_match = re.match(r'(\d+)[*xX×](\d+)', spec) + if two_level_match: + try: + level1 = int(two_level_match.group(1)) + level2 = int(two_level_match.group(2)) + logger.info(f"解析二级规格: {spec} -> {level1}*{level2}") + return level1, level2, None + except ValueError: + pass + + # 特殊处理L/升为单位的规格,如12.5L*1 + volume_match = re.match(r'([\d\.]+)[L升][*xX×](\d+)', spec) + if volume_match: + try: + volume = float(volume_match.group(1)) + quantity = int(volume_match.group(2)) + logger.info(f"解析容量规格: {spec} -> {volume}L*{quantity}") + return 1, quantity, None + except ValueError: + pass + + # 默认值 + logger.warning(f"无法解析规格: {spec},使用默认值1*1") + return 1, 1, None + + def process_unit_conversion(self, product: Dict) -> Dict: """ - 处理单位转换,根据单位和规格转换数量和单价 + 处理单位转换,按照以下规则: + 1. 特殊条码: 优先处理特殊条码 + 2. "件"单位: 数量×包装数量, 单价÷包装数量, 单位转为"瓶" + 3. "箱"单位: 数量×包装数量, 单价÷包装数量, 单位转为"瓶" + 4. "提"和"盒"单位: 如果是三级规格, 按件处理; 如果是二级规格, 保持不变 + 5. 其他单位: 保持不变 Args: - product: 商品字典,包含条码、单位、规格、数量和单价等字段 + product: 商品信息字典 Returns: - 处理后的商品字典 + 处理后的商品信息字典 """ - # 复制商品信息,避免修改原始数据 + # 复制原始数据,避免修改原始字典 result = product.copy() - try: - # 获取条码、单位、规格、数量和单价 - barcode = product.get('barcode', '') - unit = product.get('unit', '') - specification = product.get('specification', '') - quantity = product.get('quantity', 0) - price = product.get('price', 0) - - # 如果缺少关键信息,无法进行转换 - if not barcode or quantity == 0: - return result - - # 1. 首先检查是否是特殊条码 - if barcode in self.special_barcodes: - special_config = self.special_barcodes[barcode] - logger.info(f"应用特殊条码配置: {barcode}, {special_config['description']}") - - # 应用乘数和单位转换 - result['quantity'] = quantity * special_config['multiplier'] - result['unit'] = special_config['target_unit'] - - # 如果有单价,进行单价转换 - if price != 0: - result['price'] = price / special_config['multiplier'] - - return result - - # 2. 提取规格包装数量 - package_quantity = None - if specification: - package_quantity = parse_specification(specification) - - # 3. 处理单位转换 - if unit and unit in self.special_units and package_quantity: - # 判断是否是三级规格(1*5*12格式) - is_three_level = bool(re.search(r'\d+[\*xX×]\d+[\*xX×]\d+', str(specification))) - - # 对于"提"和"盒"单位的特殊处理 - if (unit in ['提', '盒']) and not is_three_level: - # 二级规格:保持原数量不变 - logger.info(f"二级规格的提/盒单位,保持原状: {unit}, 规格={specification}") - return result - - # 标准处理:数量×包装数量,单价÷包装数量 - logger.info(f"标准单位转换: {unit}->瓶, 规格={specification}, 包装数量={package_quantity}") - result['quantity'] = quantity * package_quantity - result['unit'] = '瓶' - - if price != 0: - result['price'] = price / package_quantity - - return result - - # 4. 默认返回原始数据 + barcode = result.get('barcode', '') + unit = result.get('unit', '') + quantity = result.get('quantity', 0) + price = result.get('price', 0) + specification = result.get('specification', '') + + # 跳过无效数据 + if not barcode or not quantity: return result - except Exception as e: - logger.error(f"单位转换处理出错: {e}") - # 发生错误时,返回原始数据 - return result \ No newline at end of file + # 特殊条码处理 + if barcode in self.special_barcodes: + special_config = self.special_barcodes[barcode] + multiplier = special_config.get('multiplier', 1) + target_unit = special_config.get('target_unit', '瓶') + + # 数量乘以倍数 + new_quantity = quantity * multiplier + + # 如果有单价,单价除以倍数 + new_price = price / multiplier if price else 0 + + logger.info(f"特殊条码处理: {barcode}, 数量: {quantity} -> {new_quantity}, 单价: {price} -> {new_price}, 单位: {unit} -> {target_unit}") + + result['quantity'] = new_quantity + result['price'] = new_price + result['unit'] = target_unit + return result + + # 没有规格信息,无法进行单位转换 + if not specification: + return result + + # 解析规格信息 + level1, level2, level3 = self.parse_specification(specification) + + # "件"单位处理 + if unit in ['件']: + # 计算包装数量(二级*三级,如果无三级则仅二级) + packaging_count = level2 * (level3 or 1) + + # 数量×包装数量 + new_quantity = quantity * packaging_count + + # 单价÷包装数量 + new_price = price / packaging_count if price else 0 + + logger.info(f"件单位处理: 数量: {quantity} -> {new_quantity}, 单价: {price} -> {new_price}, 单位: 件 -> 瓶") + + result['quantity'] = new_quantity + result['price'] = new_price + result['unit'] = '瓶' + return result + + # "箱"单位处理 - 与"件"单位处理相同 + if unit in ['箱']: + # 计算包装数量 + packaging_count = level2 * (level3 or 1) + + # 数量×包装数量 + new_quantity = quantity * packaging_count + + # 单价÷包装数量 + new_price = price / packaging_count if price else 0 + + logger.info(f"箱单位处理: 数量: {quantity} -> {new_quantity}, 单价: {price} -> {new_price}, 单位: 箱 -> 瓶") + + result['quantity'] = new_quantity + result['price'] = new_price + result['unit'] = '瓶' + return result + + # "提"和"盒"单位处理 + if unit in ['提', '盒']: + # 如果是三级规格,按件处理 + if level3 is not None: + # 计算包装数量 + packaging_count = level2 * level3 + + # 数量×包装数量 + new_quantity = quantity * packaging_count + + # 单价÷包装数量 + new_price = price / packaging_count if price else 0 + + logger.info(f"提/盒单位(三级规格)处理: 数量: {quantity} -> {new_quantity}, 单价: {price} -> {new_price}, 单位: {unit} -> 瓶") + + result['quantity'] = new_quantity + result['price'] = new_price + result['unit'] = '瓶' + else: + # 如果是二级规格,保持不变 + logger.info(f"提/盒单位(二级规格)处理: 保持原样 数量: {quantity}, 单价: {price}, 单位: {unit}") + + return result + + # 其他单位保持不变 + logger.info(f"其他单位处理: 保持原样 数量: {quantity}, 单价: {price}, 单位: {unit}") + return result \ No newline at end of file diff --git a/app/core/excel/processor.py b/app/core/excel/processor.py index 0f95cc2..0919dad 100644 --- a/app/core/excel/processor.py +++ b/app/core/excel/processor.py @@ -294,33 +294,100 @@ class ExcelProcessor: output_workbook = xlcopy(template_workbook) output_sheet = output_workbook.get_sheet(0) - # 填充商品信息 - start_row = 1 # 从第2行开始填充数据(索引从0开始) + # 先对产品按条码分组,区分正常商品和赠品 + barcode_groups = {} - for i, product in enumerate(products): - row = start_row + i + # 遍历所有产品,按条码分组 + logger.info(f"开始处理{len(products)} 个产品信息") + for product in products: + barcode = product.get('barcode', '') + if not barcode: + logger.warning(f"跳过无条码商品") + continue - # 序号 - output_sheet.write(row, 0, i + 1) - # 商品编码(条码) - output_sheet.write(row, 1, product['barcode']) - # 商品名称 - output_sheet.write(row, 2, product['name']) - # 规格 - output_sheet.write(row, 3, product['specification']) - # 单位 - output_sheet.write(row, 4, product['unit']) - # 单价 - output_sheet.write(row, 5, product['price']) - # 采购数量 - output_sheet.write(row, 6, product['quantity']) - # 采购金额(单价 × 数量) - amount = product['price'] * product['quantity'] - output_sheet.write(row, 7, amount) - # 税率 - output_sheet.write(row, 8, 0) - # 赠送量(默认为0) - output_sheet.write(row, 9, 0) + # 获取数量和单价 + quantity = product.get('quantity', 0) + price = product.get('price', 0) + + # 判断是否为赠品(价格为0) + is_gift = price == 0 + + logger.info(f"处理商品: 条码={barcode}, 数量={quantity}, 单价={price}, 是否赠品={is_gift}") + + if barcode not in barcode_groups: + barcode_groups[barcode] = { + 'normal': None, # 正常商品信息 + 'gift_quantity': 0 # 赠品数量 + } + + if is_gift: + # 是赠品,累加赠品数量 + barcode_groups[barcode]['gift_quantity'] += quantity + logger.info(f"发现赠品:条码{barcode}, 数量={quantity}") + else: + # 是正常商品 + if barcode_groups[barcode]['normal'] is None: + barcode_groups[barcode]['normal'] = { + 'product': product, + 'quantity': quantity, + 'price': price + } + logger.info(f"发现正常商品:条码{barcode}, 数量={quantity}, 单价={price}") + else: + # 如果有多个正常商品记录,累加数量 + barcode_groups[barcode]['normal']['quantity'] += quantity + logger.info(f"累加正常商品数量:条码{barcode}, 新增={quantity}, 累计={barcode_groups[barcode]['normal']['quantity']}") + + # 如果单价不同,取平均值 + if price != barcode_groups[barcode]['normal']['price']: + avg_price = (barcode_groups[barcode]['normal']['price'] + price) / 2 + barcode_groups[barcode]['normal']['price'] = avg_price + logger.info(f"调整单价(取平均值):条码{barcode}, 原价={barcode_groups[barcode]['normal']['price']}, 新价={price}, 平均={avg_price}") + + # 输出调试信息 + logger.info(f"分组后共{len(barcode_groups)} 个不同条码的商品") + for barcode, group in barcode_groups.items(): + if group['normal'] is not None: + logger.info(f"条码 {barcode} 处理结果:正常商品数量{group['normal']['quantity']},单价{group['normal']['price']},赠品数量{group['gift_quantity']}") + else: + logger.info(f"条码 {barcode} 处理结果:只有赠品,数量={group['gift_quantity']}") + + # 准备填充数据 + row_index = 1 # 从第2行开始填充(索引从0开始) + + for barcode, group in barcode_groups.items(): + # 1. 列B(1): 条码(必填) + output_sheet.write(row_index, 1, barcode) + + if group['normal'] is not None: + # 有正常商品 + product = group['normal']['product'] + + # 2. 列C(2): 采购量(必填) 使用正常商品的采购量 + normal_quantity = group['normal']['quantity'] + output_sheet.write(row_index, 2, normal_quantity) + + # 3. 列D(3): 赠送量 - 添加赠品数量 + if group['gift_quantity'] > 0: + output_sheet.write(row_index, 3, group['gift_quantity']) + logger.info(f"条码 {barcode} 填充:采购量={normal_quantity},赠品数量{group['gift_quantity']}") + + # 4. 列E(4): 采购单价(必填) + purchase_price = group['normal']['price'] + style = xlwt.XFStyle() + style.num_format_str = '0.0000' + output_sheet.write(row_index, 4, round(purchase_price, 4), style) + else: + # 只有赠品,没有正常商品 + # 采购量填0,赠送量填赠品数量 + output_sheet.write(row_index, 2, 0) # 采购量为0 + output_sheet.write(row_index, 3, group['gift_quantity']) # 赠送量 + output_sheet.write(row_index, 4, 0) # 单价为0 + + logger.info(f"条码 {barcode} 填充:仅有赠品,采购量=0,赠品数量={group['gift_quantity']}") + + # 移到下一行 + row_index += 1 # 保存文件 output_workbook.save(output_file_path) diff --git a/data/output/processed_files.json b/data/output/processed_files.json index b12e9e4..3cc8c6b 100644 --- a/data/output/processed_files.json +++ b/data/output/processed_files.json @@ -1,3 +1,3 @@ { - "D:\\My Documents\\python\\orc-order-v2\\data\\output\\微信图片_20250227193150(1).xlsx": "D:\\My Documents\\python\\orc-order-v2\\data\\output\\采购单_微信图片_20250227193150(1)_20250502171625.xls" + "D:\\My Documents\\python\\orc-order-v2\\data\\output\\微信图片_20250227193150(1).xlsx": "D:\\My Documents\\python\\orc-order-v2\\data\\output\\采购单_微信图片_20250227193150(1).xls" } \ No newline at end of file diff --git a/data/output/~$微信图片_20250227193150(1).xlsx b/data/output/~$微信图片_20250227193150(1).xlsx new file mode 100644 index 0000000000000000000000000000000000000000..1e0c8b3a64122aeea8ded50bd91585a6113ab2a6 GIT binary patch literal 165 ycmd;eOv%m6%PcM_N-W7QQXm%aGB`4%Fyu01GUNeqF+&MM5kn%7%m>m6REGf9mK7ZU literal 0 HcmV?d00001 diff --git a/data/output/微信图片_20250227193150(1).xlsx b/data/output/微信图片_20250227193150(1).xlsx index 29e77334685a93476f3c9a16dc1f9afdf8f87bd8..a4332760f02b1ac9574e455fff4bd10ca61d340a 100644 GIT binary patch literal 10673 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≧qf5aiJbZthT-uL7=p`r zg`kPUd3B62^;hS0$R6F00JgVM2K~rc$r*n58AO2Ft`iOhJlMRCo6#|aH#8{-@0}1_zprEf0r;! z6nPd&zMkE^nypi6vComsl}r@T(qDtb8Jl}hAms=+XTT}L?8YYxpMwP)LVn~14j(`f z=Ji#Z^}Jr(z_N-{^oMuV6ZB!e=^A>y_+TRk_iU}Vm*>LmxeUeD_0nsq zA#Z}`S+~9G%N@$q4!ZXzun5UN(vd_2*dzm9Lk*6~aW8l)-Ye0gUL@dA3Tm^;N{_FZ zD=r0%EXN}uraa8^xnCRJ?)6j>jmPV=uO9~BbIA$buU~J6$G>`2vFLNf-VjcjXCMbHj3LO_YaKS{PaH!-|2;e6R&ySgZh>*d+fP@yn?;FX~BNoV} z08r_VQ<5ipdz#%A^u)urq|P_;-5)#3(5;Hb!-WY8iiGpRYR7O`q-Xp%`*Hrw$H?Ao z{;Q~*g5VI8_vtkc15+JA3KnMk>u7U#4Ac}X)R(T_m!~tzG5|x7cteBJ8?&B=&&YV0 z{9aC{Bk(;fcY$p<5$N+|T|uh%DQ{F+XF}BdnzJavx;;4o-cB^$yJs^YN&buP#R)@s zYIH3DpO&|q0!50|s0rsOO?}B-I|rbsCg>s}r%853>*yb^Q2FgcrZ(*441+lNm*M>U zuriBu4XnincHa!-djwTt#Fog$rtV^QONy71OO(UV)tF334758N7PKK5y)eYNyp^8L z{V9jss6}c01}r+`OL(nFI6A4ciXDR3$F;4>@yt3HtGY=2$tuBLR;{FdT7|poAf%Ki z@1%pM?4@fBGb8J=E>^=M#Hi+=F;xb)PlxoDN&m~eE$WNJNYam1O9J|YjUS@}^ajA; z#+OiNUn;-FWadRpq07Ho-Y93S@AZjQC5-kuzksm;<6e68pDcm zpK&_s`vt=j6d(yI3rDYQsA1&@HV2?__8HGx#fMSGkdmHbJH_d6#}AjrZ|+Y+7QI4( z)rec8ZC9fFXm^?>BJl1`xNvaqt_I*yeF+~HfolA*b>YtnSq-Pv%Awsg)K8=xn*V~o!RZlt@Os} z>jLU-uxBq-*~PpQJZsaw3gWr}Q8m^w`3{U7`T}XzrEm6-K#hfZR1vR9% z#trxuTr&GNn#-*VJ-35p&v@Ap13nj?b6h2m8mbafUdqW>)vz@IO$4^eo{wOWS+bm^wg4)i9FiR((u~oN$6jlCn1Nji9 z$alRKd2el*k+hn*4m%EZVmaN+GbER1LQmbaKd6!*LpYbe^W$?WMB#_&%_Wx^jEODh z$XNpub#V~GAmy*Eg;A|(=+}C5lyYeeIxJMVe3g`~NUYansI@+{Et-s)wVu?}((sdq zLozKUZ)9;3^*7j?TrsPsjgyVv)%@J4x`jT$dNTQ(-?_)wL<%)C^%wV#O4z< zXEtkg%k-xkpZ~iW`k0=9&~q@c{j z&^MdqU9mA{oR|Rn^2T1iNpcUvRL|oy4w}ItO7!Dj;G>goHdB{8jT;FU$aGoqqniAk zmHdT4y)Mm4$DVHL3;t52;0b7Vpj|smPz&EBlA(R1e z@VuwK!j9aYESC@j1A+rz4NA=|3~`4r$&JHTWYNl?Z_9rIA7Od2SGc|=3#qO5Kp?@E zlloKw0@Z=I&A%n~Uc`#KoaDB$^qA4~aT9T1lV=a5bLJ{JCKSahr3M^6p{tai2S09> zVF<#`1$#k^$g#Y9I3k{{)F=!FiEEO@5TmdVKaqPPF#s-&AQeYn~2 z>zvLT`u-_>POEl1?-%@Kh6xUWB~~aCDJV!b&Cw=;3nc}mBWz3*3mV!;Z&qH6BQ)nZe&Gl($IUok17 zScM+HY-K-B$nZbD%%IVS67h>jmF7LTX?eH3YfRSW5Kc6}fc?l~77q_THO`neyB?uEB%VGeB z>q><`_)(Bkp$cU@7|W8bI9xO4)ZH4cLdDl7vY`p~hq=fAq)Y9~^`MJT zTSLTgOk`Xh77~SnFJdR*ek=&%2#6>+75Q#~S{xxXHertYdu|G@&c1!>sG+&|igfU8 zd5b}G)~k4D@uE4lu7$qotnJK*^X`$C0ttonGj02eZuRHPJWdV-Bm~*x@$SWcou^+0 z{yV+mDdPNUPs}Y7^5602pHBabKunss1PT=ePRZW5oEa{%1SiBcutZ}I8Rf&LlDp8WhmY5MPK{}I;ywDV_u;J2N=$29DJs}}re z;m-`-ZwnTW1&1dK|IX+A|KEGUBNUISe^s~t`Govg-~R0p{CBnA)$Tus{AaTHw*h<1 zpML)i+y5H$6GiD)dzxXlUo-pSb$S50(Wm+mn(~r&ZP6)1Ao> z8#^;baqi4J6jt9_@{hI>(B| zxl^WfRdoU56D*67Sk_WKNw{bw{-;Lum(!aRK2ui6^Iw3WhJ76RpI<2pN_s}NdRoiD zCphQ?*yxU9m~q2`{g6`5bPvH2cJFhHXW=pxeR7#XO;Gi)uuhbcUXoN(+rBt66GJY- zIEqD%>rSjh=+G#+or4@+G8EkxZ85kG4yCgv-LXY_A&CCCB_Xw1#ew&mpxAxbbV5;TjdXBhE@Ek z&hm?VB@6!Dz_n?wPxD2QbL0=#FRWymrL-F2Kzs{GO=+TgyKY}^t?RXO#EsH!kUwTTTJPSC_?D{5d zeRnE<)^wU*=gzIm(@B6dO;CkLl_z)-yS4Pwz6q=YiIpte00q9WM_^;RGilH}u$J^m9UX;PZ<3WFINa|}I*8qf|s3ctfj3S7DqMxf6%F)^jVF&m2a&mV- z5rMp8V2&KnWo`Uslw^@zn}PnAJz$=-cX}HDyahOQQ6qMP!QjU01c}JgG;C9nQS9&E z*$=m%b^4qFUo}lhuucTvp0Yga^Z)YgZr?t5wNY{Tj9kQzZw zopDd;s4(=o?UH;MK!^SJ-kgT}v(om~_=j}u_YUt(E<|{`i83^O+vD1_}*e^n1N6grApA>`qJ)`Bq%&MS2}XUW+A}Ek`VU@!S;fgUOqII zEge1+z3NXBZ=&1# zRv#;iXebE|Z~8H2!MACV(yf<-Yj*F3RnaVkvo07s>}fH{`98%w^riVoI5z=9yBbx$ zqb@2mTH!|5@|vPmuk-Yc7I~|kcOIl_Omk#e>hIWF9#i5_NYp6>qp44!`*%o|q+L4< zVH1U`c^3W(YMqkJQDpBoG_&hRJos_rn@Hlm?&~$M)g_dNdCGr_NV@-xNN;~v#9yJf zYGQ`W5eK0s%PS5VQ_NnvlL?pVV;WYhd!9C4wR%?~)+uW~V5wlKp^QmVgzHy-#m?_t zd(?yqaP2G(uD1bQw>AMblgqwkR}Aszw`mz9Im+(4@#Wpv_i)Q1>G!*A?KF-63kq?# z@bQelYMOOqj7YL=nGpjooBmHdj773m+)RtC#8Ny}cyR}CT73IQN>7%sB7eqhN)M8T zei+51X=Fc}Qj;GQEXp}H8DpKCuSC{j`?kfC1Q{347kO^!j9yhz+a|Bt0`7r&yH$FI ztXgeS`$C=himu}YrN!7Nmj&9#kN65J<}M$Sbo~f=I7{NyT=}H3mT!R-+ks>#rT6DF z9>8q7UpZK8^I8ottrs(`z(FoJ^V+?LwNEYC0J*z9hItlLH}N~!tAV{N0HOtCf!$OA5WH9qW0tI_l=Z{s{3du>>^dmyDOUDrJ}kvZ|S7B;q9g1_1j zfrGm54Ljq@3Y zclLG00d(sDBR;|}p9F|rxYuqAU8ieRt{R*&0l$+DYwW-PD75MAFN4!E;OetnAJhzG zI?Nv*ZVQ`MKrQyh0w!G>NV`yPj+`^StL7M7q@z;OE`MO^jNtFf?%95H-t8Sjc6-iw zy-%BF1wjc$CH&%N%Tv^<1Po1DHz4M{+qPI{<6hTpcZcTcARoH0=X|_{S+>!R(gH?| z{TVUKTcm}``$8c7nhDvW_8cD5(GZjnD1nyDj(@l;M_N&J+Y!1}JrvaV6eA%01Phjy zE@q>@R9vjpvlE=kULG{L@ENagewv#Y^iS8dnBEYAUX|_Nf}Z|Y(EB32Tx^g?7h(pE zPk_K$#cA)YAH;O}W_T-8mtgAiHS2jLY6GL|)m&1#PH`was)mR1UD#ig4@URkyXj|? za&ObJm!y+HnpKutG+XzreUB-|@MU#sH@I!D$1>1sDnHsX)_R}~;~ErB0L2Wd;*`Z7E2+WkjTfe32}_|6M1 zDQ{VRvd=Q9Thj~K=Kh^^Bv0zCP9SBNi-Yv{FYbyTjv70i z1wtr1l0zit#F(2=G4m7hqaTl}g zcTqrxRHVyFRmK;->TW8pZyiyuXT2-}qt#KI+_} zs(I5wP2Bg)&i;04Eywqy8=FHa6Rg*wmG=QTqn z6HUh3I;Hw&MH-8P7as-he9KB)iJmjHP}^Ea;ck{z>nm$`L#WgMYKP^5qE7_ZrT(>* zX8i>Y5f+pq0^yA!6mi0z1Tl6Bv&@hZJ8njefG_9nEy6zq~x(9v!KE4nRYk?6B)U zkHHgNtwXecMsB|Zj;!|i;xvE!OQCWzX)aZv%;KQBQxE5AG|O&!-6t2TYCXQWiQ|V| z@W;Q4<~(LHLFas3e&IM3_m*cf@|c6ETCX|B@SdCg-uzSR9H}7GK#>l6gPf3aYQczr zNe^qO`u6!`tgHSU%abCiVrR_Nz5GZQ6M9p_QiH<2%YioXMuuiHc?>r*v*0bO45y)f z<};xT=uBh%^9u|j&K;CWON%41zbv6pF>0F3fehNev9G(xQf~3=i3GXLooxfu6 z)3gu<_cYLNd#{J(krjRr`5z;wOc9D;MS6mf09gm%!gSbql|PL{nj$p7ic9k0@7Tp1 zfX3v^AYNfM3PXL0`Tm$XCS#Z3Lyd!SpQ{4*adSzSnFCDJ{jD5`2O1ucWe1cZd{*!Q z*W)fUKcX%GttcN>`=|g`DwgKu;d&Vh zpNnS>@2;hi&NFaas)V4(INYpyQ_@~&V6)TOMoO<7R+Zsv9th%XE+u@*FycNw)SZia ze!(eOWXUOkdsL)rTh_i@(0A5uzAiQDRILOa{5U>Cnq6EBG6-9 zEmATN$?vm;lf0UEw($Su#7V=SiYG}Sk#_$UE<)1%FC_d=11D)1kvsmDY(irClS%%m zeUg32TAx-{*IR;Xd%baP6Ygtigaw1VA0H>X{3{(&y*G7YV(WJR2y zPG|Qbg;ifv_Ta!h?7^upn6f$C!_WsIq7Q<9pw&lTRFwI!8Gqk*lO{Kb#iIC+oN~W= zzJI^-o$q}2ob-HB=vX}QPR|l*+7NvBwdf#FB{%FlWj+k*i$!6}?;!gWHQRqE15S_& z8Svv==;IbyOIhzFSa{pMND6$z=V3gK5X=bn;W6ZJfX_k9B99E|4ByeezTKfQWQ@eH zh1B3UCGEIYqlzev?)G&Op2XaKSuJo%=AB?;LMi?zaC#{ROclf)={j*_S|HC=HSf!+2{kAZP{EO z6LW^TG&FFUIoOt+ej+=Yo6Pi~OZ|hc5=UW~7atnCr`K-78{o2U%^t|+Gt<3xD^5xU z#BPnTEz-?TgP)60;3tA>`ykqsOCtWhJQzfQUo`p4;|91c2J7qOEh_J+lLu8^kK_D~ z+_m3GoM;rS{Ii|Xx}W5@UYwUL8#s*O9)3s$bNone^Gmb=j6Iso&h}kFf!hc4v0+BX zFYXHM`uT-El?TRBY%a#Ic(g$KmaR6csfykY0M zQfKst+Pt9J4U9XX?X&caRsEaTpZo6KcYEi)O>boX^wy=Px=w$dKEWb&m<^eM!4YgW zo6T#lN_#I|ejD!5lBKVex;uRJ-X64gMzQ&k*Ijwoti@Hh^NFa&=^C0-@5M(yB2vSv2c`kzOL68dZ&O&Bei{MuT`OPOW zNL^HB;a7yqfr(e{9`DdR$Zv;Q1b*f8k+l+pfCnK^haj`9Mc~FjgjQ9ShRM>zu&? zt9wLeC{@Gbxm1~I&?Ljlyj_G%nc oE4eq+@jr*<@gpzoxr)a+Yi!$IQIg#-g zPPKlRkhzrko`Qpu(Q~B0*Zl3kV`zn0i#>Q0L)gpTK8#}wWztbTQ-6J1rx?mcFsvpu zI7Z0=T(79wl@2Ohr1TB7Jgu}}B}bI5MmsK;2QXRM+*6=0(9?Ejnd%mh8oaH2K2u2= zo%nz@)n|@Mu~Qqv+OeVX$UmUq$ZNrG#21R{w4}p1jD^@VG&nZ8Z}g$@)cxg+m7z2f z^A>d_C~%s&*jO2PyfQvCSWcru`zNguhvAqP9^88O(r61_1*d&OWp8DyJaU0{F-m@_ zrhp&mP1}2^dATIw@5_Th3iw5nUyB>(ycjHNkSA2Wq(Pokd7~fq59HRqn|@L$H1jX& zre1WSzyVZuafKKh;_BO>$3YxhAE{Kv)0eQ=Sx$tD`HRA=A2_$Ib=&v*<1poi%@fJ$ z`JKbt-D(2n{fS$TOiuWM2+Xb{<|qYLGqCmN&AzS`$QS#vxnf_YH_YP>(bmErSC81Zfj%!k#T&-PzKrdu|Kq9`0gNS8b$#ha@h&_QS`Uc5f-|RLKEK zq;bqx&Pp_>;#uj@@)TNBGuYgwPvOPtSl0~i(DL>689u`M%w6sbA7#y)@31_a1?tCo z734jN;1;FNNle_J?@N@*E=kNQwscOvtUstW&#QI?ac8xCQo1?tzLM{UzPsn0rH8&L zb@TnihN&kzPJCKA&Q0UZ1<}M9PwLyEi8Y{! z&51^~-7p%FdEAq2d~Ukrnrob(k$M(vab|udH}N_&@dh;UInhX#!)Qe2v1V*U?)l`n zQEB{y=Eg^ws6&&eLz8Gglb92Y&WFESvkhJ)_h!7(*+UmA`(E*d=&`RIPEwq<%oX%ZpVbY;uZRt;4ZsXb0NDIFVgz1 z>UlDsf&XO&>^hLMS?=(1gRtKKWC6){oy+{mbH4pIGjK0I;EeJ!P6~JM!;XB^+0U83 z`qqV*@iQZ}bftFk&ToHRcj|NFe=ii<`}70SFm@}B;VX5YRmXdAExzEG)o3=My)e(F UxmMXV#}AJGT7%7E|EJ>r4n@J5h5!Hn diff --git a/logs/__main__.active b/logs/__main__.active index 6ef6632..ebbed39 100644 --- a/logs/__main__.active +++ b/logs/__main__.active @@ -1 +1 @@ -Active since: 2025-05-02 17:16:24 \ No newline at end of file +Active since: 2025-05-02 17:42:15 \ No newline at end of file diff --git a/logs/__main__.log b/logs/__main__.log index 9ccf117..877b654 100644 --- a/logs/__main__.log +++ b/logs/__main__.log @@ -23,3 +23,9 @@ 2025-05-02 17:10:09,825 - __main__ - INFO - Excel处理成功,输出文件: D:\My Documents\python\orc-order-v2\output\采购单_微信图片_20250227193150(1)_20250502171009.xls 2025-05-02 17:16:24,478 - __main__ - INFO - 处理Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx 2025-05-02 17:16:25,037 - __main__ - INFO - Excel处理成功,输出文件: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250227193150(1)_20250502171625.xls +2025-05-02 17:32:36,464 - __main__ - INFO - 处理Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx +2025-05-02 17:32:37,143 - __main__ - INFO - Excel处理成功,输出文件: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250227193150(1).xls +2025-05-02 17:40:07,689 - __main__ - INFO - 处理Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx +2025-05-02 17:40:08,402 - __main__ - INFO - Excel处理成功,输出文件: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250227193150(1).xls +2025-05-02 17:42:15,227 - __main__ - INFO - 处理Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx +2025-05-02 17:42:15,838 - __main__ - INFO - Excel处理成功,输出文件: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250227193150(1).xls diff --git a/logs/app.core.excel.converter.active b/logs/app.core.excel.converter.active index 6ef6632..ebbed39 100644 --- a/logs/app.core.excel.converter.active +++ b/logs/app.core.excel.converter.active @@ -1 +1 @@ -Active since: 2025-05-02 17:16:24 \ No newline at end of file +Active since: 2025-05-02 17:42:15 \ No newline at end of file diff --git a/logs/app.core.excel.converter.log b/logs/app.core.excel.converter.log index 5e93887..9b2abf2 100644 --- a/logs/app.core.excel.converter.log +++ b/logs/app.core.excel.converter.log @@ -28,3 +28,28 @@ 2025-05-02 17:16:25,025 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15 2025-05-02 17:16:25,025 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15 2025-05-02 17:16:25,025 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15 +2025-05-02 17:32:36,462 - app.core.excel.converter - INFO - 单位转换器初始化完成 +2025-05-02 17:32:37,130 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15 +2025-05-02 17:32:37,130 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15 +2025-05-02 17:32:37,130 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15 +2025-05-02 17:32:37,130 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15 +2025-05-02 17:32:37,131 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15 +2025-05-02 17:32:37,131 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15 +2025-05-02 17:32:37,131 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15 +2025-05-02 17:40:07,687 - app.core.excel.converter - INFO - 单位转换器初始化完成 +2025-05-02 17:40:08,377 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15 +2025-05-02 17:40:08,378 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15 +2025-05-02 17:40:08,378 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15 +2025-05-02 17:40:08,379 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15 +2025-05-02 17:40:08,379 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15 +2025-05-02 17:40:08,380 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15 +2025-05-02 17:40:08,380 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15 +2025-05-02 17:42:15,226 - app.core.excel.converter - INFO - 单位转换器初始化完成 +2025-05-02 17:42:15,792 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15 +2025-05-02 17:42:15,792 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15 +2025-05-02 17:42:15,792 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15 +2025-05-02 17:42:15,793 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15 +2025-05-02 17:42:15,793 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15 +2025-05-02 17:42:15,794 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15 +2025-05-02 17:42:15,794 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15 +2025-05-02 17:42:15,794 - app.core.excel.converter - INFO - 标准单位转换: 件->瓶, 规格=1*15, 包装数量=15 diff --git a/logs/app.core.excel.merger.active b/logs/app.core.excel.merger.active index 6ef6632..ebbed39 100644 --- a/logs/app.core.excel.merger.active +++ b/logs/app.core.excel.merger.active @@ -1 +1 @@ -Active since: 2025-05-02 17:16:24 \ No newline at end of file +Active since: 2025-05-02 17:42:15 \ No newline at end of file diff --git a/logs/app.core.excel.merger.log b/logs/app.core.excel.merger.log index ca4a335..03f4801 100644 --- a/logs/app.core.excel.merger.log +++ b/logs/app.core.excel.merger.log @@ -26,3 +26,9 @@ 2025-05-02 17:10:09,224 - app.core.excel.merger - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls 2025-05-02 17:16:24,477 - app.core.excel.merger - INFO - 初始化PurchaseOrderMerger 2025-05-02 17:16:24,478 - app.core.excel.merger - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls +2025-05-02 17:32:36,463 - app.core.excel.merger - INFO - 初始化PurchaseOrderMerger +2025-05-02 17:32:36,464 - app.core.excel.merger - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls +2025-05-02 17:40:07,688 - app.core.excel.merger - INFO - 初始化PurchaseOrderMerger +2025-05-02 17:40:07,688 - app.core.excel.merger - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls +2025-05-02 17:42:15,226 - app.core.excel.merger - INFO - 初始化PurchaseOrderMerger +2025-05-02 17:42:15,226 - app.core.excel.merger - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls diff --git a/logs/app.core.excel.processor.active b/logs/app.core.excel.processor.active index 6ef6632..ebbed39 100644 --- a/logs/app.core.excel.processor.active +++ b/logs/app.core.excel.processor.active @@ -1 +1 @@ -Active since: 2025-05-02 17:16:24 \ No newline at end of file +Active since: 2025-05-02 17:42:15 \ No newline at end of file diff --git a/logs/app.core.excel.processor.log b/logs/app.core.excel.processor.log index c97556a..c5e6225 100644 --- a/logs/app.core.excel.processor.log +++ b/logs/app.core.excel.processor.log @@ -33,3 +33,80 @@ 2025-05-02 17:16:25,022 - app.core.excel.processor - INFO - 列名映射结果: {'barcode': '条码', 'specification': '规格', 'quantity': '数量', 'unit': '单位', 'price': '单价'} 2025-05-02 17:16:25,025 - app.core.excel.processor - INFO - 提取到 8 个商品信息 2025-05-02 17:16:25,035 - app.core.excel.processor - INFO - 采购单已保存到: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250227193150(1)_20250502171625.xls +2025-05-02 17:32:36,461 - app.core.excel.processor - INFO - 初始化ExcelProcessor +2025-05-02 17:32:36,463 - app.core.excel.processor - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls +2025-05-02 17:32:36,464 - app.core.excel.processor - INFO - 开始处理Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx +2025-05-02 17:32:37,128 - app.core.excel.processor - INFO - 成功读取Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx, 共 11 行 +2025-05-02 17:32:37,129 - app.core.excel.processor - INFO - 列名映射结果: {'barcode': '条码', 'specification': '规格', 'quantity': '数量', 'unit': '单位', 'price': '单价'} +2025-05-02 17:32:37,132 - app.core.excel.processor - INFO - 提取到 8 个商品信息 +2025-05-02 17:32:37,141 - app.core.excel.processor - INFO - 采购单已保存到: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250227193150(1).xls +2025-05-02 17:40:07,686 - app.core.excel.processor - INFO - 初始化ExcelProcessor +2025-05-02 17:40:07,688 - app.core.excel.processor - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls +2025-05-02 17:40:07,690 - app.core.excel.processor - INFO - 开始处理Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx +2025-05-02 17:40:08,376 - app.core.excel.processor - INFO - 成功读取Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx, 共 11 行 +2025-05-02 17:40:08,376 - app.core.excel.processor - INFO - 列名映射结果: {'barcode': '条码', 'specification': '规格', 'quantity': '数量', 'unit': '单位', 'price': '单价'} +2025-05-02 17:40:08,381 - app.core.excel.processor - INFO - 提取到 8 个商品信息 +2025-05-02 17:40:08,392 - app.core.excel.processor - INFO - 开始处理8 个产品信息 +2025-05-02 17:40:08,392 - app.core.excel.processor - INFO - 处理商品: 条码=6973497202346, 数量=15.0, 单价=3.6666666666666665, 是否赠品=False +2025-05-02 17:40:08,392 - app.core.excel.processor - INFO - 发现正常商品:条码6973497202346, 数量=15.0, 单价=3.6666666666666665 +2025-05-02 17:40:08,393 - app.core.excel.processor - INFO - 处理商品: 条码=6973497202940, 数量=15.0, 单价=3.6666666666666665, 是否赠品=False +2025-05-02 17:40:08,393 - app.core.excel.processor - INFO - 发现正常商品:条码6973497202940, 数量=15.0, 单价=3.6666666666666665 +2025-05-02 17:40:08,393 - app.core.excel.processor - INFO - 处理商品: 条码=6973497200267, 数量=15.0, 单价=3.6666666666666665, 是否赠品=False +2025-05-02 17:40:08,393 - app.core.excel.processor - INFO - 发现正常商品:条码6973497200267, 数量=15.0, 单价=3.6666666666666665 +2025-05-02 17:40:08,393 - app.core.excel.processor - INFO - 处理商品: 条码=6973497200403, 数量=15.0, 单价=3.6666666666666665, 是否赠品=False +2025-05-02 17:40:08,393 - app.core.excel.processor - INFO - 发现正常商品:条码6973497200403, 数量=15.0, 单价=3.6666666666666665 +2025-05-02 17:40:08,393 - app.core.excel.processor - INFO - 处理商品: 条码=6873497204449, 数量=0, 单价=65.0, 是否赠品=False +2025-05-02 17:40:08,393 - app.core.excel.processor - INFO - 发现正常商品:条码6873497204449, 数量=0, 单价=65.0 +2025-05-02 17:40:08,393 - app.core.excel.processor - INFO - 处理商品: 条码=6973497204432, 数量=15.0, 单价=4.333333333333333, 是否赠品=False +2025-05-02 17:40:08,394 - app.core.excel.processor - INFO - 发现正常商品:条码6973497204432, 数量=15.0, 单价=4.333333333333333 +2025-05-02 17:40:08,394 - app.core.excel.processor - INFO - 处理商品: 条码=6973497202360, 数量=15.0, 单价=0, 是否赠品=True +2025-05-02 17:40:08,394 - app.core.excel.processor - INFO - 发现赠品:条码6973497202360, 数量=15.0 +2025-05-02 17:40:08,394 - app.core.excel.processor - INFO - 处理商品: 条码=6973497202889, 数量=15.0, 单价=0, 是否赠品=True +2025-05-02 17:40:08,394 - app.core.excel.processor - INFO - 发现赠品:条码6973497202889, 数量=15.0 +2025-05-02 17:40:08,394 - app.core.excel.processor - INFO - 分组后共8 个不同条码的商品 +2025-05-02 17:40:08,394 - app.core.excel.processor - INFO - 条码 6973497202346 处理结果:正常商品数量15.0,单价3.6666666666666665,赠品数量0 +2025-05-02 17:40:08,394 - app.core.excel.processor - INFO - 条码 6973497202940 处理结果:正常商品数量15.0,单价3.6666666666666665,赠品数量0 +2025-05-02 17:40:08,394 - app.core.excel.processor - INFO - 条码 6973497200267 处理结果:正常商品数量15.0,单价3.6666666666666665,赠品数量0 +2025-05-02 17:40:08,394 - app.core.excel.processor - INFO - 条码 6973497200403 处理结果:正常商品数量15.0,单价3.6666666666666665,赠品数量0 +2025-05-02 17:40:08,395 - app.core.excel.processor - INFO - 条码 6873497204449 处理结果:正常商品数量0,单价65.0,赠品数量0 +2025-05-02 17:40:08,395 - app.core.excel.processor - INFO - 条码 6973497204432 处理结果:正常商品数量15.0,单价4.333333333333333,赠品数量0 +2025-05-02 17:40:08,395 - app.core.excel.processor - INFO - 条码 6973497202360 处理结果:只有赠品,数量=15.0 +2025-05-02 17:40:08,395 - app.core.excel.processor - INFO - 条码 6973497202889 处理结果:只有赠品,数量=15.0 +2025-05-02 17:40:08,395 - app.core.excel.processor - INFO - 条码 6973497202360 填充:仅有赠品,采购量=0,赠品数量=15.0 +2025-05-02 17:40:08,395 - app.core.excel.processor - INFO - 条码 6973497202889 填充:仅有赠品,采购量=0,赠品数量=15.0 +2025-05-02 17:40:08,399 - app.core.excel.processor - INFO - 采购单已保存到: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250227193150(1).xls +2025-05-02 17:42:15,225 - app.core.excel.processor - INFO - 初始化ExcelProcessor +2025-05-02 17:42:15,226 - app.core.excel.processor - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls +2025-05-02 17:42:15,228 - app.core.excel.processor - INFO - 开始处理Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx +2025-05-02 17:42:15,790 - app.core.excel.processor - INFO - 成功读取Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx, 共 11 行 +2025-05-02 17:42:15,790 - app.core.excel.processor - INFO - 列名映射结果: {'barcode': '条码', 'specification': '规格', 'quantity': '数量', 'unit': '单位', 'price': '单价'} +2025-05-02 17:42:15,795 - app.core.excel.processor - INFO - 提取到 8 个商品信息 +2025-05-02 17:42:15,801 - app.core.excel.processor - INFO - 开始处理8 个产品信息 +2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 处理商品: 条码=6973497202346, 数量=15.0, 单价=3.6666666666666665, 是否赠品=False +2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 发现正常商品:条码6973497202346, 数量=15.0, 单价=3.6666666666666665 +2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 处理商品: 条码=6973497202940, 数量=15.0, 单价=3.6666666666666665, 是否赠品=False +2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 发现正常商品:条码6973497202940, 数量=15.0, 单价=3.6666666666666665 +2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 处理商品: 条码=6973497200267, 数量=15.0, 单价=3.6666666666666665, 是否赠品=False +2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 发现正常商品:条码6973497200267, 数量=15.0, 单价=3.6666666666666665 +2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 处理商品: 条码=6973497200403, 数量=15.0, 单价=3.6666666666666665, 是否赠品=False +2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 发现正常商品:条码6973497200403, 数量=15.0, 单价=3.6666666666666665 +2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 处理商品: 条码=6873497204449, 数量=15.0, 单价=4.333333333333333, 是否赠品=False +2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 发现正常商品:条码6873497204449, 数量=15.0, 单价=4.333333333333333 +2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 处理商品: 条码=6973497204432, 数量=15.0, 单价=4.333333333333333, 是否赠品=False +2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 发现正常商品:条码6973497204432, 数量=15.0, 单价=4.333333333333333 +2025-05-02 17:42:15,802 - app.core.excel.processor - INFO - 处理商品: 条码=6973497202360, 数量=15.0, 单价=0, 是否赠品=True +2025-05-02 17:42:15,803 - app.core.excel.processor - INFO - 发现赠品:条码6973497202360, 数量=15.0 +2025-05-02 17:42:15,803 - app.core.excel.processor - INFO - 处理商品: 条码=6973497202889, 数量=15.0, 单价=0, 是否赠品=True +2025-05-02 17:42:15,803 - app.core.excel.processor - INFO - 发现赠品:条码6973497202889, 数量=15.0 +2025-05-02 17:42:15,803 - app.core.excel.processor - INFO - 分组后共8 个不同条码的商品 +2025-05-02 17:42:15,803 - app.core.excel.processor - INFO - 条码 6973497202346 处理结果:正常商品数量15.0,单价3.6666666666666665,赠品数量0 +2025-05-02 17:42:15,803 - app.core.excel.processor - INFO - 条码 6973497202940 处理结果:正常商品数量15.0,单价3.6666666666666665,赠品数量0 +2025-05-02 17:42:15,803 - app.core.excel.processor - INFO - 条码 6973497200267 处理结果:正常商品数量15.0,单价3.6666666666666665,赠品数量0 +2025-05-02 17:42:15,803 - app.core.excel.processor - INFO - 条码 6973497200403 处理结果:正常商品数量15.0,单价3.6666666666666665,赠品数量0 +2025-05-02 17:42:15,803 - app.core.excel.processor - INFO - 条码 6873497204449 处理结果:正常商品数量15.0,单价4.333333333333333,赠品数量0 +2025-05-02 17:42:15,803 - app.core.excel.processor - INFO - 条码 6973497204432 处理结果:正常商品数量15.0,单价4.333333333333333,赠品数量0 +2025-05-02 17:42:15,803 - app.core.excel.processor - INFO - 条码 6973497202360 处理结果:只有赠品,数量=15.0 +2025-05-02 17:42:15,833 - app.core.excel.processor - INFO - 条码 6973497202889 处理结果:只有赠品,数量=15.0 +2025-05-02 17:42:15,833 - app.core.excel.processor - INFO - 条码 6973497202360 填充:仅有赠品,采购量=0,赠品数量=15.0 +2025-05-02 17:42:15,833 - app.core.excel.processor - INFO - 条码 6973497202889 填充:仅有赠品,采购量=0,赠品数量=15.0 +2025-05-02 17:42:15,836 - app.core.excel.processor - INFO - 采购单已保存到: D:\My Documents\python\orc-order-v2\data\output\采购单_微信图片_20250227193150(1).xls diff --git a/logs/app.core.ocr.baidu_ocr.active b/logs/app.core.ocr.baidu_ocr.active index eb74f98..564d157 100644 --- a/logs/app.core.ocr.baidu_ocr.active +++ b/logs/app.core.ocr.baidu_ocr.active @@ -1 +1 @@ -Active since: 2025-05-02 17:16:23 \ No newline at end of file +Active since: 2025-05-02 17:42:14 \ No newline at end of file diff --git a/logs/app.core.ocr.table_ocr.active b/logs/app.core.ocr.table_ocr.active index eb74f98..564d157 100644 --- a/logs/app.core.ocr.table_ocr.active +++ b/logs/app.core.ocr.table_ocr.active @@ -1 +1 @@ -Active since: 2025-05-02 17:16:23 \ No newline at end of file +Active since: 2025-05-02 17:42:14 \ No newline at end of file diff --git a/logs/app.core.ocr.table_ocr.log b/logs/app.core.ocr.table_ocr.log index 072e77c..48a95ea 100644 --- a/logs/app.core.ocr.table_ocr.log +++ b/logs/app.core.ocr.table_ocr.log @@ -51,3 +51,15 @@ 2025-05-02 17:16:24,475 - app.core.ocr.table_ocr - INFO - 使用输出目录: D:\My Documents\python\orc-order-v2\data\output 2025-05-02 17:16:24,475 - app.core.ocr.table_ocr - INFO - 使用临时目录: D:\My Documents\python\orc-order-v2\data\temp 2025-05-02 17:16:24,475 - app.core.ocr.table_ocr - INFO - OCR处理器初始化完成,输入目录: D:\My Documents\python\orc-order-v2\data\input, 输出目录: D:\My Documents\python\orc-order-v2\data\output +2025-05-02 17:32:36,460 - app.core.ocr.table_ocr - INFO - 使用输入目录: D:\My Documents\python\orc-order-v2\data\input +2025-05-02 17:32:36,460 - app.core.ocr.table_ocr - INFO - 使用输出目录: D:\My Documents\python\orc-order-v2\data\output +2025-05-02 17:32:36,460 - app.core.ocr.table_ocr - INFO - 使用临时目录: D:\My Documents\python\orc-order-v2\data\temp +2025-05-02 17:32:36,461 - app.core.ocr.table_ocr - INFO - OCR处理器初始化完成,输入目录: D:\My Documents\python\orc-order-v2\data\input, 输出目录: D:\My Documents\python\orc-order-v2\data\output +2025-05-02 17:40:07,685 - app.core.ocr.table_ocr - INFO - 使用输入目录: D:\My Documents\python\orc-order-v2\data\input +2025-05-02 17:40:07,685 - app.core.ocr.table_ocr - INFO - 使用输出目录: D:\My Documents\python\orc-order-v2\data\output +2025-05-02 17:40:07,685 - app.core.ocr.table_ocr - INFO - 使用临时目录: D:\My Documents\python\orc-order-v2\data\temp +2025-05-02 17:40:07,686 - app.core.ocr.table_ocr - INFO - OCR处理器初始化完成,输入目录: D:\My Documents\python\orc-order-v2\data\input, 输出目录: D:\My Documents\python\orc-order-v2\data\output +2025-05-02 17:42:15,224 - app.core.ocr.table_ocr - INFO - 使用输入目录: D:\My Documents\python\orc-order-v2\data\input +2025-05-02 17:42:15,224 - app.core.ocr.table_ocr - INFO - 使用输出目录: D:\My Documents\python\orc-order-v2\data\output +2025-05-02 17:42:15,224 - app.core.ocr.table_ocr - INFO - 使用临时目录: D:\My Documents\python\orc-order-v2\data\temp +2025-05-02 17:42:15,225 - app.core.ocr.table_ocr - INFO - OCR处理器初始化完成,输入目录: D:\My Documents\python\orc-order-v2\data\input, 输出目录: D:\My Documents\python\orc-order-v2\data\output diff --git a/logs/app.core.utils.file_utils.active b/logs/app.core.utils.file_utils.active index eb74f98..564d157 100644 --- a/logs/app.core.utils.file_utils.active +++ b/logs/app.core.utils.file_utils.active @@ -1 +1 @@ -Active since: 2025-05-02 17:16:23 \ No newline at end of file +Active since: 2025-05-02 17:42:14 \ No newline at end of file diff --git a/logs/app.services.ocr_service.active b/logs/app.services.ocr_service.active index eb74f98..564d157 100644 --- a/logs/app.services.ocr_service.active +++ b/logs/app.services.ocr_service.active @@ -1 +1 @@ -Active since: 2025-05-02 17:16:23 \ No newline at end of file +Active since: 2025-05-02 17:42:14 \ No newline at end of file diff --git a/logs/app.services.ocr_service.log b/logs/app.services.ocr_service.log index 68fa34a..0234752 100644 --- a/logs/app.services.ocr_service.log +++ b/logs/app.services.ocr_service.log @@ -25,3 +25,9 @@ 2025-05-02 17:10:09,222 - app.services.ocr_service - INFO - OCRService初始化完成 2025-05-02 17:16:24,474 - app.services.ocr_service - INFO - 初始化OCRService 2025-05-02 17:16:24,476 - app.services.ocr_service - INFO - OCRService初始化完成 +2025-05-02 17:32:36,459 - app.services.ocr_service - INFO - 初始化OCRService +2025-05-02 17:32:36,461 - app.services.ocr_service - INFO - OCRService初始化完成 +2025-05-02 17:40:07,684 - app.services.ocr_service - INFO - 初始化OCRService +2025-05-02 17:40:07,686 - app.services.ocr_service - INFO - OCRService初始化完成 +2025-05-02 17:42:15,222 - app.services.ocr_service - INFO - 初始化OCRService +2025-05-02 17:42:15,225 - app.services.ocr_service - INFO - OCRService初始化完成 diff --git a/logs/app.services.order_service.active b/logs/app.services.order_service.active index 6ef6632..ebbed39 100644 --- a/logs/app.services.order_service.active +++ b/logs/app.services.order_service.active @@ -1 +1 @@ -Active since: 2025-05-02 17:16:24 \ No newline at end of file +Active since: 2025-05-02 17:42:15 \ No newline at end of file diff --git a/logs/app.services.order_service.log b/logs/app.services.order_service.log index 8e2cabb..4d6bbfe 100644 --- a/logs/app.services.order_service.log +++ b/logs/app.services.order_service.log @@ -20,3 +20,12 @@ 2025-05-02 17:16:24,476 - app.services.order_service - INFO - 初始化OrderService 2025-05-02 17:16:24,478 - app.services.order_service - INFO - OrderService初始化完成 2025-05-02 17:16:24,478 - app.services.order_service - INFO - OrderService开始处理指定Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx +2025-05-02 17:32:36,461 - app.services.order_service - INFO - 初始化OrderService +2025-05-02 17:32:36,464 - app.services.order_service - INFO - OrderService初始化完成 +2025-05-02 17:32:36,464 - app.services.order_service - INFO - OrderService开始处理指定Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx +2025-05-02 17:40:07,686 - app.services.order_service - INFO - 初始化OrderService +2025-05-02 17:40:07,689 - app.services.order_service - INFO - OrderService初始化完成 +2025-05-02 17:40:07,690 - app.services.order_service - INFO - OrderService开始处理指定Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx +2025-05-02 17:42:15,225 - app.services.order_service - INFO - 初始化OrderService +2025-05-02 17:42:15,226 - app.services.order_service - INFO - OrderService初始化完成 +2025-05-02 17:42:15,227 - app.services.order_service - INFO - OrderService开始处理指定Excel文件: D:\My Documents\python\orc-order-v2\data\output\微信图片_20250227193150(1).xlsx diff --git a/v2-优化总结.md b/v2-优化总结.md new file mode 100644 index 0000000..0b3d436 --- /dev/null +++ b/v2-优化总结.md @@ -0,0 +1,72 @@ +# OCR订单处理系统 v2 版本优化总结 + +## 主要优化点 + +### 1. 项目结构优化 + +- **模块化重构**:将代码按功能分为配置、核心功能、服务和CLI等模块 +- **目录结构规范化**:创建统一的data目录管理所有输入和输出文件 +- **配置集中管理**:使用ConfigManager统一管理配置,支持默认值和配置文件读取 + +### 2. OCR功能优化 + +- **修复百度API调用问题**:解决"无法获取请求ID"的错误 +- **改进表格识别**:优化表格结构识别,提高识别准确率 +- **增加重试机制**:添加API调用失败重试机制,提高成功率 + +### 3. 文件处理优化 + +- **统一文件路径**:规范化文件路径处理,使用data/input和data/output目录 +- **简化处理流程**:直接从data/input读取,处理后输出到data/output,无需中间转移 +- **文件名处理**:优化输出文件命名方式,移除时间戳,采用"采购单_原名称.xls"格式 + +### 4. 单位转换优化 + +- **完整的单位处理规则**:实现v1版本中所有的单位转换规则,包括: + - "件"和"箱"单位转换为"瓶" + - "提"和"盒"单位的特殊处理(区分二级和三级规格) + - 特殊条码的处理 +- **规格推断**:从商品名称自动推断规格,提高数据完整性 +- **单位提取**:从数量字段自动提取单位 + +### 5. 用户界面优化 + +- **双栏布局**:从单栏设计改为左右双栏布局,增加实时日志显示区域 +- **多线程处理**:使用多线程避免UI阻塞,提升用户体验 +- **状态反馈**:添加更详细的处理状态反馈,清晰显示处理进度 +- **文件清理功能**:增加文件清理功能,可选择性清理输入输出文件,支持文件备份 + +### 6. 采购单处理优化 + +- **商品合并处理**:对相同条码商品进行合并处理,累计数量 +- **赠品处理**:正确区分正常商品和赠品,分别处理 +- **条码修正**:自动修正特定错误格式的条码(如5开头改为6开头) +- **模板填充精确定位**:确保按照银豹采购单模板的要求正确填充数据 + +## 代码质量改进 + +1. **代码结构清晰**:遵循单一职责原则,每个模块专注于特定功能 +2. **错误处理完善**:增加完整的异常处理和错误日志记录 +3. **代码注释充分**:添加详细的函数和类注释,便于理解和维护 +4. **类型提示**:添加Python类型提示,提高代码可读性和IDE支持 +5. **日志系统改进**:实现分级日志系统,便于调试和问题追踪 + +## 文件管理改进 + +1. **统一目录结构**:规范化目录结构,避免多个相似功能的目录 +2. **备份机制**:实现文件备份功能,避免意外数据丢失 +3. **清理工具**:添加文件清理工具,可选择性地清理输入和输出文件 +4. **处理记录**:保存文件处理记录,避免重复处理 + +## 性能优化 + +1. **减少文件操作**:优化文件读写次数,减少不必要的文件复制操作 +2. **批量处理**:支持批量模式,提高处理效率 +3. **资源释放**:及时释放文件句柄和内存资源,避免资源泄漏 + +## 可维护性改进 + +1. **配置外部化**:将配置参数提取到config.ini文件,便于调整 +2. **模块间低耦合**:模块之间通过明确的接口交互,降低耦合度 +3. **可扩展设计**:系统设计考虑未来扩展,如添加新的特殊条码处理规则 +4. **完整文档**:提供详细的README文档,说明系统功能和使用方法 \ No newline at end of file diff --git a/启动器.py b/启动器.py index 0f3287d..059bb22 100644 --- a/启动器.py +++ b/启动器.py @@ -283,6 +283,132 @@ def organize_project_files(log_widget): add_to_log(log_widget, "没有需要整理的文件\n") messagebox.showinfo("整理完成", "没有需要整理的文件。") +def clean_data_files(log_widget): + """清理data目录中的文件""" + # 确保目录存在 + ensure_directories() + + add_to_log(log_widget, "开始清理文件...\n") + + # 获取需要清理的目录 + input_dir = os.path.abspath("data/input") + output_dir = os.path.abspath("data/output") + + # 统计文件信息 + input_files = [f for f in os.listdir(input_dir) if os.path.isfile(os.path.join(input_dir, f))] + output_files = [f for f in os.listdir(output_dir) if os.path.isfile(os.path.join(output_dir, f))] + + # 显示统计信息 + add_to_log(log_widget, f"输入目录 ({input_dir}) 共有 {len(input_files)} 个文件\n") + add_to_log(log_widget, f"输出目录 ({output_dir}) 共有 {len(output_files)} 个文件\n") + + # 显示确认对话框 + if not input_files and not output_files: + messagebox.showinfo("清理文件", "没有需要清理的文件") + return + + confirm_message = "确定要清理以下文件吗?\n\n" + confirm_message += f"- 输入目录: {len(input_files)} 个文件\n" + confirm_message += f"- 输出目录: {len(output_files)} 个文件\n" + confirm_message += "\n此操作不可撤销!" + + if not messagebox.askyesno("确认清理", confirm_message): + add_to_log(log_widget, "清理操作已取消\n") + return + + # 清理输入目录的文件 + files_deleted = 0 + + # 先提示用户选择要清理的目录 + options = [] + if input_files: + options.append(("输入目录(data/input)", input_dir)) + if output_files: + options.append(("输出目录(data/output)", output_dir)) + + # 创建临时的选择对话框 + dialog = tk.Toplevel() + dialog.title("选择要清理的目录") + dialog.geometry("300x200") + dialog.transient(log_widget.winfo_toplevel()) # 设置为主窗口的子窗口 + dialog.grab_set() # 模态对话框 + + tk.Label(dialog, text="请选择要清理的目录:", font=("Arial", 12)).pack(pady=10) + + # 选择变量 + choices = {} + for name, path in options: + var = tk.BooleanVar(value=True) # 默认选中 + choices[path] = var + tk.Checkbutton(dialog, text=name, variable=var, font=("Arial", 10)).pack(anchor=tk.W, padx=20, pady=5) + + # 删除前备份选项 + backup_var = tk.BooleanVar(value=False) + tk.Checkbutton(dialog, text="删除前备份文件", variable=backup_var, font=("Arial", 10)).pack(anchor=tk.W, padx=20, pady=5) + + result = {"confirmed": False, "choices": {}, "backup": False} + + def on_confirm(): + result["confirmed"] = True + result["choices"] = {path: var.get() for path, var in choices.items()} + result["backup"] = backup_var.get() + dialog.destroy() + + def on_cancel(): + dialog.destroy() + + # 按钮 + button_frame = tk.Frame(dialog) + button_frame.pack(pady=10) + tk.Button(button_frame, text="确认", command=on_confirm, width=10).pack(side=tk.LEFT, padx=10) + tk.Button(button_frame, text="取消", command=on_cancel, width=10).pack(side=tk.LEFT, padx=10) + + # 等待对话框关闭 + dialog.wait_window() + + if not result["confirmed"]: + add_to_log(log_widget, "清理操作已取消\n") + return + + # 备份文件 + if result["backup"]: + backup_dir = os.path.join("data", "backup", datetime.datetime.now().strftime("%Y%m%d%H%M%S")) + os.makedirs(backup_dir, exist_ok=True) + add_to_log(log_widget, f"创建备份目录: {backup_dir}\n") + + for dir_path, selected in result["choices"].items(): + if selected: + dir_name = os.path.basename(dir_path) + backup_subdir = os.path.join(backup_dir, dir_name) + os.makedirs(backup_subdir, exist_ok=True) + + files = [f for f in os.listdir(dir_path) if os.path.isfile(os.path.join(dir_path, f))] + for file in files: + src = os.path.join(dir_path, file) + dst = os.path.join(backup_subdir, file) + try: + shutil.copy2(src, dst) + add_to_log(log_widget, f"已备份: {src} -> {dst}\n") + except Exception as e: + add_to_log(log_widget, f"备份失败: {src}, 错误: {e}\n") + + # 删除所选目录的文件 + for dir_path, selected in result["choices"].items(): + if selected: + files = [f for f in os.listdir(dir_path) if os.path.isfile(os.path.join(dir_path, f))] + for file in files: + file_path = os.path.join(dir_path, file) + try: + os.remove(file_path) + add_to_log(log_widget, f"已删除: {file_path}\n") + files_deleted += 1 + except Exception as e: + add_to_log(log_widget, f"删除失败: {file_path}, 错误: {e}\n") + + # 显示结果 + add_to_log(log_widget, f"清理完成,共删除 {files_deleted} 个文件\n") + messagebox.showinfo("清理完成", f"共删除 {files_deleted} 个文件") + def main(): """主函数""" # 确保必要的目录结构存在并转移旧目录内容 @@ -379,6 +505,15 @@ def main(): command=lambda: organize_project_files(log_text) ).pack(pady=5) + # 清理文件按钮 + tk.Button( + buttons_frame, + text="清理文件", + width=20, + height=2, + command=lambda: clean_data_files(log_text) + ).pack(pady=5) + # 打开输入目录 tk.Button( buttons_frame,