ai说excel部分没问题了,暂且信一次,提交文件
This commit is contained in:
parent
0035cd1893
commit
131fff6a7d
234
README.md
234
README.md
@ -35,137 +35,167 @@ orc-order-v2/
|
|||||||
│ │ │ └── converter.py # 单位转换与规格处理
|
│ │ │ └── converter.py # 单位转换与规格处理
|
||||||
│ │ │
|
│ │ │
|
||||||
│ │ └── utils/ # 工具函数
|
│ │ └── utils/ # 工具函数
|
||||||
│ │ ├── file_utils.py # 文件操作工具
|
│ │ ├── file_utils.py # 文件处理工具
|
||||||
│ │ ├── log_utils.py # 日志工具
|
│ │ └── log_utils.py # 日志工具
|
||||||
│ │ └── string_utils.py # 字符串处理工具
|
|
||||||
│ │
|
│ │
|
||||||
│ └── services/ # 业务服务
|
│ └── services/ # 服务层
|
||||||
│ ├── ocr_service.py # OCR服务
|
│ ├── ocr_service.py # OCR服务
|
||||||
│ └── order_service.py # 订单处理服务
|
│ └── excel_service.py # Excel处理服务
|
||||||
│
|
│
|
||||||
├── data/ # 数据目录
|
├── data/ # 数据目录
|
||||||
│ ├── input/ # 输入文件
|
│ ├── input/ # 输入图片目录
|
||||||
│ ├── output/ # 输出文件
|
│ ├── output/ # 处理结果输出目录
|
||||||
│ └── temp/ # 临时文件
|
│ ├── temp/ # 临时文件目录
|
||||||
|
│ └── backup/ # 备份目录
|
||||||
│
|
│
|
||||||
├── logs/ # 日志目录
|
├── logs/ # 日志目录
|
||||||
│
|
│
|
||||||
├── templates/ # 模板文件
|
├── templates/ # 模板目录
|
||||||
│ └── 银豹-采购单模板.xls # 采购单模板
|
│ └── 银豹-采购单模板.xls # Excel模板文件
|
||||||
│
|
│
|
||||||
├── 启动器.py # 图形界面启动器
|
|
||||||
├── run.py # 命令行入口
|
|
||||||
├── config.ini # 配置文件
|
├── 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
|
```bash
|
||||||
# OCR识别
|
pip install -r requirements.txt
|
||||||
python run.py ocr [--input 图片路径] [--batch]
|
|
||||||
|
|
||||||
# Excel处理
|
|
||||||
python run.py excel [--input Excel文件路径]
|
|
||||||
|
|
||||||
# 订单合并
|
|
||||||
python run.py merge [--input 采购单文件路径列表]
|
|
||||||
|
|
||||||
# 完整流程
|
|
||||||
python run.py pipeline
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## 文件处理流程
|
### 配置文件
|
||||||
|
|
||||||
1. **OCR识别处理**:
|
在`config.ini`中配置以下信息:
|
||||||
- 读取`data/input`目录下的图片文件
|
|
||||||
- 调用百度OCR API进行表格识别
|
|
||||||
- 保存识别结果为Excel文件到`data/output`目录
|
|
||||||
|
|
||||||
2. **Excel处理**:
|
|
||||||
- 读取OCR识别生成的Excel文件
|
|
||||||
- 提取商品信息(条码、名称、规格、单价、数量等)
|
|
||||||
- 按照采购单模板格式生成标准采购单Excel文件
|
|
||||||
- 输出文件命名为"采购单_原文件名.xls"
|
|
||||||
|
|
||||||
3. **采购单合并**:
|
|
||||||
- 读取所有采购单Excel文件
|
|
||||||
- 合并相同商品的数量
|
|
||||||
- 生成总采购单
|
|
||||||
|
|
||||||
## 配置说明
|
|
||||||
|
|
||||||
系统配置文件`config.ini`包含以下主要配置:
|
|
||||||
|
|
||||||
```ini
|
```ini
|
||||||
[API]
|
[OCR]
|
||||||
api_key = 您的百度API Key
|
api_key = 你的百度OCR API Key
|
||||||
secret_key = 您的百度Secret Key
|
secret_key = 你的百度OCR Secret Key
|
||||||
timeout = 30
|
|
||||||
max_retries = 3
|
|
||||||
retry_delay = 2
|
|
||||||
api_url = https://aip.baidubce.com/rest/2.0/ocr/v1/table
|
|
||||||
|
|
||||||
[Paths]
|
[Paths]
|
||||||
input_folder = data/input
|
input_folder = data/input
|
||||||
output_folder = data/output
|
output_folder = data/output
|
||||||
temp_folder = data/temp
|
template_file = templates/银豹-采购单模板.xls
|
||||||
|
|
||||||
[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
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## 使用方法
|
||||||
|
|
||||||
|
### 图形界面
|
||||||
|
|
||||||
|
运行`启动器.py`启动图形界面:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python 启动器.py
|
||||||
|
```
|
||||||
|
|
||||||
|
图形界面包括以下功能:
|
||||||
|
- **处理单个文件**:选择并处理单个图片文件
|
||||||
|
- **批量处理**:处理data/input目录中的所有图片文件
|
||||||
|
- **合并处理**:合并多个采购单
|
||||||
|
- **清理文件**:清理input和output目录中的文件
|
||||||
|
- **查看日志**:实时显示处理日志
|
||||||
|
|
||||||
|
### 命令行模式
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 处理单个文件
|
||||||
|
python run.py --file=image.jpg
|
||||||
|
|
||||||
|
# 批量处理目录中的所有文件
|
||||||
|
python run.py --batch
|
||||||
|
|
||||||
|
# 合并采购单
|
||||||
|
python run.py --merge
|
||||||
|
```
|
||||||
|
|
||||||
|
## 单位处理规则
|
||||||
|
|
||||||
|
系统支持多种单位的智能处理,自动识别和转换不同的计量单位。单位处理逻辑如下:
|
||||||
|
|
||||||
|
### 标准单位处理
|
||||||
|
|
||||||
|
| 单位 | 处理规则 | 示例 |
|
||||||
|
|------|----------|------|
|
||||||
|
| 件 | 数量×包装数量<br>单价÷包装数量<br>单位转换为"瓶" | 1件(规格1*12) → 12瓶<br>单价108元/件 → 9元/瓶 |
|
||||||
|
| 箱 | 数量×包装数量<br>单价÷包装数量<br>单位转换为"瓶" | 2箱(规格1*24) → 48瓶<br>单价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密钥
|
1. 确保输入文件格式正确,支持jpg、png等图片格式
|
||||||
2. 图片质量会影响OCR识别结果,建议使用清晰的原始图片
|
2. 处理结果将输出到data/output目录下
|
||||||
3. 处理大量图片时可能会受到API调用频率限制
|
3. 定期清理临时文件和日志文件
|
||||||
4. 所有处理好的文件会保存在`data/output`目录中
|
4. 及时更新百度OCR API密钥
|
||||||
|
5. 为避免数据丢失,可使用清理功能前的备份选项
|
||||||
|
|
||||||
## 错误排查
|
## 错误排查
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@ -1,30 +1,26 @@
|
|||||||
"""
|
"""
|
||||||
单位转换处理模块
|
单位转换模块
|
||||||
-------------
|
----------
|
||||||
提供规格和单位的处理和转换功能。
|
提供单位转换功能,支持规格推断和单位自动提取。
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import re
|
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.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__)
|
logger = get_logger(__name__)
|
||||||
|
|
||||||
class UnitConverter:
|
class UnitConverter:
|
||||||
"""
|
"""
|
||||||
单位转换器:处理商品规格和单位转换
|
单位转换器:处理不同单位之间的转换,支持从商品名称推断规格
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
"""初始化单位转换器"""
|
"""
|
||||||
|
初始化单位转换器
|
||||||
|
"""
|
||||||
# 特殊条码配置
|
# 特殊条码配置
|
||||||
self.special_barcodes = {
|
self.special_barcodes = {
|
||||||
'6925019900087': {
|
'6925019900087': {
|
||||||
@ -32,182 +28,299 @@ class UnitConverter:
|
|||||||
'target_unit': '瓶', # 目标单位
|
'target_unit': '瓶', # 目标单位
|
||||||
'description': '特殊处理:数量*10,单位转换为瓶'
|
'description': '特殊处理:数量*10,单位转换为瓶'
|
||||||
}
|
}
|
||||||
# 可以在这里添加更多特殊条码的配置
|
# 可以添加更多特殊条码的配置
|
||||||
}
|
}
|
||||||
|
|
||||||
# 有效的单位列表
|
# 规格推断的正则表达式模式
|
||||||
self.valid_units = ['件', '箱', '包', '提', '盒', '瓶', '个', '支', '袋', '副', '桶', '罐', 'L', 'l', '升']
|
self.spec_patterns = [
|
||||||
|
# 1*6、1x12、1X20等格式
|
||||||
# 需要特殊处理的单位
|
(r'(\d+)[*xX×](\d+)', r'\1*\2'),
|
||||||
self.special_units = ['件', '箱', '提', '盒']
|
# 1*5*12和1x5x12等三级格式
|
||||||
|
(r'(\d+)[*xX×](\d+)[*xX×](\d+)', r'\1*\2*\3'),
|
||||||
logger.info("单位转换器初始化完成")
|
# "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:
|
Args:
|
||||||
barcode: 条码
|
quantity_str: 数量字符串,如"2箱"、"5件"
|
||||||
multiplier: 数量乘数
|
|
||||||
target_unit: 目标单位
|
Returns:
|
||||||
description: 处理描述
|
(数量, 单位)的元组,如果无法提取则返回(None, None)
|
||||||
"""
|
"""
|
||||||
self.special_barcodes[barcode] = {
|
if not quantity_str or not isinstance(quantity_str, str):
|
||||||
'multiplier': multiplier,
|
return None, None
|
||||||
'target_unit': target_unit,
|
|
||||||
'description': description or f'特殊处理:数量*{multiplier},单位转换为{target_unit}'
|
# 匹配数字+单位格式
|
||||||
}
|
match = re.match(r'^([\d\.]+)\s*([^\d\s\.]+)$', quantity_str.strip())
|
||||||
logger.info(f"添加特殊条码配置: {barcode}, {description}")
|
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:
|
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:
|
Returns:
|
||||||
推断的规格,如果无法推断则返回None
|
推断的规格,如果无法推断则返回None
|
||||||
"""
|
"""
|
||||||
if not product_name or not isinstance(product_name, str):
|
if not name or not isinstance(name, str):
|
||||||
return None
|
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:
|
return None
|
||||||
# 清理商品名称
|
|
||||||
name = clean_string(product_name)
|
def parse_specification(self, spec: str) -> Tuple[int, int, Optional[int]]:
|
||||||
|
|
||||||
# 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]]:
|
|
||||||
"""
|
"""
|
||||||
从数量字符串提取单位
|
解析规格字符串,支持1*12和1*5*12等格式
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
quantity_str: 数量字符串
|
spec: 规格字符串
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
(数量, 单位)元组
|
(一级包装, 二级包装, 三级包装)元组,如果是二级包装,第三个值为None
|
||||||
"""
|
"""
|
||||||
if not quantity_str or not isinstance(quantity_str, str):
|
if not spec or not isinstance(spec, str):
|
||||||
return None, None
|
return 1, 1, None
|
||||||
|
|
||||||
try:
|
# 处理三级包装,如1*5*12
|
||||||
# 清理数量字符串
|
three_level_match = re.match(r'(\d+)[*xX×](\d+)[*xX×](\d+)', spec)
|
||||||
quantity_str = clean_string(quantity_str)
|
if three_level_match:
|
||||||
|
try:
|
||||||
# 提取数字和单位
|
level1 = int(three_level_match.group(1))
|
||||||
return extract_number_and_unit(quantity_str)
|
level2 = int(three_level_match.group(2))
|
||||||
|
level3 = int(three_level_match.group(3))
|
||||||
except Exception as e:
|
logger.info(f"解析三级规格: {spec} -> {level1}*{level2}*{level3}")
|
||||||
logger.error(f"从数量字符串提取单位时出错: {quantity_str}, 错误: {e}")
|
return level1, level2, level3
|
||||||
return None, None
|
except ValueError:
|
||||||
|
pass
|
||||||
def process_unit_conversion(self, product: Dict[str, Any]) -> Dict[str, Any]:
|
|
||||||
|
# 处理二级包装,如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:
|
Args:
|
||||||
product: 商品字典,包含条码、单位、规格、数量和单价等字段
|
product: 商品信息字典
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
处理后的商品字典
|
处理后的商品信息字典
|
||||||
"""
|
"""
|
||||||
# 复制商品信息,避免修改原始数据
|
# 复制原始数据,避免修改原始字典
|
||||||
result = product.copy()
|
result = product.copy()
|
||||||
|
|
||||||
try:
|
barcode = result.get('barcode', '')
|
||||||
# 获取条码、单位、规格、数量和单价
|
unit = result.get('unit', '')
|
||||||
barcode = product.get('barcode', '')
|
quantity = result.get('quantity', 0)
|
||||||
unit = product.get('unit', '')
|
price = result.get('price', 0)
|
||||||
specification = product.get('specification', '')
|
specification = result.get('specification', '')
|
||||||
quantity = product.get('quantity', 0)
|
|
||||||
price = product.get('price', 0)
|
# 跳过无效数据
|
||||||
|
if not barcode or not quantity:
|
||||||
# 如果缺少关键信息,无法进行转换
|
|
||||||
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. 默认返回原始数据
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
except Exception as e:
|
# 特殊条码处理
|
||||||
logger.error(f"单位转换处理出错: {e}")
|
if barcode in self.special_barcodes:
|
||||||
# 发生错误时,返回原始数据
|
special_config = self.special_barcodes[barcode]
|
||||||
return result
|
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
|
||||||
@ -294,33 +294,100 @@ class ExcelProcessor:
|
|||||||
output_workbook = xlcopy(template_workbook)
|
output_workbook = xlcopy(template_workbook)
|
||||||
output_sheet = output_workbook.get_sheet(0)
|
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)
|
quantity = product.get('quantity', 0)
|
||||||
# 商品编码(条码)
|
price = product.get('price', 0)
|
||||||
output_sheet.write(row, 1, product['barcode'])
|
|
||||||
# 商品名称
|
# 判断是否为赠品(价格为0)
|
||||||
output_sheet.write(row, 2, product['name'])
|
is_gift = price == 0
|
||||||
# 规格
|
|
||||||
output_sheet.write(row, 3, product['specification'])
|
logger.info(f"处理商品: 条码={barcode}, 数量={quantity}, 单价={price}, 是否赠品={is_gift}")
|
||||||
# 单位
|
|
||||||
output_sheet.write(row, 4, product['unit'])
|
if barcode not in barcode_groups:
|
||||||
# 单价
|
barcode_groups[barcode] = {
|
||||||
output_sheet.write(row, 5, product['price'])
|
'normal': None, # 正常商品信息
|
||||||
# 采购数量
|
'gift_quantity': 0 # 赠品数量
|
||||||
output_sheet.write(row, 6, product['quantity'])
|
}
|
||||||
# 采购金额(单价 × 数量)
|
|
||||||
amount = product['price'] * product['quantity']
|
if is_gift:
|
||||||
output_sheet.write(row, 7, amount)
|
# 是赠品,累加赠品数量
|
||||||
# 税率
|
barcode_groups[barcode]['gift_quantity'] += quantity
|
||||||
output_sheet.write(row, 8, 0)
|
logger.info(f"发现赠品:条码{barcode}, 数量={quantity}")
|
||||||
# 赠送量(默认为0)
|
else:
|
||||||
output_sheet.write(row, 9, 0)
|
# 是正常商品
|
||||||
|
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)
|
output_workbook.save(output_file_path)
|
||||||
|
|||||||
@ -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"
|
||||||
}
|
}
|
||||||
BIN
data/output/~$微信图片_20250227193150(1).xlsx
Normal file
BIN
data/output/~$微信图片_20250227193150(1).xlsx
Normal file
Binary file not shown.
Binary file not shown.
BIN
data/output/采购单_微信图片_20250227193150(1).xls
Normal file
BIN
data/output/采购单_微信图片_20250227193150(1).xls
Normal file
Binary file not shown.
Binary file not shown.
@ -1 +1 @@
|
|||||||
Active since: 2025-05-02 17:16:24
|
Active since: 2025-05-02 17:42:15
|
||||||
@ -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: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: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: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
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
Active since: 2025-05-02 17:16:24
|
Active since: 2025-05-02 17:42:15
|
||||||
@ -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: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
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
Active since: 2025-05-02 17:16:24
|
Active since: 2025-05-02 17:42:15
|
||||||
@ -26,3 +26,9 @@
|
|||||||
2025-05-02 17:10:09,224 - app.core.excel.merger - INFO - 初始化完成,模板文件: templates\银豹-采购单模板.xls
|
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,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: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
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
Active since: 2025-05-02 17:16:24
|
Active since: 2025-05-02 17:42:15
|
||||||
@ -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,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,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: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
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
Active since: 2025-05-02 17:16:23
|
Active since: 2025-05-02 17:42:14
|
||||||
@ -1 +1 @@
|
|||||||
Active since: 2025-05-02 17:16:23
|
Active since: 2025-05-02 17:42:14
|
||||||
@ -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\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 - 使用临时目录: 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: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
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
Active since: 2025-05-02 17:16:23
|
Active since: 2025-05-02 17:42:14
|
||||||
@ -1 +1 @@
|
|||||||
Active since: 2025-05-02 17:16:23
|
Active since: 2025-05-02 17:42:14
|
||||||
@ -25,3 +25,9 @@
|
|||||||
2025-05-02 17:10:09,222 - app.services.ocr_service - INFO - OCRService初始化完成
|
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,474 - app.services.ocr_service - INFO - 初始化OCRService
|
||||||
2025-05-02 17:16:24,476 - 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初始化完成
|
||||||
|
|||||||
@ -1 +1 @@
|
|||||||
Active since: 2025-05-02 17:16:24
|
Active since: 2025-05-02 17:42:15
|
||||||
@ -20,3 +20,12 @@
|
|||||||
2025-05-02 17:16:24,476 - app.services.order_service - INFO - 初始化OrderService
|
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初始化完成
|
||||||
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: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
|
||||||
|
|||||||
72
v2-优化总结.md
Normal file
72
v2-优化总结.md
Normal file
@ -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文档,说明系统功能和使用方法
|
||||||
135
启动器.py
135
启动器.py
@ -283,6 +283,132 @@ def organize_project_files(log_widget):
|
|||||||
add_to_log(log_widget, "没有需要整理的文件\n")
|
add_to_log(log_widget, "没有需要整理的文件\n")
|
||||||
messagebox.showinfo("整理完成", "没有需要整理的文件。")
|
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():
|
def main():
|
||||||
"""主函数"""
|
"""主函数"""
|
||||||
# 确保必要的目录结构存在并转移旧目录内容
|
# 确保必要的目录结构存在并转移旧目录内容
|
||||||
@ -379,6 +505,15 @@ def main():
|
|||||||
command=lambda: organize_project_files(log_text)
|
command=lambda: organize_project_files(log_text)
|
||||||
).pack(pady=5)
|
).pack(pady=5)
|
||||||
|
|
||||||
|
# 清理文件按钮
|
||||||
|
tk.Button(
|
||||||
|
buttons_frame,
|
||||||
|
text="清理文件",
|
||||||
|
width=20,
|
||||||
|
height=2,
|
||||||
|
command=lambda: clean_data_files(log_text)
|
||||||
|
).pack(pady=5)
|
||||||
|
|
||||||
# 打开输入目录
|
# 打开输入目录
|
||||||
tk.Button(
|
tk.Button(
|
||||||
buttons_frame,
|
buttons_frame,
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user