First Commit
This commit is contained in:
11
.claude/settings.local.json
Normal file
11
.claude/settings.local.json
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"permissions": {
|
||||||
|
"allow": [
|
||||||
|
"Bash(pip install:*)",
|
||||||
|
"Bash(python3:*)",
|
||||||
|
"Bash(source venv/bin/activate)"
|
||||||
|
],
|
||||||
|
"deny": [],
|
||||||
|
"ask": []
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
AccountingEntries.xlsx
Normal file
BIN
AccountingEntries.xlsx
Normal file
Binary file not shown.
79
CHANGELOG.md
Normal file
79
CHANGELOG.md
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
# 更新日志
|
||||||
|
|
||||||
|
## v1.2 (2025-10-17)
|
||||||
|
|
||||||
|
### 新功能
|
||||||
|
|
||||||
|
#### 1. 汇率文件支持
|
||||||
|
- **功能**: 支持从 `exchange_rate.txt` 文件读取汇率
|
||||||
|
- **优先级**: 文件汇率 > 程序默认汇率
|
||||||
|
- **使用方法**:
|
||||||
|
```bash
|
||||||
|
echo "7.25" > exchange_rate.txt
|
||||||
|
python3 generate_accounting_entries.py
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. 智能汇率验证
|
||||||
|
- **范围检查**: 汇率必须在 0.1 ~ 100 范围内
|
||||||
|
- **格式验证**: 自动检测非数字格式
|
||||||
|
- **错误处理**: 异常情况自动回退到默认汇率 (7.1072)
|
||||||
|
|
||||||
|
#### 3. 详细日志输出
|
||||||
|
程序运行时会显示汇率来源:
|
||||||
|
- 从文件读取: `从 exchange_rate.txt 读取汇率: 7.25`
|
||||||
|
- 文件不存在: `汇率文件 exchange_rate.txt 不存在,使用默认汇率: 7.1072`
|
||||||
|
- 格式错误: `汇率文件 exchange_rate.txt 中的值无法解析,使用默认汇率: 7.1072`
|
||||||
|
- 值不合理: `汇率文件中的值 150.0 不合理,使用默认汇率: 7.1072`
|
||||||
|
|
||||||
|
### 代码修改
|
||||||
|
|
||||||
|
**文件**: `generate_accounting_entries.py`
|
||||||
|
|
||||||
|
1. **新增导入**:
|
||||||
|
- `import os` - 用于文件检查
|
||||||
|
|
||||||
|
2. **新增函数**:
|
||||||
|
- `load_exchange_rate()` (line 16-52) - 汇率加载和验证逻辑
|
||||||
|
|
||||||
|
3. **修改函数签名**:
|
||||||
|
- `create_accounting_entries(data, exchange_rate)` - 添加汇率参数
|
||||||
|
|
||||||
|
4. **修改 main 函数**:
|
||||||
|
- 调用 `load_exchange_rate()` 获取汇率
|
||||||
|
- 传递汇率参数到 `create_accounting_entries()`
|
||||||
|
|
||||||
|
### 文档更新
|
||||||
|
|
||||||
|
1. **User.md**:
|
||||||
|
- 更新"汇率配置"章节,添加文件方法说明
|
||||||
|
- 更新 Q4 常见问题
|
||||||
|
- 更新文件说明表,添加 `exchange_rate.txt`
|
||||||
|
- 添加 v1.2 更新记录
|
||||||
|
|
||||||
|
2. **CLAUDE.md**:
|
||||||
|
- 更新"Exchange Rate"配置章节
|
||||||
|
- 添加详细的错误处理说明和示例
|
||||||
|
- 更新版本历史
|
||||||
|
|
||||||
|
### 测试结果
|
||||||
|
|
||||||
|
✅ 正常汇率文件 (7.25): 成功读取
|
||||||
|
✅ 文件不存在: 使用默认汇率
|
||||||
|
✅ 非数字格式 (abc): 使用默认汇率
|
||||||
|
✅ 不合理值 (150): 使用默认汇率
|
||||||
|
|
||||||
|
### 向后兼容性
|
||||||
|
|
||||||
|
- ✅ 完全向后兼容
|
||||||
|
- ✅ 不影响现有功能
|
||||||
|
- ✅ 无 `exchange_rate.txt` 文件时使用程序默认值
|
||||||
|
|
||||||
|
### 文件清单
|
||||||
|
|
||||||
|
| 文件 | 状态 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| `generate_accounting_entries.py` | 已修改 | 添加汇率文件读取功能 |
|
||||||
|
| `exchange_rate.txt` | 新增 | 汇率配置文件 (可选) |
|
||||||
|
| `User.md` | 已更新 | 用户文档 |
|
||||||
|
| `CLAUDE.md` | 已更新 | 开发文档 |
|
||||||
|
| `CHANGELOG.md` | 新增 | 本文件 |
|
||||||
236
CLAUDE.md
Normal file
236
CLAUDE.md
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
# CLAUDE.md
|
||||||
|
|
||||||
|
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||||
|
|
||||||
|
## Project Overview
|
||||||
|
|
||||||
|
财务Excel数据处理系统 (Financial Excel Data Processing System) - A Python-based automation system for processing financial Excel data, extracting payment information, and generating standardized accounting entries with data validation and error marking capabilities.
|
||||||
|
|
||||||
|
**Language**: Chinese (中文) - All documentation, comments, and output are in Chinese.
|
||||||
|
|
||||||
|
## Development Commands
|
||||||
|
|
||||||
|
### Setup and Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install dependencies (system-wide)
|
||||||
|
pip install openpyxl --break-system-packages
|
||||||
|
|
||||||
|
# OR use virtual environment (recommended)
|
||||||
|
python3 -m venv venv
|
||||||
|
source venv/bin/activate # Linux/Mac
|
||||||
|
# venv\Scripts\activate # Windows
|
||||||
|
pip install openpyxl
|
||||||
|
```
|
||||||
|
|
||||||
|
### Run the Processing Pipeline
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Step 1: Extract data from Excel to JSON
|
||||||
|
python3 process_excel.py
|
||||||
|
|
||||||
|
# Step 2: Generate accounting entries Excel
|
||||||
|
python3 generate_accounting_entries.py
|
||||||
|
|
||||||
|
# Optional: Analyze Excel structure (debugging tool)
|
||||||
|
python3 analyze_excel.py
|
||||||
|
```
|
||||||
|
|
||||||
|
### Verify Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -c "import openpyxl; print(openpyxl.__version__)"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture and Data Flow
|
||||||
|
|
||||||
|
### Processing Pipeline
|
||||||
|
|
||||||
|
```
|
||||||
|
data/data.xlsx (Raw financial data)
|
||||||
|
↓
|
||||||
|
[process_excel.py] - Extract payment records
|
||||||
|
↓
|
||||||
|
res.json (Intermediate JSON data)
|
||||||
|
↓
|
||||||
|
[generate_accounting_entries.py] - Generate accounting entries
|
||||||
|
↓
|
||||||
|
AccountingEntries.xlsx (Final accounting entry table)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Components
|
||||||
|
|
||||||
|
1. **`process_excel.py`** - Excel Data Extraction Engine
|
||||||
|
- Handles merged and non-merged cells in column F (ReceivedAmount)
|
||||||
|
- Extracts orders from rows within merged cell ranges
|
||||||
|
- Validates amounts: `ReceivedAmount + HandlingFee ≈ Sum(Order[].Amount)` (tolerance: 0.01)
|
||||||
|
- Uses `data_only=True` to read formula results from column O
|
||||||
|
|
||||||
|
2. **`generate_accounting_entries.py`** - Accounting Entry Generator
|
||||||
|
- Creates debit/credit entries following Chinese accounting standards
|
||||||
|
- Merges cells for same ReceivedAmount groups
|
||||||
|
- Marks validation failures with pink background (#FAD1D4)
|
||||||
|
- Applies fixed exchange rate to currency conversions
|
||||||
|
|
||||||
|
3. **`analyze_excel.py`** - Structure Analysis Utility
|
||||||
|
- Debugging tool to inspect merged cells
|
||||||
|
- Preview data structure
|
||||||
|
|
||||||
|
### Data Structures
|
||||||
|
|
||||||
|
#### res.json Schema
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"ReceivedAmount": 12125, // Column F - supports merged cells
|
||||||
|
"HandlingFee": 25, // Column G - null becomes 0
|
||||||
|
"Order": [
|
||||||
|
{
|
||||||
|
"OrderNum": "XLRQD300T25", // Column H
|
||||||
|
"Amount": 550, // Column I
|
||||||
|
"AccountName": "24台湾长荣航运" // Column O - formula result
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"checkRes": true // Validation: amount match within 0.01
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Accounting Entry Rules
|
||||||
|
|
||||||
|
**For each ReceivedAmount record:**
|
||||||
|
|
||||||
|
1. **Debit Entry (到账金额)** - 1 record per ReceivedAmount
|
||||||
|
- Account: `1002.02` - 银行存款 - 中行USD
|
||||||
|
- Currency: 美元 (USD)
|
||||||
|
- Amount: `ReceivedAmount × EXCHANGE_RATE`
|
||||||
|
|
||||||
|
2. **Debit Entry (手续费)** - Only if HandlingFee > 0
|
||||||
|
- Account: `5603.03` - 财务费用-手续费
|
||||||
|
- Currency: 人民币 (RMB)
|
||||||
|
- Amount: `HandlingFee × EXCHANGE_RATE`
|
||||||
|
|
||||||
|
3. **Credit Entries (订单明细)** - 1 record per Order
|
||||||
|
- Account: `1122` - 应收账款
|
||||||
|
- Currency: 美元 (USD)
|
||||||
|
- Amount: `Order.Amount × EXCHANGE_RATE`
|
||||||
|
- **Display Order.Amount in "应收账款" column**
|
||||||
|
- Skip orders where Amount is null
|
||||||
|
|
||||||
|
### Special Processing Logic
|
||||||
|
|
||||||
|
#### Merged Cell Handling (process_excel.py:33-69)
|
||||||
|
- `get_f_column_ranges()`: Identifies all data ranges in column F
|
||||||
|
- Handles mixed scenarios: merged and non-merged cells
|
||||||
|
- Non-merged cells are treated as single-row ranges
|
||||||
|
- Merged cell value read from top-left corner (min_row, min_col)
|
||||||
|
|
||||||
|
#### Validation and Error Marking
|
||||||
|
- **checkRes calculation**: `abs((ReceivedAmount + HandlingFee) - Sum(Order[].Amount)) < 0.01`
|
||||||
|
- **Error marking**: Pink background (#FAD1D4) applied to all entries where checkRes = false
|
||||||
|
- Background color applied **before** cell merging to ensure visibility
|
||||||
|
|
||||||
|
#### Cell Merging Strategy (generate_accounting_entries.py:178-206)
|
||||||
|
- Groups entries by `(ReceivedAmount, HandlingFee)` key
|
||||||
|
- Merges "到账金额" (column A) and "手续费" (column B) for consecutive rows
|
||||||
|
- Centers content vertically and horizontally
|
||||||
|
- Re-applies background color after merging
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Exchange Rate
|
||||||
|
|
||||||
|
**Priority**: Program reads exchange rate in the following order:
|
||||||
|
|
||||||
|
1. **From `exchange_rate.txt` file** (if exists in current directory)
|
||||||
|
- Create a text file named `exchange_rate.txt` containing only the exchange rate value
|
||||||
|
- Example: `echo "7.25" > exchange_rate.txt`
|
||||||
|
- Validation: Rate must be between 0.1 and 100, otherwise falls back to default
|
||||||
|
|
||||||
|
2. **From default constant** (if file doesn't exist or contains invalid value)
|
||||||
|
- Location: `generate_accounting_entries.py:13`
|
||||||
|
- Default value: `7.1072`
|
||||||
|
|
||||||
|
**Error Handling** (generate_accounting_entries.py:16-52):
|
||||||
|
- File not found → Use default rate
|
||||||
|
- Invalid format (non-numeric) → Use default rate
|
||||||
|
- Unreasonable value (<0.1 or >100) → Use default rate
|
||||||
|
- Any other error → Use default rate
|
||||||
|
|
||||||
|
**Examples**:
|
||||||
|
```bash
|
||||||
|
# Set custom exchange rate
|
||||||
|
echo "7.25" > exchange_rate.txt
|
||||||
|
|
||||||
|
# Program will display which rate is being used
|
||||||
|
python3 generate_accounting_entries.py
|
||||||
|
# Output: "从 exchange_rate.txt 读取汇率: 7.25"
|
||||||
|
|
||||||
|
# Remove file to use default
|
||||||
|
rm exchange_rate.txt
|
||||||
|
python3 generate_accounting_entries.py
|
||||||
|
# Output: "汇率文件 exchange_rate.txt 不存在,使用默认汇率: 7.1072"
|
||||||
|
```
|
||||||
|
|
||||||
|
### Column Mapping (data/data.xlsx)
|
||||||
|
| Field | Column | Notes |
|
||||||
|
|-------|--------|-------|
|
||||||
|
| ReceivedAmount | F (6) | Supports merged cells |
|
||||||
|
| HandlingFee | G (7) | Null → 0 |
|
||||||
|
| OrderNum | H (8) | Skip if empty |
|
||||||
|
| Amount | I (9) | Null orders skipped |
|
||||||
|
| AccountName | O (15) | Formula result (data_only=True) |
|
||||||
|
|
||||||
|
### Excel Output Format
|
||||||
|
**Column widths**: `[12, 10, 18, 12, 25, 25, 8, 15, 25, 25, 10, 10, 12, 15]`
|
||||||
|
|
||||||
|
**Headers**:
|
||||||
|
```
|
||||||
|
到账金额, 手续费, 订单号, 应收账款, 金蝶名称,
|
||||||
|
摘要, 借/贷, 科目代码(*), 科目名称(*),
|
||||||
|
核算项目, 币别, 汇率, 原币金额, 金额
|
||||||
|
```
|
||||||
|
|
||||||
|
**Header style**: Bold, blue background (#CCE5FF), centered
|
||||||
|
|
||||||
|
## Important Implementation Notes
|
||||||
|
|
||||||
|
1. **Data starts from row 2** (row 1 is header)
|
||||||
|
|
||||||
|
2. **Formula handling**: Always use `data_only=True` when loading workbook to read calculated values
|
||||||
|
|
||||||
|
3. **Order filtering**: Skip rows where OrderNum is None or empty string
|
||||||
|
|
||||||
|
4. **Amount precision**: All calculations rounded to 2 decimal places
|
||||||
|
|
||||||
|
5. **UTF-8 encoding**: All files use UTF-8 encoding
|
||||||
|
|
||||||
|
6. **Error handling**:
|
||||||
|
- File not found: Exit with error message
|
||||||
|
- Invalid sheet: Exit with error message
|
||||||
|
- Invalid data: Log and skip row
|
||||||
|
- checkRes=false: Mark but continue processing
|
||||||
|
|
||||||
|
7. **Performance**: Handles 300+ rows of Excel data generating 500+ accounting entries in <10 seconds
|
||||||
|
|
||||||
|
## Testing Guidance
|
||||||
|
|
||||||
|
### Test Scenarios (from task.md:253-272)
|
||||||
|
|
||||||
|
1. **Single order, no fee**: ReceivedAmount=695, HandlingFee=0, Order[0].Amount=695
|
||||||
|
- Expected: 2 entries (debit + credit), checkRes=true
|
||||||
|
|
||||||
|
2. **Multiple orders with fee**: ReceivedAmount=12125, HandlingFee=25, Orders=[550, 11600]
|
||||||
|
- Expected: 4 entries, checkRes=true
|
||||||
|
|
||||||
|
3. **Amount mismatch**: ReceivedAmount=17270, HandlingFee=0, Orders=[5676, 11450]
|
||||||
|
- Expected: checkRes=false, pink background on all entries
|
||||||
|
|
||||||
|
4. **Null order amount**: ReceivedAmount=240, HandlingFee=25, Order[0].Amount=null
|
||||||
|
- Expected: Skip order credit entry, no error
|
||||||
|
|
||||||
|
## Version History
|
||||||
|
|
||||||
|
- **v1.2** (2025-10-17): Added exchange rate file support (`exchange_rate.txt`), intelligent rate validation, improved error handling
|
||||||
|
- **v1.1** (2025-01-17): Optimized accounting rules - removed redundant debit entries, simplified single-order logic
|
||||||
|
- **v1.0** (2025-01-17): Initial release with extraction, generation, validation, and error marking features
|
||||||
360
User.md
Normal file
360
User.md
Normal file
@@ -0,0 +1,360 @@
|
|||||||
|
# 财务Excel数据处理程序 - 使用说明
|
||||||
|
|
||||||
|
## 概述
|
||||||
|
|
||||||
|
本程序用于处理财务Excel数据,自动提取收款信息并生成会计分录表。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 前置要求
|
||||||
|
|
||||||
|
### 运行环境
|
||||||
|
|
||||||
|
- **Python版本**: Python 3.x
|
||||||
|
- **操作系统**: Windows / Linux / macOS
|
||||||
|
|
||||||
|
### 依赖库安装
|
||||||
|
|
||||||
|
本程序依赖以下Python第三方包:
|
||||||
|
|
||||||
|
#### 1. openpyxl
|
||||||
|
|
||||||
|
**用途**: Excel文件读写操作
|
||||||
|
|
||||||
|
**安装命令**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install openpyxl --break-system-packages
|
||||||
|
```
|
||||||
|
|
||||||
|
或使用虚拟环境(推荐):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 创建虚拟环境
|
||||||
|
python3 -m venv venv
|
||||||
|
|
||||||
|
# 激活虚拟环境
|
||||||
|
# Linux/Mac:
|
||||||
|
source venv/bin/activate
|
||||||
|
# Windows:
|
||||||
|
venv\Scripts\activate
|
||||||
|
|
||||||
|
# 安装依赖
|
||||||
|
pip install openpyxl
|
||||||
|
```
|
||||||
|
|
||||||
|
**版本要求**: 建议使用最新稳定版本
|
||||||
|
|
||||||
|
#### 验证安装
|
||||||
|
|
||||||
|
安装完成后,可以通过以下命令验证:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 -c "import openpyxl; print(openpyxl.__version__)"
|
||||||
|
```
|
||||||
|
|
||||||
|
如果输出版本号(例如: 3.1.2),则说明安装成功。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 功能说明
|
||||||
|
|
||||||
|
### 1. 数据提取 (`process_excel.py`)
|
||||||
|
|
||||||
|
从Excel文件中提取财务数据并输出为JSON格式。
|
||||||
|
|
||||||
|
**输入文件**: `data/data.xlsx` (Sheet1)
|
||||||
|
|
||||||
|
**输出文件**: `res.json`
|
||||||
|
|
||||||
|
**提取字段**:
|
||||||
|
- `ReceivedAmount`: 实收金额 (F列)
|
||||||
|
- `HandlingFee`: 手续费 (G列,空值记为0)
|
||||||
|
- `Order`: 订单列表
|
||||||
|
- `OrderNum`: 订单号 (H列)
|
||||||
|
- `Amount`: 金额 (I列)
|
||||||
|
- `AccountName`: 账户名称 (O列)
|
||||||
|
- `checkRes`: 验证结果 (实收金额+手续费 = 订单金额之和)
|
||||||
|
|
||||||
|
**运行命令**:
|
||||||
|
```bash
|
||||||
|
python3 process_excel.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**输出示例**:
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"ReceivedAmount": 12125,
|
||||||
|
"HandlingFee": 25,
|
||||||
|
"Order": [
|
||||||
|
{
|
||||||
|
"OrderNum": "XLRQD300T25",
|
||||||
|
"Amount": 550,
|
||||||
|
"AccountName": "24台湾长荣航运"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"checkRes": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 2. 会计分录生成 (`generate_accounting_entries.py`)
|
||||||
|
|
||||||
|
根据`res.json`生成标准会计分录表。
|
||||||
|
|
||||||
|
**输入文件**: `res.json`
|
||||||
|
|
||||||
|
**输出文件**: `AccountingEntries.xlsx`
|
||||||
|
|
||||||
|
**运行命令**:
|
||||||
|
```bash
|
||||||
|
python3 generate_accounting_entries.py
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 会计分录规则
|
||||||
|
|
||||||
|
### 基本规则
|
||||||
|
|
||||||
|
每笔到账金额产生以下分录:
|
||||||
|
|
||||||
|
1. **到账金额 - 借方** (每笔记录1条)
|
||||||
|
- 科目代码: `1002.02`
|
||||||
|
- 科目名称: `银行存款 - 中行USD`
|
||||||
|
- 币别: 美元
|
||||||
|
- 原币金额: ReceivedAmount
|
||||||
|
- 金额: ReceivedAmount × 汇率
|
||||||
|
|
||||||
|
2. **手续费 - 借方** (如果手续费>0)
|
||||||
|
- 科目代码: `5603.03`
|
||||||
|
- 科目名称: `财务费用-手续费`
|
||||||
|
- 币别: 人民币
|
||||||
|
- 金额: HandlingFee × 汇率
|
||||||
|
|
||||||
|
3. **订单明细 - 贷方** (每个Order记录1条)
|
||||||
|
- 科目代码: `1122`
|
||||||
|
- 科目名称: `应收账款`
|
||||||
|
- 应收账款: Order.Amount (显示在"应收账款"列)
|
||||||
|
- 币别: 美元
|
||||||
|
- 原币金额: Order.Amount
|
||||||
|
- 金额: Order.Amount × 汇率
|
||||||
|
|
||||||
|
### 特殊规则
|
||||||
|
|
||||||
|
- **金额验证**: checkRes为false的记录,所有相关分录行标记为粉红色背景(#FAD1D4)
|
||||||
|
- **单元格合并**: 同一笔到账金额的所有分录,"到账金额"和"手续费"列会合并显示
|
||||||
|
- **空值处理**: 订单金额为空的订单会被跳过,不生成贷方分录
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 汇率配置
|
||||||
|
|
||||||
|
程序支持两种方式设置汇率:
|
||||||
|
|
||||||
|
### 方法一: 使用汇率文件 (推荐)
|
||||||
|
|
||||||
|
在程序目录下创建 `exchange_rate.txt` 文件,文件中只包含汇率数值。
|
||||||
|
|
||||||
|
**操作步骤**:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 创建汇率文件
|
||||||
|
echo "7.25" > exchange_rate.txt
|
||||||
|
|
||||||
|
# 运行程序,会自动读取该文件中的汇率
|
||||||
|
python3 generate_accounting_entries.py
|
||||||
|
```
|
||||||
|
|
||||||
|
**输出示例**:
|
||||||
|
```
|
||||||
|
从 exchange_rate.txt 读取汇率: 7.25
|
||||||
|
使用汇率: 7.25
|
||||||
|
```
|
||||||
|
|
||||||
|
**注意事项**:
|
||||||
|
- 汇率值必须是有效数字
|
||||||
|
- 汇率范围: 0.1 ~ 100 (超出范围会使用默认汇率)
|
||||||
|
- 文件格式错误会自动使用默认汇率
|
||||||
|
|
||||||
|
### 方法二: 修改程序默认值
|
||||||
|
|
||||||
|
编辑 `generate_accounting_entries.py` 文件第13行:
|
||||||
|
|
||||||
|
```python
|
||||||
|
DEFAULT_EXCHANGE_RATE = 7.1072 # 修改此默认值
|
||||||
|
```
|
||||||
|
|
||||||
|
**默认汇率**: 7.1072
|
||||||
|
|
||||||
|
**优先级**: 汇率文件 > 程序默认值
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 文件说明
|
||||||
|
|
||||||
|
| 文件名 | 说明 |
|
||||||
|
|--------|------|
|
||||||
|
| `data/data.xlsx` | 原始财务数据Excel文件 |
|
||||||
|
| `process_excel.py` | Excel数据提取程序 |
|
||||||
|
| `res.json` | 提取的财务数据(JSON格式) |
|
||||||
|
| `generate_accounting_entries.py` | 会计分录生成程序 |
|
||||||
|
| `AccountingEntries.xlsx` | 生成的会计分录表 |
|
||||||
|
| `exchange_rate.txt` | 汇率配置文件(可选) |
|
||||||
|
| `analyze_excel.py` | Excel结构分析工具(可选) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 使用流程
|
||||||
|
|
||||||
|
### 标准流程
|
||||||
|
|
||||||
|
1. **准备数据**
|
||||||
|
```bash
|
||||||
|
# 确保 data/data.xlsx 文件存在
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **提取数据**
|
||||||
|
```bash
|
||||||
|
python3 process_excel.py
|
||||||
|
# 输出: res.json
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **生成会计分录**
|
||||||
|
```bash
|
||||||
|
python3 generate_accounting_entries.py
|
||||||
|
# 输出: AccountingEntries.xlsx
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **检查结果**
|
||||||
|
- 打开 `AccountingEntries.xlsx`
|
||||||
|
- 粉红色背景的行表示金额不匹配,需要核对
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 数据验证
|
||||||
|
|
||||||
|
### checkRes字段说明
|
||||||
|
|
||||||
|
- **true**: 实收金额 + 手续费 = 订单金额之和 (误差<0.01)
|
||||||
|
- **false**: 金额不匹配,需要人工核对
|
||||||
|
|
||||||
|
### 识别问题记录
|
||||||
|
|
||||||
|
在 `AccountingEntries.xlsx` 中:
|
||||||
|
- 粉红色背景(#FAD1D4)的行表示该笔记录金额不匹配
|
||||||
|
- 建议优先核对这些记录
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 常见问题
|
||||||
|
|
||||||
|
### Q1: 如何处理合并单元格?
|
||||||
|
|
||||||
|
程序自动处理F列的合并单元格:
|
||||||
|
- 合并单元格: 该区域内所有行属于同一笔记录
|
||||||
|
- 非合并单元格: 每行单独处理
|
||||||
|
|
||||||
|
### Q2: 手续费为空怎么办?
|
||||||
|
|
||||||
|
程序自动将空值记为0。
|
||||||
|
|
||||||
|
### Q3: 订单金额为空怎么办?
|
||||||
|
|
||||||
|
金额为空的订单会被跳过,不生成贷方分录。
|
||||||
|
|
||||||
|
### Q4: 如何修改汇率?
|
||||||
|
|
||||||
|
**推荐方法**: 创建 `exchange_rate.txt` 文件,输入汇率值即可。
|
||||||
|
|
||||||
|
```bash
|
||||||
|
echo "7.25" > exchange_rate.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
**备选方法**: 编辑 `generate_accounting_entries.py` 第13行的 `DEFAULT_EXCHANGE_RATE` 值。
|
||||||
|
|
||||||
|
### Q5: Excel列对应关系
|
||||||
|
|
||||||
|
| 字段 | Excel列 | 说明 |
|
||||||
|
|------|---------|------|
|
||||||
|
| ReceivedAmount | F | 实收金额 |
|
||||||
|
| HandlingFee | G | 手续费 |
|
||||||
|
| OrderNum | H | 订单号 |
|
||||||
|
| Amount | I | 解款金额 |
|
||||||
|
| AccountName | O | 金蝶名称 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 输出示例
|
||||||
|
|
||||||
|
### res.json 示例
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"ReceivedAmount": 9455,
|
||||||
|
"HandlingFee": 25,
|
||||||
|
"Order": [
|
||||||
|
{
|
||||||
|
"OrderNum": "XLRQD063M25",
|
||||||
|
"Amount": 9480,
|
||||||
|
"AccountName": "20 Transsea"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"checkRes": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### AccountingEntries.xlsx 结构
|
||||||
|
|
||||||
|
| 到账金额 | 手续费 | 订单号 | 应收账款 | 金蝶名称 | 摘要 | 借/贷 | 科目代码 | 科目名称 | 核算项目 | 币别 | 汇率 | 原币金额 | 金额 |
|
||||||
|
|---------|--------|--------|---------|---------|------|-------|----------|----------|----------|------|------|----------|------|
|
||||||
|
| 9455 | 25 | XLRQD063M25 | | 20 Transsea | 美金收款-XLRQD063M25 | 借 | 1002.02 | 银行存款 - 中行USD | 20 Transsea | 美元 | 7.1072 | 9455 | 67,188.54 |
|
||||||
|
| (合并) | (合并) | XLRQD063M25 | | | 美金收款-XLRQD063M25 | 借 | 5603.03 | 财务费用-手续费 | | 人民币 | | | 177.68 |
|
||||||
|
| (合并) | (合并) | XLRQD063M25 | 9480 | 20 Transsea | 美金收款-XLRQD063M25 | 贷 | 1122 | 应收账款 | 20 Transsea | 美元 | 7.1072 | 9480 | 67,366.22 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 注意事项
|
||||||
|
|
||||||
|
1. **数据起始行**: 程序从第2行开始读取(第1行为表头)
|
||||||
|
2. **汇率固定**: 默认使用固定汇率7.1072,不会从Excel读取
|
||||||
|
3. **金额精度**: 计算结果保留2位小数
|
||||||
|
4. **合并单元格**: "到账金额"和"手续费"列会自动合并
|
||||||
|
5. **背景标记**: checkRes=false的记录用粉红色标记
|
||||||
|
6. **文件编码**: 所有文件使用UTF-8编码
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 更新记录
|
||||||
|
|
||||||
|
- **v1.2** - 2025-10-17
|
||||||
|
- 新增汇率文件支持: 可通过 `exchange_rate.txt` 设置汇率
|
||||||
|
- 智能汇率验证: 自动检测异常汇率并回退到默认值
|
||||||
|
- 改进错误处理: 汇率文件异常时提供详细提示信息
|
||||||
|
|
||||||
|
- **v1.1** - 2025-01-17
|
||||||
|
- 优化会计分录规则: 移除"到账金额-贷方"记录
|
||||||
|
- 每个Order记录都生成对应的贷方分录
|
||||||
|
- Order生成的贷方记录在"应收账款"列显示Amount金额
|
||||||
|
- 简化单订单处理逻辑
|
||||||
|
|
||||||
|
- **v1.0** - 2025-01-17
|
||||||
|
- Excel数据提取
|
||||||
|
- 会计分录生成
|
||||||
|
- 合并单元格支持
|
||||||
|
- 金额验证功能
|
||||||
|
- 背景色标记
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 联系支持
|
||||||
|
|
||||||
|
如遇问题或需要帮助,请检查:
|
||||||
|
1. Excel文件格式是否正确
|
||||||
|
2. 列映射是否匹配
|
||||||
|
3. Python依赖是否已安装
|
||||||
|
4. 汇率设置是否正确
|
||||||
BIN
__pycache__/generate_accounting_entries.cpython-312.pyc
Normal file
BIN
__pycache__/generate_accounting_entries.cpython-312.pyc
Normal file
Binary file not shown.
43
analyze_excel.py
Normal file
43
analyze_excel.py
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""分析Excel文件结构,查看合并单元格信息"""
|
||||||
|
|
||||||
|
from openpyxl import load_workbook
|
||||||
|
|
||||||
|
# 加载Excel文件
|
||||||
|
wb = load_workbook('data/data.xlsx')
|
||||||
|
ws = wb['Sheet1']
|
||||||
|
|
||||||
|
print("=" * 80)
|
||||||
|
print("合并单元格信息:")
|
||||||
|
print("=" * 80)
|
||||||
|
|
||||||
|
# 获取所有合并单元格
|
||||||
|
merged_cells = list(ws.merged_cells.ranges)
|
||||||
|
print(f"总共有 {len(merged_cells)} 个合并单元格区域\n")
|
||||||
|
|
||||||
|
# 筛选F列的合并单元格
|
||||||
|
f_column_merges = []
|
||||||
|
for merge in merged_cells:
|
||||||
|
# 检查是否在F列
|
||||||
|
if merge.min_col == 6 and merge.max_col == 6: # F列是第6列
|
||||||
|
f_column_merges.append(merge)
|
||||||
|
print(f"F列合并: {merge} (行{merge.min_row}到{merge.max_row})")
|
||||||
|
|
||||||
|
print(f"\nF列合并单元格数量: {len(f_column_merges)}")
|
||||||
|
|
||||||
|
print("\n" + "=" * 80)
|
||||||
|
print("前10行数据预览 (F, G, H, I, O列):")
|
||||||
|
print("=" * 80)
|
||||||
|
print(f"{'行号':<6} {'F列(实收)':<15} {'G列(手续费)':<15} {'H列(订单号)':<20} {'I列(金额)':<15} {'O列(账户)':<20}")
|
||||||
|
print("-" * 100)
|
||||||
|
|
||||||
|
for row in range(1, min(11, ws.max_row + 1)):
|
||||||
|
f_val = ws.cell(row, 6).value # F列
|
||||||
|
g_val = ws.cell(row, 7).value # G列
|
||||||
|
h_val = ws.cell(row, 8).value # H列
|
||||||
|
i_val = ws.cell(row, 9).value # I列
|
||||||
|
o_val = ws.cell(row, 15).value # O列
|
||||||
|
|
||||||
|
print(f"{row:<6} {str(f_val)[:15]:<15} {str(g_val)[:15]:<15} {str(h_val)[:20]:<20} {str(i_val)[:15]:<15} {str(o_val)[:20]:<20}")
|
||||||
|
|
||||||
|
print(f"\n总行数: {ws.max_row}")
|
||||||
BIN
data/data.xlsx
Normal file
BIN
data/data.xlsx
Normal file
Binary file not shown.
BIN
data/~$data.xlsx
Normal file
BIN
data/~$data.xlsx
Normal file
Binary file not shown.
1
exchange_rate.txt
Normal file
1
exchange_rate.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
7.1072
|
||||||
308
generate_accounting_entries.py
Normal file
308
generate_accounting_entries.py
Normal file
@@ -0,0 +1,308 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
根据res.json生成会计分录表AccountingEntries.xlsx
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
from openpyxl import Workbook
|
||||||
|
from openpyxl.styles import Font, Alignment, PatternFill
|
||||||
|
from typing import List, Dict, Any
|
||||||
|
|
||||||
|
# 默认汇率
|
||||||
|
DEFAULT_EXCHANGE_RATE = 7.1072
|
||||||
|
|
||||||
|
|
||||||
|
def load_exchange_rate() -> float:
|
||||||
|
"""
|
||||||
|
从exchange_rate.txt文件中读取汇率
|
||||||
|
如果文件不存在或值异常,则使用默认汇率
|
||||||
|
|
||||||
|
返回:
|
||||||
|
汇率值
|
||||||
|
"""
|
||||||
|
rate_file = 'exchange_rate.txt'
|
||||||
|
|
||||||
|
# 检查文件是否存在
|
||||||
|
if not os.path.exists(rate_file):
|
||||||
|
print(f"汇率文件 {rate_file} 不存在,使用默认汇率: {DEFAULT_EXCHANGE_RATE}")
|
||||||
|
return DEFAULT_EXCHANGE_RATE
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 读取文件内容
|
||||||
|
with open(rate_file, 'r', encoding='utf-8') as f:
|
||||||
|
content = f.read().strip()
|
||||||
|
|
||||||
|
# 尝试转换为浮点数
|
||||||
|
rate = float(content)
|
||||||
|
|
||||||
|
# 验证汇率是否合理 (假设汇率应该在 0.1 到 100 之间)
|
||||||
|
if rate <= 0 or rate > 100:
|
||||||
|
print(f"汇率文件中的值 {rate} 不合理,使用默认汇率: {DEFAULT_EXCHANGE_RATE}")
|
||||||
|
return DEFAULT_EXCHANGE_RATE
|
||||||
|
|
||||||
|
print(f"从 {rate_file} 读取汇率: {rate}")
|
||||||
|
return rate
|
||||||
|
|
||||||
|
except ValueError:
|
||||||
|
print(f"汇率文件 {rate_file} 中的值无法解析,使用默认汇率: {DEFAULT_EXCHANGE_RATE}")
|
||||||
|
return DEFAULT_EXCHANGE_RATE
|
||||||
|
except Exception as e:
|
||||||
|
print(f"读取汇率文件时发生错误: {e},使用默认汇率: {DEFAULT_EXCHANGE_RATE}")
|
||||||
|
return DEFAULT_EXCHANGE_RATE
|
||||||
|
|
||||||
|
|
||||||
|
def create_accounting_entries(data: List[Dict[str, Any]], exchange_rate: float) -> List[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
根据财务数据生成会计分录
|
||||||
|
|
||||||
|
参数:
|
||||||
|
data: res.json中的财务数据
|
||||||
|
exchange_rate: 汇率
|
||||||
|
|
||||||
|
返回:
|
||||||
|
会计分录列表
|
||||||
|
"""
|
||||||
|
entries = []
|
||||||
|
|
||||||
|
for record in data:
|
||||||
|
received_amount = record["ReceivedAmount"]
|
||||||
|
handling_fee = record["HandlingFee"]
|
||||||
|
orders = record["Order"]
|
||||||
|
check_res = record.get("checkRes", True) # 获取checkRes字段
|
||||||
|
|
||||||
|
# 跳过无效记录
|
||||||
|
if received_amount is None or not orders:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 1. ReceivedAmount 借方记录
|
||||||
|
# 科目代码: 1002.02, 科目名称: 银行存款 - 中行USD
|
||||||
|
for order in orders:
|
||||||
|
order_num = order["OrderNum"]
|
||||||
|
account_name = order["AccountName"]
|
||||||
|
|
||||||
|
entry_debit = {
|
||||||
|
"到账金额": received_amount,
|
||||||
|
"手续费": handling_fee,
|
||||||
|
"订单号": order_num,
|
||||||
|
"应收账款": "",
|
||||||
|
"金蝶名称": account_name,
|
||||||
|
"摘要": f"美金收款-{order_num}",
|
||||||
|
"借/贷": "借",
|
||||||
|
"科目代码(*)": "1002.02",
|
||||||
|
"科目名称(*)": "银行存款 - 中行USD",
|
||||||
|
"核算项目": account_name,
|
||||||
|
"币别": "美元",
|
||||||
|
"汇率": exchange_rate,
|
||||||
|
"原币金额": received_amount,
|
||||||
|
"金额": round(received_amount * exchange_rate, 2),
|
||||||
|
"_check_res": check_res # 添加checkRes标记
|
||||||
|
}
|
||||||
|
entries.append(entry_debit)
|
||||||
|
break # 只记录一次借方
|
||||||
|
|
||||||
|
# 2. 手续费借方记录 (如果手续费>0)
|
||||||
|
# 科目代码: 5603.03, 科目名称: 财务费用-手续费
|
||||||
|
if handling_fee > 0:
|
||||||
|
# 获取第一个订单号用于摘要
|
||||||
|
first_order_num = orders[0]["OrderNum"] if orders else ""
|
||||||
|
|
||||||
|
entry_fee = {
|
||||||
|
"到账金额": received_amount,
|
||||||
|
"手续费": handling_fee,
|
||||||
|
"订单号": first_order_num,
|
||||||
|
"应收账款": "",
|
||||||
|
"金蝶名称": "",
|
||||||
|
"摘要": f"美金收款-{first_order_num}",
|
||||||
|
"借/贷": "借",
|
||||||
|
"科目代码(*)": "5603.03",
|
||||||
|
"科目名称(*)": "财务费用-手续费",
|
||||||
|
"核算项目": "",
|
||||||
|
"币别": "人民币",
|
||||||
|
"汇率": "",
|
||||||
|
"原币金额": "",
|
||||||
|
"金额": round(handling_fee * exchange_rate, 2),
|
||||||
|
"_check_res": check_res # 添加checkRes标记
|
||||||
|
}
|
||||||
|
entries.append(entry_fee)
|
||||||
|
|
||||||
|
# 3. Order列表中每一项的贷方记录
|
||||||
|
# 科目代码: 1122, 科目名称: 应收账款
|
||||||
|
for order in orders:
|
||||||
|
order_num = order["OrderNum"]
|
||||||
|
amount = order["Amount"]
|
||||||
|
account_name = order["AccountName"]
|
||||||
|
|
||||||
|
# 跳过金额为空的订单
|
||||||
|
if amount is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
entry_order = {
|
||||||
|
"到账金额": received_amount,
|
||||||
|
"手续费": handling_fee,
|
||||||
|
"订单号": order_num,
|
||||||
|
"应收账款": amount, # 填入Order的Amount金额
|
||||||
|
"金蝶名称": account_name,
|
||||||
|
"摘要": f"美金收款-{order_num}",
|
||||||
|
"借/贷": "贷",
|
||||||
|
"科目代码(*)": "1122",
|
||||||
|
"科目名称(*)": "应收账款",
|
||||||
|
"核算项目": account_name,
|
||||||
|
"币别": "美元",
|
||||||
|
"汇率": exchange_rate,
|
||||||
|
"原币金额": amount,
|
||||||
|
"金额": round(amount * exchange_rate, 2),
|
||||||
|
"_check_res": check_res # 添加checkRes标记
|
||||||
|
}
|
||||||
|
entries.append(entry_order)
|
||||||
|
|
||||||
|
return entries
|
||||||
|
|
||||||
|
|
||||||
|
def save_to_excel(entries: List[Dict[str, Any]], output_file: str):
|
||||||
|
"""
|
||||||
|
将会计分录保存为Excel文件
|
||||||
|
|
||||||
|
参数:
|
||||||
|
entries: 会计分录列表
|
||||||
|
output_file: 输出文件路径
|
||||||
|
"""
|
||||||
|
from openpyxl.utils import get_column_letter
|
||||||
|
|
||||||
|
wb = Workbook()
|
||||||
|
ws = wb.active
|
||||||
|
ws.title = "会计分录"
|
||||||
|
|
||||||
|
# 定义表头
|
||||||
|
headers = [
|
||||||
|
"到账金额", "手续费", "订单号", "应收账款", "金蝶名称",
|
||||||
|
"摘要", "借/贷", "科目代码(*)", "科目名称(*)",
|
||||||
|
"核算项目", "币别", "汇率", "原币金额", "金额"
|
||||||
|
]
|
||||||
|
|
||||||
|
# 写入表头
|
||||||
|
for col_idx, header in enumerate(headers, start=1):
|
||||||
|
cell = ws.cell(row=1, column=col_idx, value=header)
|
||||||
|
cell.font = Font(bold=True)
|
||||||
|
cell.fill = PatternFill(start_color="CCE5FF", end_color="CCE5FF", fill_type="solid")
|
||||||
|
cell.alignment = Alignment(horizontal="center", vertical="center")
|
||||||
|
|
||||||
|
# 写入数据
|
||||||
|
error_fill = PatternFill(start_color="FAD1D4", end_color="FAD1D4", fill_type="solid")
|
||||||
|
|
||||||
|
for row_idx, entry in enumerate(entries, start=2):
|
||||||
|
check_res = entry.get("_check_res", True)
|
||||||
|
|
||||||
|
# 写入数据
|
||||||
|
ws.cell(row=row_idx, column=1, value=entry.get("到账金额", ""))
|
||||||
|
ws.cell(row=row_idx, column=2, value=entry.get("手续费", ""))
|
||||||
|
ws.cell(row=row_idx, column=3, value=entry.get("订单号", ""))
|
||||||
|
ws.cell(row=row_idx, column=4, value=entry.get("应收账款", ""))
|
||||||
|
ws.cell(row=row_idx, column=5, value=entry.get("金蝶名称", ""))
|
||||||
|
ws.cell(row=row_idx, column=6, value=entry.get("摘要", ""))
|
||||||
|
ws.cell(row=row_idx, column=7, value=entry.get("借/贷", ""))
|
||||||
|
ws.cell(row=row_idx, column=8, value=entry.get("科目代码(*)", ""))
|
||||||
|
ws.cell(row=row_idx, column=9, value=entry.get("科目名称(*)", ""))
|
||||||
|
ws.cell(row=row_idx, column=10, value=entry.get("核算项目", ""))
|
||||||
|
ws.cell(row=row_idx, column=11, value=entry.get("币别", ""))
|
||||||
|
ws.cell(row=row_idx, column=12, value=entry.get("汇率", ""))
|
||||||
|
ws.cell(row=row_idx, column=13, value=entry.get("原币金额", ""))
|
||||||
|
ws.cell(row=row_idx, column=14, value=entry.get("金额", ""))
|
||||||
|
|
||||||
|
# 先设置所有背景颜色(在合并单元格之前)
|
||||||
|
for row_idx, entry in enumerate(entries, start=2):
|
||||||
|
check_res = entry.get("_check_res", True)
|
||||||
|
if not check_res:
|
||||||
|
for col_idx in range(1, 15):
|
||||||
|
ws.cell(row=row_idx, column=col_idx).fill = error_fill
|
||||||
|
|
||||||
|
# 合并同一ReceivedAmount的"到账金额"和"手续费"单元格
|
||||||
|
merge_groups = {} # {(received_amount, handling_fee): [row_start, row_end]}
|
||||||
|
|
||||||
|
for row_idx, entry in enumerate(entries, start=2):
|
||||||
|
received_amount = entry.get("到账金额", "")
|
||||||
|
handling_fee = entry.get("手续费", "")
|
||||||
|
key = (received_amount, handling_fee)
|
||||||
|
|
||||||
|
if key not in merge_groups:
|
||||||
|
merge_groups[key] = [row_idx, row_idx]
|
||||||
|
else:
|
||||||
|
# 检查是否连续
|
||||||
|
if row_idx == merge_groups[key][1] + 1:
|
||||||
|
merge_groups[key][1] = row_idx
|
||||||
|
else:
|
||||||
|
# 不连续,创建新组
|
||||||
|
merge_groups[f"{key}_{row_idx}"] = [row_idx, row_idx]
|
||||||
|
|
||||||
|
# 执行合并
|
||||||
|
for key, (start_row, end_row) in merge_groups.items():
|
||||||
|
if start_row < end_row: # 只有多于1行时才合并
|
||||||
|
# 合并"到账金额"列(A列)
|
||||||
|
ws.merge_cells(f'A{start_row}:A{end_row}')
|
||||||
|
ws.cell(start_row, 1).alignment = Alignment(horizontal="center", vertical="center")
|
||||||
|
|
||||||
|
# 合并"手续费"列(B列)
|
||||||
|
ws.merge_cells(f'B{start_row}:B{end_row}')
|
||||||
|
ws.cell(start_row, 2).alignment = Alignment(horizontal="center", vertical="center")
|
||||||
|
|
||||||
|
# 合并后重新应用背景颜色(确保合并单元格也有背景色)
|
||||||
|
for row_idx, entry in enumerate(entries, start=2):
|
||||||
|
check_res = entry.get("_check_res", True)
|
||||||
|
if not check_res:
|
||||||
|
for col_idx in range(1, 15):
|
||||||
|
ws.cell(row=row_idx, column=col_idx).fill = error_fill
|
||||||
|
|
||||||
|
# 调整列宽
|
||||||
|
column_widths = [12, 10, 18, 12, 25, 25, 8, 15, 25, 25, 10, 10, 12, 15]
|
||||||
|
for col_idx, width in enumerate(column_widths, start=1):
|
||||||
|
ws.column_dimensions[chr(64 + col_idx)].width = width
|
||||||
|
|
||||||
|
# 保存文件
|
||||||
|
wb.save(output_file)
|
||||||
|
print(f"\n会计分录已保存到: {output_file}")
|
||||||
|
print(f"总共生成 {len(entries)} 条会计分录")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""主函数"""
|
||||||
|
input_file = 'res.json'
|
||||||
|
output_file = 'AccountingEntries.xlsx'
|
||||||
|
|
||||||
|
print("开始生成会计分录...")
|
||||||
|
print(f"读取文件: {input_file}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 加载汇率
|
||||||
|
exchange_rate = load_exchange_rate()
|
||||||
|
|
||||||
|
# 读取JSON数据
|
||||||
|
with open(input_file, 'r', encoding='utf-8') as f:
|
||||||
|
data = json.load(f)
|
||||||
|
|
||||||
|
print(f"读取了 {len(data)} 条财务记录")
|
||||||
|
print(f"使用汇率: {exchange_rate}")
|
||||||
|
|
||||||
|
# 生成会计分录
|
||||||
|
entries = create_accounting_entries(data, exchange_rate)
|
||||||
|
|
||||||
|
# 保存到Excel
|
||||||
|
save_to_excel(entries, output_file)
|
||||||
|
|
||||||
|
# 统计信息
|
||||||
|
debit_count = sum(1 for e in entries if e["借/贷"] == "借")
|
||||||
|
credit_count = sum(1 for e in entries if e["借/贷"] == "贷")
|
||||||
|
|
||||||
|
print(f"\n统计:")
|
||||||
|
print(f" 借方记录: {debit_count} 条")
|
||||||
|
print(f" 贷方记录: {credit_count} 条")
|
||||||
|
|
||||||
|
print("\n处理完成!")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n错误: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
214
process_excel.py
Normal file
214
process_excel.py
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
财务Excel数据处理程序
|
||||||
|
读取data/data.xlsx中的Sheet1表格,提取财务数据并输出到res.json
|
||||||
|
"""
|
||||||
|
|
||||||
|
import json
|
||||||
|
from openpyxl import load_workbook
|
||||||
|
from typing import List, Dict, Any, Optional
|
||||||
|
|
||||||
|
|
||||||
|
def get_cell_value(ws, row: int, col: int) -> Any:
|
||||||
|
"""获取单元格值,处理公式计算结果"""
|
||||||
|
cell = ws.cell(row, col)
|
||||||
|
return cell.value
|
||||||
|
|
||||||
|
|
||||||
|
def get_merged_cell_value(ws, row: int, col: int, merged_ranges) -> Any:
|
||||||
|
"""
|
||||||
|
获取合并单元格的值
|
||||||
|
如果单元格在合并区域内,返回合并区域左上角单元格的值
|
||||||
|
"""
|
||||||
|
for merged_range in merged_ranges:
|
||||||
|
if merged_range.min_row <= row <= merged_range.max_row and \
|
||||||
|
merged_range.min_col <= col <= merged_range.max_col:
|
||||||
|
# 返回合并区域左上角的值
|
||||||
|
return ws.cell(merged_range.min_row, merged_range.min_col).value
|
||||||
|
|
||||||
|
# 不在任何合并区域内,直接返回单元格值
|
||||||
|
return ws.cell(row, col).value
|
||||||
|
|
||||||
|
|
||||||
|
def get_f_column_ranges(ws, start_row: int = 2) -> List[tuple]:
|
||||||
|
"""
|
||||||
|
获取F列的所有数据区域(包括合并和非合并单元格)
|
||||||
|
从第2行开始读取
|
||||||
|
返回: [(起始行, 结束行), ...]
|
||||||
|
"""
|
||||||
|
merged_cells = list(ws.merged_cells.ranges)
|
||||||
|
|
||||||
|
# 找到F列的所有合并单元格区域
|
||||||
|
f_merges = []
|
||||||
|
for merge in merged_cells:
|
||||||
|
# F列是第6列
|
||||||
|
if merge.min_col == 6 and merge.max_col == 6 and merge.min_row >= start_row:
|
||||||
|
f_merges.append((merge.min_row, merge.max_row))
|
||||||
|
|
||||||
|
# 创建合并单元格行的集合,用于快速查找
|
||||||
|
merged_rows = set()
|
||||||
|
for start, end in f_merges:
|
||||||
|
for row in range(start, end + 1):
|
||||||
|
merged_rows.add(row)
|
||||||
|
|
||||||
|
# 处理非合并单元格(从第2行开始到最大行)
|
||||||
|
all_ranges = []
|
||||||
|
for row in range(start_row, ws.max_row + 1):
|
||||||
|
if row not in merged_rows:
|
||||||
|
# 检查F列是否有值
|
||||||
|
f_value = ws.cell(row, 6).value
|
||||||
|
if f_value is not None:
|
||||||
|
# 非合并单元格,单独一行
|
||||||
|
all_ranges.append((row, row))
|
||||||
|
|
||||||
|
# 添加合并单元格区域
|
||||||
|
all_ranges.extend(f_merges)
|
||||||
|
|
||||||
|
# 按起始行排序
|
||||||
|
all_ranges.sort(key=lambda x: x[0])
|
||||||
|
return all_ranges
|
||||||
|
|
||||||
|
|
||||||
|
def extract_orders(ws, start_row: int, end_row: int, merged_ranges) -> List[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
提取指定行范围内的订单数据
|
||||||
|
|
||||||
|
参数:
|
||||||
|
ws: 工作表对象
|
||||||
|
start_row: 起始行
|
||||||
|
end_row: 结束行
|
||||||
|
merged_ranges: 合并单元格范围列表
|
||||||
|
|
||||||
|
返回:
|
||||||
|
订单列表
|
||||||
|
"""
|
||||||
|
orders = []
|
||||||
|
|
||||||
|
for row in range(start_row, end_row + 1):
|
||||||
|
# H列: 订单号, I列: 金额, O列: 账户名
|
||||||
|
order_num = get_merged_cell_value(ws, row, 8, merged_ranges) # H列
|
||||||
|
amount = get_merged_cell_value(ws, row, 9, merged_ranges) # I列
|
||||||
|
account_name = get_merged_cell_value(ws, row, 15, merged_ranges) # O列
|
||||||
|
|
||||||
|
# 跳过空订单号的行
|
||||||
|
if order_num is None or str(order_num).strip() == '':
|
||||||
|
continue
|
||||||
|
|
||||||
|
order = {
|
||||||
|
"OrderNum": str(order_num).strip() if order_num else None,
|
||||||
|
"Amount": amount,
|
||||||
|
"AccountName": str(account_name).strip() if account_name else None
|
||||||
|
}
|
||||||
|
orders.append(order)
|
||||||
|
|
||||||
|
return orders
|
||||||
|
|
||||||
|
|
||||||
|
def process_excel_data(file_path: str) -> List[Dict[str, Any]]:
|
||||||
|
"""
|
||||||
|
处理Excel文件,提取所有财务记录
|
||||||
|
从第2行开始读取数据
|
||||||
|
|
||||||
|
参数:
|
||||||
|
file_path: Excel文件路径
|
||||||
|
|
||||||
|
返回:
|
||||||
|
记录列表,每条记录包含ReceivedAmount, HandlingFee和Order列表
|
||||||
|
"""
|
||||||
|
# 加载Excel文件,data_only=True读取公式计算结果
|
||||||
|
wb = load_workbook(file_path, data_only=True)
|
||||||
|
ws = wb['Sheet1']
|
||||||
|
|
||||||
|
# 获取所有合并单元格范围
|
||||||
|
merged_ranges = list(ws.merged_cells.ranges)
|
||||||
|
|
||||||
|
# 获取F列的所有数据区域(从第2行开始)
|
||||||
|
f_ranges = get_f_column_ranges(ws, start_row=2)
|
||||||
|
|
||||||
|
print(f"找到 {len(f_ranges)} 个F列数据区域(包含合并和非合并单元格)")
|
||||||
|
|
||||||
|
results = []
|
||||||
|
|
||||||
|
for start_row, end_row in f_ranges:
|
||||||
|
# 获取F列(实收金额)和G列(手续费)的值
|
||||||
|
# 合并单元格的值在左上角,非合并单元格直接获取
|
||||||
|
received_amount = get_cell_value(ws, start_row, 6) # F列
|
||||||
|
handling_fee = get_cell_value(ws, start_row, 7) # G列
|
||||||
|
|
||||||
|
# 手续费为空时记为0
|
||||||
|
if handling_fee is None:
|
||||||
|
handling_fee = 0
|
||||||
|
|
||||||
|
# 提取该区域的订单列表
|
||||||
|
orders = extract_orders(ws, start_row, end_row, merged_ranges)
|
||||||
|
|
||||||
|
# 跳过没有订单的记录
|
||||||
|
if not orders:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 计算订单金额总和
|
||||||
|
order_amount_sum = sum(order["Amount"] for order in orders if order["Amount"] is not None)
|
||||||
|
|
||||||
|
# 计算实收金额+手续费
|
||||||
|
received_plus_fee = (received_amount if received_amount is not None else 0) + handling_fee
|
||||||
|
|
||||||
|
# 检查是否相等(考虑浮点数精度)
|
||||||
|
check_res = abs(received_plus_fee - order_amount_sum) < 0.01
|
||||||
|
|
||||||
|
record = {
|
||||||
|
"ReceivedAmount": received_amount,
|
||||||
|
"HandlingFee": handling_fee,
|
||||||
|
"Order": orders,
|
||||||
|
"checkRes": check_res
|
||||||
|
}
|
||||||
|
|
||||||
|
results.append(record)
|
||||||
|
|
||||||
|
return results
|
||||||
|
|
||||||
|
|
||||||
|
def save_to_json(data: List[Dict[str, Any]], output_file: str):
|
||||||
|
"""
|
||||||
|
将数据保存为JSON文件
|
||||||
|
|
||||||
|
参数:
|
||||||
|
data: 要保存的数据
|
||||||
|
output_file: 输出文件路径
|
||||||
|
"""
|
||||||
|
with open(output_file, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(data, f, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
|
print(f"\n数据已保存到: {output_file}")
|
||||||
|
print(f"总共提取 {len(data)} 条记录")
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""主函数"""
|
||||||
|
input_file = 'data/data.xlsx'
|
||||||
|
output_file = 'res.json'
|
||||||
|
|
||||||
|
print("开始处理Excel文件...")
|
||||||
|
print(f"输入文件: {input_file}")
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 提取数据
|
||||||
|
data = process_excel_data(input_file)
|
||||||
|
|
||||||
|
# 保存到JSON
|
||||||
|
save_to_json(data, output_file)
|
||||||
|
|
||||||
|
# 显示前3条记录作为预览
|
||||||
|
if data:
|
||||||
|
print("\n前3条记录预览:")
|
||||||
|
print(json.dumps(data[:3], ensure_ascii=False, indent=2))
|
||||||
|
|
||||||
|
print("\n处理完成!")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print(f"\n错误: {e}")
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
355
task.md
Normal file
355
task.md
Normal file
@@ -0,0 +1,355 @@
|
|||||||
|
# 财务Excel数据处理系统 - 需求文档
|
||||||
|
|
||||||
|
## 项目概述
|
||||||
|
|
||||||
|
本系统用于自动化处理财务Excel数据,提取收款信息并生成标准会计分录表,支持数据验证和异常标记功能。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 功能模块
|
||||||
|
|
||||||
|
### 模块一: Excel数据提取 (`process_excel.py`)
|
||||||
|
|
||||||
|
#### 1.1 数据源
|
||||||
|
- **文件路径**: `data/data.xlsx`
|
||||||
|
- **工作表**: Sheet1
|
||||||
|
- **数据起始行**: 第2行(第1行为表头)
|
||||||
|
|
||||||
|
#### 1.2 数据提取规则
|
||||||
|
|
||||||
|
##### 1.2.1 主记录字段
|
||||||
|
- **ReceivedAmount** (实收金额)
|
||||||
|
- 来源: F列
|
||||||
|
- 说明: 支持合并单元格(一行或多行)
|
||||||
|
- 处理: 非合并单元格按单行处理
|
||||||
|
|
||||||
|
- **HandlingFee** (手续费)
|
||||||
|
- 来源: G列
|
||||||
|
- 处理: 空值自动记为0
|
||||||
|
|
||||||
|
##### 1.2.2 订单明细字段
|
||||||
|
- **Order** (订单列表)
|
||||||
|
- 范围: F列合并单元格包含的所有行
|
||||||
|
- 包含字段:
|
||||||
|
- **OrderNum** (订单号): H列
|
||||||
|
- **Amount** (金额): I列
|
||||||
|
- **AccountName** (账户名称): O列(支持公式,读取计算结果)
|
||||||
|
|
||||||
|
##### 1.2.3 数据验证
|
||||||
|
- **checkRes** (验证结果): Boolean
|
||||||
|
- 计算规则: `ReceivedAmount + HandlingFee ≈ Sum(Order[].Amount)`
|
||||||
|
- 容差: 0.01
|
||||||
|
- true: 金额匹配
|
||||||
|
- false: 金额不匹配,需要人工核对
|
||||||
|
|
||||||
|
#### 1.3 输出格式
|
||||||
|
|
||||||
|
**文件**: `res.json`
|
||||||
|
|
||||||
|
**结构**:
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"ReceivedAmount": 12125,
|
||||||
|
"HandlingFee": 25,
|
||||||
|
"Order": [
|
||||||
|
{
|
||||||
|
"OrderNum": "XLRQD300T25",
|
||||||
|
"Amount": 550,
|
||||||
|
"AccountName": "24台湾长荣航运"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"OrderNum": "XLRQD044T25",
|
||||||
|
"Amount": 11600,
|
||||||
|
"AccountName": "24台湾长荣航运"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"checkRes": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 1.4 特殊处理
|
||||||
|
- 跳过订单号为空的行
|
||||||
|
- 处理F列合并和非合并单元格混合的情况
|
||||||
|
- 读取O列公式计算后的值(data_only=True)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 模块二: 会计分录生成 (`generate_accounting_entries.py`)
|
||||||
|
|
||||||
|
#### 2.1 输入输出
|
||||||
|
- **输入**: `res.json`
|
||||||
|
- **输出**: `AccountingEntries.xlsx`
|
||||||
|
|
||||||
|
#### 2.2 会计分录规则
|
||||||
|
|
||||||
|
##### 2.2.1 基本分录
|
||||||
|
|
||||||
|
每笔 ReceivedAmount 记录生成以下分录:
|
||||||
|
|
||||||
|
**1) 到账金额 - 借方记录** (每笔记录1条)
|
||||||
|
- 科目代码: `1002.02`
|
||||||
|
- 科目名称: `银行存款 - 中行USD`
|
||||||
|
- 摘要: `美金收款-{OrderNum}`
|
||||||
|
- 核算项目: Order[0].AccountName
|
||||||
|
- 币别: 美元
|
||||||
|
- 汇率: 7.1072 (可配置)
|
||||||
|
- 原币金额: ReceivedAmount
|
||||||
|
- 金额: ReceivedAmount × 汇率
|
||||||
|
|
||||||
|
**2) 手续费 - 借方记录** (仅当 HandlingFee > 0)
|
||||||
|
- 科目代码: `5603.03`
|
||||||
|
- 科目名称: `财务费用-手续费`
|
||||||
|
- 摘要: `美金收款-{OrderNum}`
|
||||||
|
- 核算项目: 空
|
||||||
|
- 币别: 人民币
|
||||||
|
- 汇率: 空
|
||||||
|
- 原币金额: 空
|
||||||
|
- 金额: HandlingFee × 汇率
|
||||||
|
|
||||||
|
**3) 订单明细 - 贷方记录** (每个Order记录1条)
|
||||||
|
- 科目代码: `1122`
|
||||||
|
- 科目名称: `应收账款`
|
||||||
|
- 应收账款: Order.Amount (显示在"应收账款"列)
|
||||||
|
- 摘要: `美金收款-{OrderNum}`
|
||||||
|
- 核算项目: Order.AccountName
|
||||||
|
- 币别: 美元
|
||||||
|
- 汇率: 7.1072 (可配置)
|
||||||
|
- 原币金额: Order.Amount
|
||||||
|
- 金额: Order.Amount × 汇率
|
||||||
|
|
||||||
|
##### 2.2.2 特殊规则
|
||||||
|
|
||||||
|
**空值处理**
|
||||||
|
- Order.Amount为null的订单跳过,不生成分录
|
||||||
|
|
||||||
|
**金额验证**
|
||||||
|
- checkRes为false的记录,所有相关分录行标记为粉红色背景(#FAD1D4)
|
||||||
|
|
||||||
|
#### 2.3 Excel格式设置
|
||||||
|
|
||||||
|
##### 2.3.1 表头
|
||||||
|
- 字段: 到账金额, 手续费, 订单号, 应收账款, 金蝶名称, 摘要, 借/贷, 科目代码(*), 科目名称(*), 核算项目, 币别, 汇率, 原币金额, 金额
|
||||||
|
- 样式: 粗体, 蓝色背景(#CCE5FF), 居中对齐
|
||||||
|
|
||||||
|
##### 2.3.2 单元格合并
|
||||||
|
- **到账金额列** (A列): 同一ReceivedAmount的所有分录行合并
|
||||||
|
- **手续费列** (B列): 同一ReceivedAmount的所有分录行合并
|
||||||
|
- 对齐方式: 垂直居中,水平居中
|
||||||
|
|
||||||
|
##### 2.3.3 异常标记
|
||||||
|
- 条件: 原始记录的 checkRes = false
|
||||||
|
- 处理: 该记录产生的所有分录行设置背景色
|
||||||
|
- 颜色: #FAD1D4 (粉红色)
|
||||||
|
- 目的: 突出显示金额不匹配的记录,便于人工核对
|
||||||
|
|
||||||
|
##### 2.3.4 列宽设置
|
||||||
|
```
|
||||||
|
A(到账金额): 12, B(手续费): 10, C(订单号): 18,
|
||||||
|
D(应收账款): 12, E(金蝶名称): 25, F(摘要): 25,
|
||||||
|
G(借/贷): 8, H(科目代码): 15, I(科目名称): 25,
|
||||||
|
J(核算项目): 25, K(币别): 10, L(汇率): 10,
|
||||||
|
M(原币金额): 12, N(金额): 15
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 配置参数
|
||||||
|
|
||||||
|
### 汇率配置
|
||||||
|
- **变量名**: `EXCHANGE_RATE`
|
||||||
|
- **位置**: `generate_accounting_entries.py` 第12行
|
||||||
|
- **默认值**: 7.1072
|
||||||
|
- **修改方式**: 直接编辑变量值
|
||||||
|
|
||||||
|
```python
|
||||||
|
# 固定汇率
|
||||||
|
EXCHANGE_RATE = 7.1072 # 修改此值
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 数据流程
|
||||||
|
|
||||||
|
```
|
||||||
|
data/data.xlsx (原始数据)
|
||||||
|
↓
|
||||||
|
[process_excel.py 提取]
|
||||||
|
↓
|
||||||
|
res.json (中间数据)
|
||||||
|
↓
|
||||||
|
[generate_accounting_entries.py 生成]
|
||||||
|
↓
|
||||||
|
AccountingEntries.xlsx (会计分录表)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 技术要求
|
||||||
|
|
||||||
|
### 开发语言
|
||||||
|
- Python 3.x
|
||||||
|
|
||||||
|
### 依赖库
|
||||||
|
- openpyxl: Excel文件读写
|
||||||
|
|
||||||
|
### 安装命令
|
||||||
|
```bash
|
||||||
|
pip install openpyxl --break-system-packages
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 数据示例
|
||||||
|
|
||||||
|
### 输入示例 (data.xlsx)
|
||||||
|
|
||||||
|
| 到账金额 | 手续费 | 订单号 | 解款金额 | 金蝶名称 |
|
||||||
|
|---------|--------|---------|---------|---------|
|
||||||
|
| 12125 | 25 | XLRQD300T25 | 550 | 24台湾长荣航运 |
|
||||||
|
| (合并) | (合并) | XLRQD044T25 | 11600 | 24台湾长荣航运 |
|
||||||
|
|
||||||
|
### 中间数据 (res.json)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"ReceivedAmount": 12125,
|
||||||
|
"HandlingFee": 25,
|
||||||
|
"Order": [
|
||||||
|
{"OrderNum": "XLRQD300T25", "Amount": 550, "AccountName": "24台湾长荣航运"},
|
||||||
|
{"OrderNum": "XLRQD044T25", "Amount": 11600, "AccountName": "24台湾长荣航运"}
|
||||||
|
],
|
||||||
|
"checkRes": true
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 输出示例 (AccountingEntries.xlsx)
|
||||||
|
|
||||||
|
| 到账金额 | 手续费 | 订单号 | 应收账款 | 金蝶名称 | 借/贷 | 科目代码 | 科目名称 | 币别 | 原币金额 | 金额 |
|
||||||
|
|---------|--------|--------|---------|---------|-------|----------|----------|------|----------|------|
|
||||||
|
| 12125 | 25 | XLRQD300T25 | | 24台湾长荣航运 | 借 | 1002.02 | 银行存款 - 中行USD | 美元 | 12125 | 86,174.80 |
|
||||||
|
| (合并) | (合并) | XLRQD300T25 | | | 借 | 5603.03 | 财务费用-手续费 | 人民币 | | 177.68 |
|
||||||
|
| (合并) | (合并) | XLRQD300T25 | 550 | 24台湾长荣航运 | 贷 | 1122 | 应收账款 | 美元 | 550 | 3,908.96 |
|
||||||
|
| (合并) | (合并) | XLRQD044T25 | 11600 | 24台湾长荣航运 | 贷 | 1122 | 应收账款 | 美元 | 11600 | 82,443.52 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 验证规则
|
||||||
|
|
||||||
|
### checkRes计算
|
||||||
|
```python
|
||||||
|
received_plus_fee = ReceivedAmount + HandlingFee
|
||||||
|
order_amount_sum = Sum(Order[].Amount where Amount is not null)
|
||||||
|
checkRes = abs(received_plus_fee - order_amount_sum) < 0.01
|
||||||
|
```
|
||||||
|
|
||||||
|
### 异常情况
|
||||||
|
- **checkRes = false**: 粉红色背景标记
|
||||||
|
- **Order.Amount = null**: 跳过该订单,不生成分录
|
||||||
|
- **HandlingFee = null**: 自动记为0
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 测试用例
|
||||||
|
|
||||||
|
### 测试用例1: 正常单订单
|
||||||
|
- 输入: ReceivedAmount=695, HandlingFee=0, Order[0].Amount=695
|
||||||
|
- 预期: 2行分录(到账借+订单贷), checkRes=true
|
||||||
|
- 应收账款列: 贷方记录显示695
|
||||||
|
|
||||||
|
### 测试用例2: 正常多订单
|
||||||
|
- 输入: ReceivedAmount=12125, HandlingFee=25, Order=[550, 11600]
|
||||||
|
- 预期: 4行分录(到账借+手续费借+2个订单贷), checkRes=true
|
||||||
|
- 应收账款列: 两条贷方记录分别显示550和11600
|
||||||
|
|
||||||
|
### 测试用例3: 金额不匹配
|
||||||
|
- 输入: ReceivedAmount=17270, HandlingFee=0, Order=[5676, 11450]
|
||||||
|
- 预期: checkRes=false, 所有分录行粉红色背景(#FAD1D4)
|
||||||
|
|
||||||
|
### 测试用例4: 订单金额为空
|
||||||
|
- 输入: ReceivedAmount=240, HandlingFee=25, Order[0].Amount=null
|
||||||
|
- 预期: 跳过订单明细分录,不报错,只生成到账借方和手续费借方
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 文件清单
|
||||||
|
|
||||||
|
| 文件名 | 类型 | 说明 |
|
||||||
|
|--------|------|------|
|
||||||
|
| data/data.xlsx | 输入 | 原始财务数据 |
|
||||||
|
| process_excel.py | 程序 | 数据提取脚本 |
|
||||||
|
| res.json | 中间 | 提取的JSON数据 |
|
||||||
|
| generate_accounting_entries.py | 程序 | 会计分录生成脚本 |
|
||||||
|
| AccountingEntries.xlsx | 输出 | 会计分录表 |
|
||||||
|
| analyze_excel.py | 工具 | Excel结构分析工具 |
|
||||||
|
| task.md | 文档 | 需求文档(本文件) |
|
||||||
|
| User.md | 文档 | 用户使用说明 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 性能要求
|
||||||
|
|
||||||
|
- 支持处理300+行Excel数据
|
||||||
|
- 生成500+行会计分录
|
||||||
|
- 处理时间 < 10秒
|
||||||
|
- 实测: 174条记录生成566条会计分录
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 错误处理
|
||||||
|
|
||||||
|
### Excel读取错误
|
||||||
|
- 文件不存在: 提示并退出
|
||||||
|
- Sheet1不存在: 提示并退出
|
||||||
|
- 列映射错误: 记录日志,跳过该行
|
||||||
|
|
||||||
|
### 数据验证错误
|
||||||
|
- 空值: 自动处理(HandlingFee=0, 跳过Amount=null)
|
||||||
|
- 格式错误: 记录日志,继续处理
|
||||||
|
- checkRes=false: 标记但继续处理
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 扩展需求
|
||||||
|
|
||||||
|
### 未来可能增加的功能
|
||||||
|
- [ ] 支持多工作表批量处理
|
||||||
|
- [ ] 汇率从配置文件读取
|
||||||
|
- [ ] 生成汇总统计报表
|
||||||
|
- [ ] 支持导出金蝶格式
|
||||||
|
- [ ] 添加数据修正功能
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 版本信息
|
||||||
|
|
||||||
|
- **版本**: v1.1
|
||||||
|
- **最后更新**: 2025-01-17
|
||||||
|
- **作者**: Claude Code
|
||||||
|
- **状态**: 已完成并测试
|
||||||
|
|
||||||
|
### 版本历史
|
||||||
|
|
||||||
|
**v1.1** (2025-01-17)
|
||||||
|
- 优化会计分录规则: 移除"到账金额-贷方"记录
|
||||||
|
- 每个Order记录都生成对应的贷方分录(无例外)
|
||||||
|
- Order生成的贷方记录在"应收账款"列显示Amount金额
|
||||||
|
- 简化单订单处理逻辑,移除单订单优化规则
|
||||||
|
|
||||||
|
**v1.0** (2025-01-17)
|
||||||
|
- 初始版本
|
||||||
|
- Excel数据提取功能
|
||||||
|
- 会计分录生成功能
|
||||||
|
- 合并单元格支持
|
||||||
|
- 金额验证功能
|
||||||
|
- 异常背景色标记
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 备注
|
||||||
|
|
||||||
|
1. 所有金额计算保留2位小数
|
||||||
|
2. 汇率统一使用固定值,不从Excel读取
|
||||||
|
3. 合并单元格的值读取左上角单元格
|
||||||
|
4. O列公式使用data_only=True读取计算结果
|
||||||
|
5. 背景色仅用于标记,不影响数据内容
|
||||||
5
venv/pyvenv.cfg
Normal file
5
venv/pyvenv.cfg
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
home = /usr/bin
|
||||||
|
include-system-site-packages = false
|
||||||
|
version = 3.12.3
|
||||||
|
executable = /usr/bin/python3.12
|
||||||
|
command = /usr/bin/python3 -m venv /mnt/d/Cursor/TableProcessing/venv
|
||||||
Reference in New Issue
Block a user