refactor: optimize material deletion with exact code matching and database queries
- Change from fuzzy material name matching to exact material code matching - Migrate from MaterialsTypeToBeDeleted to MaterialsToBeDeleted table - Add should_delete_material() for on-demand database queries - Remove in-memory material list loading to reduce memory footprint - Update documentation to reflect new matching logic - Remove main scripts from git tracking (contain user-specific config) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
6
.gitignore
vendored
6
.gitignore
vendored
@@ -23,6 +23,12 @@ tests/
|
|||||||
config/user_settings.json
|
config/user_settings.json
|
||||||
nul
|
nul
|
||||||
|
|
||||||
|
# 主脚本(包含用户特定配置)
|
||||||
|
main.py
|
||||||
|
main_clean.py
|
||||||
|
main_ui.py
|
||||||
|
record.py
|
||||||
|
|
||||||
# 环境变量
|
# 环境变量
|
||||||
.env
|
.env
|
||||||
.env.local
|
.env.local
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
"""
|
"""
|
||||||
待删除物料查询组件
|
待删除物料查询组件
|
||||||
从数据库查询指定负责人需要删除的物料名称
|
从数据库查询指定负责人需要删除的物料编码
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from typing import List, Dict, Any
|
from typing import List, Dict, Any
|
||||||
@@ -9,25 +9,25 @@ from db.connection import get_connection
|
|||||||
|
|
||||||
def get_materials_to_delete(manager_name):
|
def get_materials_to_delete(manager_name):
|
||||||
"""
|
"""
|
||||||
根据负责人名称查询待删除物料名称列表
|
根据负责人名称查询待删除物料编码列表
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
manager_name: 负责人姓名
|
manager_name: 负责人姓名
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
物料名称列表(关键字)
|
物料编码列表
|
||||||
"""
|
"""
|
||||||
query = """
|
query = """
|
||||||
SELECT [MaterialName]
|
SELECT [MaterialCode]
|
||||||
FROM [dbo].[MaterialsTypeToBeDeleted]
|
FROM [dbo].[MaterialsToBeDeleted]
|
||||||
WHERE [ManagerName] = ?
|
WHERE [ManagerName] = ?
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with get_connection() as conn:
|
with get_connection() as conn:
|
||||||
results = conn.execute_query(query, (manager_name,))
|
results = conn.execute_query(query, (manager_name,))
|
||||||
# 提取物料名称并去除空值
|
# 提取物料编码并去除空值
|
||||||
material_names = [row["MaterialName"] for row in results if row["MaterialName"]]
|
material_codes = [row["MaterialCode"] for row in results if row["MaterialCode"]]
|
||||||
return material_names
|
return material_codes
|
||||||
|
|
||||||
|
|
||||||
def get_all_materials_to_delete() -> List[Dict[str, Any]]:
|
def get_all_materials_to_delete() -> List[Dict[str, Any]]:
|
||||||
@@ -35,15 +35,40 @@ def get_all_materials_to_delete() -> List[Dict[str, Any]]:
|
|||||||
获取所有待删除物料记录
|
获取所有待删除物料记录
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
List[Dict[str, Any]]: 包含MaterialName和ManagerName的记录列表
|
List[Dict[str, Any]]: 包含MaterialCode和ManagerName的记录列表
|
||||||
"""
|
"""
|
||||||
query = """
|
query = """
|
||||||
SELECT [MaterialName], [ManagerName]
|
SELECT [MaterialCode], [ManagerName]
|
||||||
FROM [dbo].[MaterialsTypeToBeDeleted]
|
FROM [dbo].[MaterialsToBeDeleted]
|
||||||
WHERE [MaterialName] IS NOT NULL
|
WHERE [MaterialCode] IS NOT NULL
|
||||||
ORDER BY [ManagerName], [MaterialName]
|
ORDER BY [ManagerName], [MaterialCode]
|
||||||
"""
|
"""
|
||||||
|
|
||||||
with get_connection() as conn:
|
with get_connection() as conn:
|
||||||
results = conn.execute_query(query)
|
results = conn.execute_query(query)
|
||||||
return results
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def should_delete_material(manager_name: str, material_code: str) -> bool:
|
||||||
|
"""
|
||||||
|
检查指定物料编码是否需要删除
|
||||||
|
|
||||||
|
直接在数据库层面查询,避免将大量数据加载到内存
|
||||||
|
|
||||||
|
Args:
|
||||||
|
manager_name: 负责人姓名
|
||||||
|
material_code: 物料编码
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 如果物料需要删除返回 True,否则返回 False
|
||||||
|
"""
|
||||||
|
query = """
|
||||||
|
SELECT COUNT(*) as count
|
||||||
|
FROM [dbo].[MaterialsToBeDeleted]
|
||||||
|
WHERE [ManagerName] = ?
|
||||||
|
AND [MaterialCode] = ?
|
||||||
|
"""
|
||||||
|
|
||||||
|
with get_connection() as conn:
|
||||||
|
results = conn.execute_query(query, (manager_name, material_code))
|
||||||
|
return results[0]["count"] > 0 if results else False
|
||||||
|
|||||||
345
docs/MAIN_CLEAN_FLOW.md
Normal file
345
docs/MAIN_CLEAN_FLOW.md
Normal file
@@ -0,0 +1,345 @@
|
|||||||
|
# main_clean.py 流程说明
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
`main_clean.py` 是离散备料计划维护数据清理工具的主程序入口。该工具通过 Playwright 自动化登录用友 BIP 系统,读取生产订单号,然后逐个清理订单中不需要的物料数据。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 系统架构图
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
Start([开始]) --> Init[初始化 DiscreteMaterialPlanCleaner]
|
||||||
|
|
||||||
|
Init --> QueryDB[(查询数据库<br/>获取待删除物料列表)]
|
||||||
|
|
||||||
|
QueryDB --> Login[登录用友BIP系统]
|
||||||
|
|
||||||
|
Login --> OpenMenu[点击功能菜单]
|
||||||
|
OpenMenu --> OpenPage[打开离散生产订单维护页面]
|
||||||
|
|
||||||
|
OpenPage --> SetupQuery[设置查询界面]
|
||||||
|
SetupQuery --> ReadFile[(读取ProductionID.txt)]
|
||||||
|
ReadFile --> GetOrders[(查询数据库<br/>获取生产订单号)]
|
||||||
|
|
||||||
|
GetOrders --> LoopStart{遍历订单}
|
||||||
|
|
||||||
|
LoopStart --> ProcessOrder[处理单个订单]
|
||||||
|
ProcessOrder --> QueryOrder[查询订单详情]
|
||||||
|
QueryOrder --> OpenPlanPage[打开备料计划页面]
|
||||||
|
OpenPlanPage --> WaitLoad[等待页面加载完成]
|
||||||
|
|
||||||
|
WaitLoad --> CheckData{检查数据状态}
|
||||||
|
|
||||||
|
CheckData -->|detail_count = 0| SkipOrder[跳过该订单]
|
||||||
|
CheckData -->|status != 审批通过| SkipOrder
|
||||||
|
CheckData -->|detail_count > 0<br/>status = 审批通过| ClickModify[点击修改按钮]
|
||||||
|
|
||||||
|
ClickModify --> ExpandForm[展开详细信息表单]
|
||||||
|
ExpandForm --> LoopMaterials{遍历物料}
|
||||||
|
|
||||||
|
LoopMaterials --> GetMaterial[获取物料信息]
|
||||||
|
GetMaterial --> CheckDelete{需要删除?}
|
||||||
|
|
||||||
|
CheckDelete -->|是| MarkDelete[标记待删除]
|
||||||
|
CheckDelete -->|否| KeepMaterial[保留物料]
|
||||||
|
|
||||||
|
MarkDelete --> NextMaterial{下一个物料?}
|
||||||
|
KeepMaterial --> NextMaterial
|
||||||
|
|
||||||
|
NextMaterial -->|是| LoopMaterials
|
||||||
|
NextMaterial -->|否| ClosePlanPage[关闭备料计划页面]
|
||||||
|
|
||||||
|
SkipOrder --> ClosePlanPage
|
||||||
|
ClosePlanPage --> NextOrder{下一个订单?}
|
||||||
|
|
||||||
|
NextOrder -->|是| LoopStart
|
||||||
|
NextOrder -->|否| Logout[注销账号]
|
||||||
|
|
||||||
|
Logout --> CloseBrowser[关闭浏览器]
|
||||||
|
CloseBrowser --> End([结束])
|
||||||
|
|
||||||
|
style Init fill:#e1f5ff
|
||||||
|
style QueryDB fill:#fff4e1
|
||||||
|
style Login fill:#e1f5ff
|
||||||
|
style End fill:#ffe1e1
|
||||||
|
style CheckDelete fill:#fff4e1
|
||||||
|
style CheckData fill:#fff4e1
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 详细流程说明
|
||||||
|
|
||||||
|
### 1. 初始化阶段
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
A[main函数] --> B[创建 DiscreteMaterialPlanCleaner 实例]
|
||||||
|
B --> C[配置参数:<br/>username, password,<br/>manager_name, headless, verbose]
|
||||||
|
|
||||||
|
style A fill:#e1f5ff
|
||||||
|
style B fill:#e1f5ff
|
||||||
|
style C fill:#e1f5ff
|
||||||
|
```
|
||||||
|
|
||||||
|
**代码位置**: `main_clean.py:9-24`
|
||||||
|
|
||||||
|
```python
|
||||||
|
cleaner = DiscreteMaterialPlanCleaner(
|
||||||
|
username="BLDpengqiangqiang",
|
||||||
|
password="Cqbld123456.",
|
||||||
|
manager_name="彭羽",
|
||||||
|
headless=False,
|
||||||
|
verbose=True,
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. 数据准备阶段
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
A[clean.clean方法] --> B[查询数据库<br/>MaterialsToBeDeleted表]
|
||||||
|
B --> C[获取指定负责人的<br/>待删除物料编码列表]
|
||||||
|
|
||||||
|
style A fill:#fff4e1
|
||||||
|
style B fill:#fff4e1
|
||||||
|
style C fill:#fff4e1
|
||||||
|
```
|
||||||
|
|
||||||
|
**代码位置**: `utils/离散备料计划维护数据清理.py:292-304`
|
||||||
|
|
||||||
|
| 模块 | 功能 | 文件 |
|
||||||
|
|------|------|------|
|
||||||
|
| `get_materials_to_delete()` | 查询待删除物料编码 | `db/materials_to_delete.py` |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 3. 登录与导航
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
sequenceDiagram
|
||||||
|
participant Main as main_clean.py
|
||||||
|
participant Auth as auth.py
|
||||||
|
participant Browser as Chromium Browser
|
||||||
|
participant ERP as 用友BIP系统
|
||||||
|
|
||||||
|
Main->>Auth: login(playwright, username, password)
|
||||||
|
Auth->>Browser: launch(headless=False)
|
||||||
|
Auth->>Browser: new_context()
|
||||||
|
Auth->>Browser: new_page()
|
||||||
|
Auth->>ERP: goto(登录页面URL)
|
||||||
|
Auth->>ERP: 填写用户名和密码
|
||||||
|
Auth->>ERP: 点击登录按钮
|
||||||
|
alt 有强制登录弹窗
|
||||||
|
ERP-->>Auth: 显示弹窗
|
||||||
|
Auth->>ERP: 点击确定
|
||||||
|
end
|
||||||
|
Auth-->>Main: 返回 (browser, context, page, main_frame)
|
||||||
|
```
|
||||||
|
|
||||||
|
**代码位置**: `utils/auth.py:8-72`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 4. 设置查询界面
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
A[点击图标按钮] --> B[选择"订单号查询"]
|
||||||
|
B --> C[点击"全部"标签]
|
||||||
|
C --> D{填充显示条数}
|
||||||
|
D --> E{成功?}
|
||||||
|
E -->|否| F[重试<br/>最多3次]
|
||||||
|
F --> D
|
||||||
|
E -->|是| G[继续]
|
||||||
|
|
||||||
|
style D fill:#fff4e1
|
||||||
|
style E fill:#fff4e1
|
||||||
|
style F fill:#ffe1e1
|
||||||
|
```
|
||||||
|
|
||||||
|
**代码位置**: `utils/离散备料计划维护数据清理.py:263-290`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 5. 获取生产订单号
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart LR
|
||||||
|
A[read_production_ids] --> B[读取ProductionID.txt]
|
||||||
|
B --> C[解析总排号列表]
|
||||||
|
C --> D[query_production_order_numbers]
|
||||||
|
D --> E[查询数据库<br/>获取生产订单号]
|
||||||
|
|
||||||
|
style A fill:#fff4e1
|
||||||
|
style D fill:#fff4e1
|
||||||
|
```
|
||||||
|
|
||||||
|
**代码位置**: `utils/离散备料计划维护数据清理.py:41-59`
|
||||||
|
|
||||||
|
| 函数 | 功能 | 数据库表 |
|
||||||
|
|------|------|----------|
|
||||||
|
| `read_production_ids()` | 读取总排号文件 | 文件输入 |
|
||||||
|
| `query_production_order_numbers()` | 查询生产订单号 | ProductionOrder |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 6. 订单处理流程
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
Start([开始处理订单]) --> FillInput[填充生产订单号]
|
||||||
|
FillInput --> ClickQuery[点击查询按钮]
|
||||||
|
ClickQuery --> WaitLoad[等待加载完成]
|
||||||
|
|
||||||
|
WaitLoad --> ClickMore[点击"更多"按钮]
|
||||||
|
ClickMore --> OpenPlan[打开"备料计划"页面]
|
||||||
|
|
||||||
|
OpenPlan --> GetIframe[获取嵌套iframe]
|
||||||
|
GetIframe --> WaitPlanLoad[等待备料计划页面加载]
|
||||||
|
|
||||||
|
WaitPlanLoad --> ExtractInfo[提取信息:<br/>- 编码<br/>- 详细信息数量<br/>- 备料状态]
|
||||||
|
|
||||||
|
ExtractInfo --> CheckStatus{状态检查}
|
||||||
|
|
||||||
|
style Start fill:#e1f5ff
|
||||||
|
style CheckStatus fill:#fff4e1
|
||||||
|
```
|
||||||
|
|
||||||
|
**代码位置**: `utils/离散备料计划维护数据清理.py:61-167`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 7. 物料清理流程
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
flowchart TB
|
||||||
|
Check{detail_count > 0<br/>&<br/>status = 审批通过} -->|否| Skip[跳过处理]
|
||||||
|
Check -->|是| ClickModify[点击修改按钮]
|
||||||
|
|
||||||
|
ClickModify --> WaitSave[等待保存按钮可见]
|
||||||
|
WaitSave --> Expand[点击展开按钮]
|
||||||
|
Expand --> GetForm[获取表单容器]
|
||||||
|
|
||||||
|
GetForm --> LoopStart{遍历物料}
|
||||||
|
|
||||||
|
LoopStart --> GetSerial[定位序号]
|
||||||
|
GetSerial --> GetCode[获取材料编码]
|
||||||
|
GetCode --> GetName[获取材料名称]
|
||||||
|
GetName --> GetQty1[获取累计待发数量]
|
||||||
|
GetQty1 --> GetQty2[获取累计出库数量]
|
||||||
|
|
||||||
|
GetQty2 --> Match{物料编码<br/>在待删除列表中?}
|
||||||
|
|
||||||
|
Match -->|精确匹配| Delete[标记待删除]
|
||||||
|
Match -->|未匹配| Keep[保留]
|
||||||
|
|
||||||
|
Delete --> Next{下一个?}
|
||||||
|
Keep --> Next
|
||||||
|
|
||||||
|
Next -->|是| LoopStart
|
||||||
|
Next -->|否| Done([完成])
|
||||||
|
|
||||||
|
style Check fill:#fff4e1
|
||||||
|
style Match fill:#fff4e1
|
||||||
|
style Done fill:#e1f5ff
|
||||||
|
```
|
||||||
|
|
||||||
|
**代码位置**: `utils/离散备料计划维护数据清理.py:169-261`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 关键数据结构
|
||||||
|
|
||||||
|
### DiscreteMaterialPlanCleaner 类
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
classDiagram
|
||||||
|
class DiscreteMaterialPlanCleaner {
|
||||||
|
+str username
|
||||||
|
+str password
|
||||||
|
+str manager_name
|
||||||
|
+bool headless
|
||||||
|
+bool verbose
|
||||||
|
+_print(args)
|
||||||
|
+get_production_order_numbers(file)
|
||||||
|
+process_order(frame, order_id, ...)
|
||||||
|
+setup_query_interface(frame)
|
||||||
|
+clean(file, debug_mode, ...)
|
||||||
|
}
|
||||||
|
|
||||||
|
class auth {
|
||||||
|
+login(playwright, username, password)
|
||||||
|
+logout(main_frame, verbose)
|
||||||
|
+close_session(browser, context)
|
||||||
|
}
|
||||||
|
|
||||||
|
class materials_to_delete {
|
||||||
|
+get_materials_to_delete(manager_name)
|
||||||
|
+get_all_materials_to_delete()
|
||||||
|
}
|
||||||
|
|
||||||
|
class production_order_query {
|
||||||
|
+read_production_ids(file)
|
||||||
|
+query_production_order_numbers(ids)
|
||||||
|
}
|
||||||
|
|
||||||
|
DiscreteMaterialPlanCleaner --> auth
|
||||||
|
DiscreteMaterialPlanCleaner --> materials_to_delete
|
||||||
|
DiscreteMaterialPlanCleaner --> production_order_query
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 配置参数说明
|
||||||
|
|
||||||
|
| 参数 | 类型 | 默认值 | 说明 |
|
||||||
|
|------|------|--------|------|
|
||||||
|
| `username` | str | - | 登录用户名 |
|
||||||
|
| `password` | str | - | 登录密码 |
|
||||||
|
| `manager_name` | str | - | 负责人姓名(用于查询待删除物料) |
|
||||||
|
| `headless` | bool | False | 是否无头模式运行浏览器 |
|
||||||
|
| `verbose` | bool | True | 是否打印详细日志 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 文件依赖关系
|
||||||
|
|
||||||
|
```mermaid
|
||||||
|
graph TD
|
||||||
|
A[main_clean.py] --> B[utils/离散备料计划维护数据清理.py]
|
||||||
|
B --> C[utils/auth.py]
|
||||||
|
B --> D[db/production_order_query.py]
|
||||||
|
B --> E[db/materials_to_delete.py]
|
||||||
|
B --> F[db/connection.py]
|
||||||
|
|
||||||
|
style A fill:#e1f5ff
|
||||||
|
style B fill:#e1f5ff
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 执行入口
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 直接运行
|
||||||
|
python main_clean.py
|
||||||
|
|
||||||
|
# 或在虚拟环境中运行
|
||||||
|
.venv\Scripts\activate
|
||||||
|
python main_clean.py
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **浏览器模式**: 默认使用非无头模式 (`headless=False`),便于调试观察
|
||||||
|
2. **日志输出**: 启用详细日志 (`verbose=True`),打印每一步操作
|
||||||
|
3. **数据库连接**: 使用上下文管理器自动管理数据库连接
|
||||||
|
4. **异常处理**: 加载超时会自动重试或跳过
|
||||||
|
5. **调试模式**: 支持在指定订单处暂停调试
|
||||||
27
main.py
27
main.py
@@ -1,27 +0,0 @@
|
|||||||
"""
|
|
||||||
主程序 - 使用离散备料计划维护数据提取工具
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
from utils.离散备料计划维护数据提取 import DiscreteMaterialPlanExtractor
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
# 创建提取器
|
|
||||||
extractor = DiscreteMaterialPlanExtractor(
|
|
||||||
username="BLDpengqiangqiang",
|
|
||||||
password="Cqbld123456.",
|
|
||||||
headless=True,
|
|
||||||
verbose=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
# 设置文件路径
|
|
||||||
order_id_file = os.path.join(os.path.dirname(__file__), "ProductionID.txt")
|
|
||||||
output_file = r"D:/python/playwrite/data/离散备料计划维护_合并.xlsx"
|
|
||||||
|
|
||||||
# 执行提取
|
|
||||||
extractor.extract(order_id_file, output_file)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
"""
|
|
||||||
主程序 - 使用离散备料计划维护数据清理工具
|
|
||||||
"""
|
|
||||||
|
|
||||||
import os
|
|
||||||
from utils.离散备料计划维护数据清理 import DiscreteMaterialPlanCleaner
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
# 创建清理器
|
|
||||||
cleaner = DiscreteMaterialPlanCleaner(
|
|
||||||
username="BLDpengqiangqiang",
|
|
||||||
password="Cqbld123456.",
|
|
||||||
manager_name="彭羽",
|
|
||||||
headless=True,
|
|
||||||
verbose=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
# 设置文件路径
|
|
||||||
order_id_file = os.path.join(os.path.dirname(__file__), "ProductionID.txt")
|
|
||||||
|
|
||||||
# 执行清理
|
|
||||||
cleaner.clean(order_id_file)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
59
main_ui.py
59
main_ui.py
@@ -1,59 +0,0 @@
|
|||||||
#!/usr/bin/env python
|
|
||||||
# -*- coding: utf-8 -*-
|
|
||||||
"""
|
|
||||||
ERP 自动化工具 - GUI 版本
|
|
||||||
|
|
||||||
这是一个基于 Tkinter 的图形用户界面应用程序,
|
|
||||||
用于自动化 ERP 系统(用友 YonBIP)的数据提取、校验和查询任务。
|
|
||||||
|
|
||||||
运行方式:
|
|
||||||
python main_ui.py
|
|
||||||
|
|
||||||
功能:
|
|
||||||
- 数据提取:从 ERP 系统提取备料计划数据
|
|
||||||
- 物料校验:校验物料状态并匹配待删除物料
|
|
||||||
- 数据查询:查询生产订单号等信息
|
|
||||||
- 设置管理:管理系统配置
|
|
||||||
- 用户认证:基于用户权限的数据访问控制
|
|
||||||
"""
|
|
||||||
|
|
||||||
import tkinter as tk
|
|
||||||
from tkinter import messagebox
|
|
||||||
from gui.main_window import MainWindow
|
|
||||||
from gui.login_dialog import LoginDialog
|
|
||||||
from auth.session_manager import SessionManager
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
"""主函数"""
|
|
||||||
# 创建根窗口
|
|
||||||
root = tk.Tk()
|
|
||||||
|
|
||||||
# 显示登录对话框
|
|
||||||
login_dialog = LoginDialog(root)
|
|
||||||
credentials = login_dialog.get_credentials()
|
|
||||||
|
|
||||||
if not credentials:
|
|
||||||
# 用户取消登录或关闭对话框
|
|
||||||
root.destroy()
|
|
||||||
return
|
|
||||||
|
|
||||||
username, password = credentials
|
|
||||||
|
|
||||||
# 认证并初始化会话
|
|
||||||
session_manager = SessionManager.get_instance()
|
|
||||||
|
|
||||||
if not session_manager.login(username, password):
|
|
||||||
messagebox.showerror("登录失败", "用户名或密码错误")
|
|
||||||
root.destroy()
|
|
||||||
return
|
|
||||||
|
|
||||||
# 创建主窗口(传入 session_manager)
|
|
||||||
app = MainWindow(root, session_manager)
|
|
||||||
|
|
||||||
# 启动主事件循环
|
|
||||||
root.mainloop()
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
main()
|
|
||||||
@@ -64,7 +64,7 @@ class DiscreteMaterialPlanCleaner:
|
|||||||
order_id,
|
order_id,
|
||||||
order_index,
|
order_index,
|
||||||
page1,
|
page1,
|
||||||
materials_to_delete=None,
|
manager_name=None,
|
||||||
debug_mode=False,
|
debug_mode=False,
|
||||||
debug_order=None,
|
debug_order=None,
|
||||||
):
|
):
|
||||||
@@ -75,12 +75,12 @@ class DiscreteMaterialPlanCleaner:
|
|||||||
order_id: 订单号
|
order_id: 订单号
|
||||||
order_index: 订单索引
|
order_index: 订单索引
|
||||||
page1: 页面对象
|
page1: 页面对象
|
||||||
materials_to_delete: 待删除物料关键字列表
|
manager_name: 负责人姓名(用于查询待删除物料)
|
||||||
debug_mode: 是否启用调试模式
|
debug_mode: 是否启用调试模式
|
||||||
debug_order: 调试订单号
|
debug_order: 调试订单号
|
||||||
"""
|
"""
|
||||||
if materials_to_delete is None:
|
if manager_name is None:
|
||||||
materials_to_delete = []
|
manager_name = self.manager_name
|
||||||
from playwright.sync_api import TimeoutError
|
from playwright.sync_api import TimeoutError
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
@@ -180,22 +180,23 @@ class DiscreteMaterialPlanCleaner:
|
|||||||
child_form.wait_for(state="visible", timeout=5000)
|
child_form.wait_for(state="visible", timeout=5000)
|
||||||
self._print(f"父容器 .card-table-side-box 已找到")
|
self._print(f"父容器 .card-table-side-box 已找到")
|
||||||
|
|
||||||
page2.pause()
|
# page2.pause()
|
||||||
for id in range(detail_count):
|
for id in range(detail_count):
|
||||||
id_lable_locator = child_form.get_by_text("序号 " + str(id + 1))
|
id_lable_locator = child_form.get_by_text("序号 " + str(id + 1))
|
||||||
id_lable_locator.wait_for(state="visible", timeout=10000)
|
id_lable_locator.wait_for(state="visible", timeout=10000)
|
||||||
self._print(f"处理 {id_lable_locator.inner_text()} ")
|
self._print(f"处理 {id_lable_locator.inner_text()} ")
|
||||||
|
|
||||||
# 获取材料编码(通过文本定位,取第一个input)
|
# 获取材料编码(用于精确匹配)
|
||||||
input_box = (
|
input_box = (
|
||||||
child_form.locator("div")
|
child_form.locator("div")
|
||||||
.filter(has_text=re.compile(r"^材料编码\d{11}$", re.MULTILINE))
|
.filter(has_text=re.compile(r"^材料编码\d{11}$", re.MULTILINE))
|
||||||
.locator("input")
|
.locator("input")
|
||||||
.first
|
.first
|
||||||
)
|
)
|
||||||
self._print(f"材料编码:{input_box.input_value()}")
|
material_code = input_box.input_value()
|
||||||
|
self._print(f"材料编码:{material_code}")
|
||||||
|
|
||||||
# 获取材料名称
|
# 获取材料名称(仅用于日志)
|
||||||
input_box = (
|
input_box = (
|
||||||
child_form.locator("div")
|
child_form.locator("div")
|
||||||
.filter(has_text=re.compile(r"^材料名称$"))
|
.filter(has_text=re.compile(r"^材料名称$"))
|
||||||
@@ -220,22 +221,17 @@ class DiscreteMaterialPlanCleaner:
|
|||||||
)
|
)
|
||||||
self._print(f"累计出库数量:{input_box.input_value()}")
|
self._print(f"累计出库数量:{input_box.input_value()}")
|
||||||
|
|
||||||
# 检查是否需要清理该物料
|
# 检查是否需要清理该物料(直接数据库查询)
|
||||||
should_delete = False
|
from db.materials_to_delete import should_delete_material
|
||||||
matched_keyword = None
|
should_delete = should_delete_material(manager_name, material_code)
|
||||||
for keyword in materials_to_delete:
|
|
||||||
if keyword in material_name:
|
|
||||||
should_delete = True
|
|
||||||
matched_keyword = keyword
|
|
||||||
break
|
|
||||||
|
|
||||||
if should_delete:
|
if should_delete:
|
||||||
self._print(
|
self._print(
|
||||||
f">>> 需要清理:材料名称【{material_name}】匹配关键字【{matched_keyword}】"
|
f">>> 需要清理:材料名称【{material_name}】材料编码【{material_code}】"
|
||||||
)
|
)
|
||||||
# TODO: 执行删除操作
|
# TODO: 执行删除操作
|
||||||
else:
|
else:
|
||||||
self._print(f"保留:材料名称【{material_name}】无需清理")
|
self._print(f"保留:材料名称【{material_name}】材料编码【{material_code}】无需清理")
|
||||||
|
|
||||||
if id != detail_count - 1:
|
if id != detail_count - 1:
|
||||||
child_form.get_by_role("button").filter(
|
child_form.get_by_role("button").filter(
|
||||||
@@ -298,10 +294,7 @@ class DiscreteMaterialPlanCleaner:
|
|||||||
debug_mode: 是否启用调试模式
|
debug_mode: 是否启用调试模式
|
||||||
debug_order: 调试订单索引
|
debug_order: 调试订单索引
|
||||||
"""
|
"""
|
||||||
# 获取待删除物料列表
|
self._print(f"使用负责人 [{self.manager_name}] 进行数据清理")
|
||||||
self._print(f"正在查询负责人 [{self.manager_name}] 的待删除物料列表...")
|
|
||||||
materials_to_delete = get_materials_to_delete(self.manager_name)
|
|
||||||
self._print(f"查询到 {len(materials_to_delete)} 个待删除物料关键字")
|
|
||||||
|
|
||||||
with sync_playwright() as playwright:
|
with sync_playwright() as playwright:
|
||||||
# 调用登录模块
|
# 调用登录模块
|
||||||
@@ -348,7 +341,7 @@ class DiscreteMaterialPlanCleaner:
|
|||||||
order_id,
|
order_id,
|
||||||
order_index,
|
order_index,
|
||||||
page1,
|
page1,
|
||||||
materials_to_delete,
|
self.manager_name,
|
||||||
debug_mode=debug_mode,
|
debug_mode=debug_mode,
|
||||||
debug_order=debug_order,
|
debug_order=debug_order,
|
||||||
)
|
)
|
||||||
|
|||||||
Reference in New Issue
Block a user