feat: 网银流水导入模板+现金流联动(财务数据通道P1)
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
# 财务数据通道方案:网银流水标准导入模板(P1)
|
||||
|
||||
- 提出:研学调度中枢(反向上报触发,2026-08-28)
|
||||
- 执行:项目Bot(方案/验收/协调)→ 全栈Bot(开发)
|
||||
- 背景:酣客现金流2.2万 vs 短债350万 = 全年最大风险,现金流监控空窗(不直连,靠手工)
|
||||
|
||||
## 一、ERP连通可行性结论(已完成摸底,2026-08-28)
|
||||
|
||||
| 检查项 | 结果 |
|
||||
|--------|------|
|
||||
| ERP网关容器 | 活着(erp-gateway Up 4 days,127.0.0.1:8300) |
|
||||
| ERP真实数据库 | **不可达**:SQL Server 211.149.143.215 连接超时(pymssql OperationalError 20009) |
|
||||
| ERP API 端点 | /stats/monthly、/cashflow 等全部 000/500 |
|
||||
| erp_sync cron | 2026-08-19 已 PAUSED(#PAUSED-20260819) |
|
||||
| kpi_values erp源数据 | 0 条(从未同步成功) |
|
||||
| data_source_config | 12条ERP源配置存在但全部空转 |
|
||||
|
||||
**结论:ERP连通短期无望(真实ERP库在外部网络不可达),走方案②网银流水标准导入模板。**
|
||||
|
||||
## 二、现状盘点(基础设施大部分已就绪)
|
||||
|
||||
| 已有资产 | 状态 |
|
||||
|----------|------|
|
||||
| voucher_details 表(凭证明细) | ✅ 已建,0行。字段:voucher_no/voucher_date/subject_code/subject_name/debit_amount/credit_amount/summary/new_standard_category/period |
|
||||
| import_logs 表(导入日志) | ✅ 已建,0行。字段:filename/batch/total_rows/success_rows/failed_rows/errors/period/import_type/created_by |
|
||||
| VoucherDetail 模型 | ✅ 已注册(app/models/__init__.py:614) |
|
||||
| /api/cma/cash/balance | ✅ 现金余额(手工基线) |
|
||||
| /api/cma/cash/dashboard | ✅ 现金流看板 |
|
||||
| /api/cma/cash/gap-forecast | ✅ 缺口预测 |
|
||||
| /api/cma/cash/plans | ✅ 收付款计划 |
|
||||
| DataManage.vue Excel导入tab | ✅ 已有(import-excel-smart,KPI导入) |
|
||||
| 现金流KPI(EXT_202-208各店现金等) | ✅ 存在,2026-06有数据(手工Excel导入) |
|
||||
|
||||
**缺口(本次开发内容)**:无凭证/网银流水导入API、无校验规则(借贷平衡/期间合计/结转行识别)、无前端流水导入界面、现金流余额不自动更新。
|
||||
|
||||
## 三、开发内容(全栈Bot执行)
|
||||
|
||||
### 3.1 导入模板定义(xlsx)
|
||||
|
||||
模板列(与 voucher_details 字段对齐):
|
||||
|
||||
| 列名 | 字段 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| 凭证日期 | voucher_date | ✅ | YYYY-MM-DD 或 日期格式 |
|
||||
| 凭证号 | voucher_no | ✅ | 字符串 |
|
||||
| 科目编码 | subject_code | ✅ | 如 1002(银行存款) |
|
||||
| 科目名称 | subject_name | ✅ | 如 银行存款-工行 |
|
||||
| 借方金额 | debit_amount | 二选一 | 无则0 |
|
||||
| 贷方金额 | credit_amount | 二选一 | 无则0 |
|
||||
| 摘要 | summary | 可选 | 结转行识别依据 |
|
||||
|
||||
生成模板文件:`backend/scripts/templates/网银流水导入模板.xlsx`(含表头+1行示例)。
|
||||
|
||||
### 3.2 新入库接口 POST /api/cma/cash/import/vouchers
|
||||
|
||||
入参:multipart file + entity_id(Depends get_entity_id)+ period(可选,默认从文件名/日期提取)
|
||||
|
||||
处理流程:
|
||||
1. 解析xlsx(openpyxl/pandas)
|
||||
2. 逐行校验:日期可解析、科目编码/名称非空、金额为数字且≥0、借贷不全为0
|
||||
3. 校验规则(核心):
|
||||
- **借贷平衡**:Σ借方 = Σ贷方(容差 0.01),不平衡返回错误+差额
|
||||
- **期间合计**:按 period 汇总借方/贷方合计(用于对账展示)
|
||||
- **结转行识别**:摘要含"结转"或科目名称含"本年利润/结转" → 标记 carry_forward=True,不参与现金流计算
|
||||
4. 写入 voucher_details(batch = 文件名_时间戳),period 从日期列提取
|
||||
5. 写 import_logs(total/success/failed/errors 明细)
|
||||
6. **现金流联动**:计算货币资金类科目(科目编码 1001/1002 开头)期末余额 → 调用 set_current_cash_balance → 同步更新 EXT_现金类KPI 实际值(写入 kpi_values,source_type=ledger)→ 看板可见
|
||||
|
||||
返回:{success, total, success_rows, failed_rows, errors[], 借贷平衡校验, 期间合计, 结转行数, 现金余额}
|
||||
|
||||
### 3.3 校验规则实现细节
|
||||
|
||||
- 借贷平衡容差:|Σ借-Σ贷| <= 0.01 通过
|
||||
- 期间合计:返回 {period: {debit_total, credit_total}} 供对账
|
||||
- 结转行识别:summary LIKE '%结转%' OR subject_name LIKE '%本年利润%' OR subject_name LIKE '%结转%'
|
||||
- 失败行收集:{行号, 原因} 数组,不中断整体导入(部分成功模式)
|
||||
|
||||
### 3.4 前端:CashPlan.vue 增加"网银流水导入"tab
|
||||
|
||||
- el-tab-pane "流水导入":上传xlsx → 调 import/vouchers → 显示校验结果(借贷平衡✅/❌、期间合计、成功/失败行、错误明细)→ 成功提示
|
||||
- 注意:项目已知 el-dialog 坑,弹窗用 MyDialog;交互组件用原生 button
|
||||
- 导入成功后刷新 cash dashboard(现金余额更新可见)
|
||||
|
||||
### 3.5 财务Bot自助入库流程(文档)
|
||||
|
||||
文档:`docs/财务Bot网银流水自助入库流程.md`
|
||||
- 每月出纳导出网银流水 → 按模板整理xlsx
|
||||
- 财务Bot调 POST /api/cma/cash/import/vouchers(curl 或脚本)
|
||||
- 校验通过 → 入库 → 现金余额自动更新 → 看板可见
|
||||
- 校验失败 → 按错误明细修正后重导
|
||||
|
||||
## 四、验收标准(铁律七:不验证=没做)
|
||||
|
||||
1. ERP可行性结论 ✅(已有:ERP库不可达,走②)
|
||||
2. 模板文件存在:ls backend/scripts/templates/网银流水导入模板.xlsx
|
||||
3. 校验规则跑通:构造测试xlsx(含借贷不平衡、结转行、正常行)实测三种规则
|
||||
4. 入库接口可用:curl 导入 → SELECT voucher_details 有数据 → import_logs 有记录
|
||||
5. 现金流联动:导入后 GET /api/cma/cash/balance 现金余额=货币资金科目余额,dashboard可见
|
||||
6. 前端:CashPlan.vue 有"流水导入"tab,上传可导入
|
||||
7. 文档:财务Bot自助入库流程文档存在
|
||||
8. 无回归:/health 正常,已有cash端点正常
|
||||
|
||||
## 五、开发约束
|
||||
|
||||
- 代码库:/root/cma-management(后端 FastAPI + 前端 Vue3)
|
||||
- 后端重启:systemctl restart cma-backend(禁止手动起 uvicorn)
|
||||
- 前端部署:npm run build → cp -rf dist/* /var/www/cma/
|
||||
- 完成后 git add 关键目录(backend/app/ frontend/src/)+ commit + push
|
||||
- 数据库:MySQL cma 库,多租户 entity_id 隔离
|
||||
@@ -0,0 +1,160 @@
|
||||
# 财务Bot网银流水自助入库流程
|
||||
|
||||
> 适用:每月出纳导出银行/现金流水 → 按模板整理 xlsx → 财务Bot调用导入 API → 三校验 → 入库 → 现金流KPI联动 → 看板可见。
|
||||
> 方案文档:`docs/网银流水导入方案-20260828.md` | 后端代码:`backend/app/api/cash.py`(网银流水标准导入区)
|
||||
|
||||
---
|
||||
|
||||
## 一、流程总览
|
||||
|
||||
```
|
||||
出纳导出网银流水(Excel)
|
||||
│
|
||||
▼
|
||||
按模板整理 xlsx(列:凭证日期/凭证号/科目编码/科目名称/借方金额/贷方金额/摘要)
|
||||
│
|
||||
▼
|
||||
① 下载模板 GET /api/cma/cash/import/template (模板缺失时参考)
|
||||
② 导入 POST /api/cma/cash/import/vouchers (multipart file + entity_id)
|
||||
│
|
||||
▼
|
||||
三校验:① 借贷平衡(容差0.01) ② 期间合计 ③ 结转行识别
|
||||
│
|
||||
▼
|
||||
入库:voucher_details(凭证明细)+ import_logs(导入日志)
|
||||
│
|
||||
▼
|
||||
现金流联动:现金余额自动更新 + EXT_现金类KPI + F_CASH_SAFETY 现金安全垫KPI
|
||||
│
|
||||
▼
|
||||
看板可见:GET /api/cma/cash/balance、/api/cma/cash/dashboard
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 二、模板字段说明
|
||||
|
||||
模板下载:`GET /api/cma/cash/import/template`(需登录态,返回 xlsx 附件)。
|
||||
|
||||
| 列名 | 字段 | 必填 | 说明 |
|
||||
|------|------|------|------|
|
||||
| 凭证日期 | voucher_date | ✅ | 日期格式(Excel日期/文本均可,如 2026-08-01) |
|
||||
| 凭证号 | voucher_no | ✅ | 如 `记-001` |
|
||||
| 科目编码 | subject_code | ✅ | 如 `1002`、`1001`、`1122`;**1001/1002 开头视为货币资金科目**(现金流联动依据) |
|
||||
| 科目名称 | subject_name | ✅ | 如 `银行存款-工行` |
|
||||
| 借方金额 | debit_amount | 条件 | 数字,可含千分位逗号;与贷方二选一 |
|
||||
| 贷方金额 | credit_amount | 条件 | 数字,可含千分位逗号;与借方二选一 |
|
||||
| 摘要 | summary | 条件 | 含"结转"或科目名含"本年利润"/"结转" → 识别为结转行 |
|
||||
|
||||
> 列名兼容中英文别名(如 `date`/`voucher_date`、`借方`/`debit_amount` 等,不区分大小写),但建议严格使用模板列名。
|
||||
> 模板为单示例行,导入前删除示例行或直接覆盖为真实流水。
|
||||
|
||||
---
|
||||
|
||||
## 三、curl 命令示例
|
||||
|
||||
### 1. 登录拿 token(admin/admin123,账套 entity_id=1 酣客)
|
||||
|
||||
```bash
|
||||
TOKEN=$(curl -s -X POST http://127.0.0.1:8010/api/cma/auth/login \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"username":"admin","password":"admin123","entity_id":1}' \
|
||||
| python3 -c "import sys,json;print(json.load(sys.stdin)['token'])")
|
||||
|
||||
echo "$TOKEN" # 响应字段含 token/entity_id/entity_name/user
|
||||
```
|
||||
|
||||
### 2. 下载导入模板
|
||||
|
||||
```bash
|
||||
curl -s -o 网银流水导入模板.xlsx \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
http://127.0.0.1:8010/api/cma/cash/import/template
|
||||
|
||||
ls -la 网银流水导入模板.xlsx # 应 >1000 字节
|
||||
```
|
||||
|
||||
### 3. 导入网银流水(multipart 上传)
|
||||
|
||||
```bash
|
||||
curl -s -X POST http://127.0.0.1:8010/api/cma/cash/import/vouchers \
|
||||
-H "Authorization: Bearer $TOKEN" \
|
||||
-F "entity_id=1" \
|
||||
-F "file=@/path/to/2026年08月网银流水.xlsx"
|
||||
```
|
||||
|
||||
### 4. 看板核对
|
||||
|
||||
```bash
|
||||
curl -s -H "Authorization: Bearer $TOKEN" "http://127.0.0.1:8010/api/cma/cash/balance?entity_id=1"
|
||||
curl -s -H "Authorization: Bearer $TOKEN" "http://127.0.0.1:8010/api/cma/cash/dashboard?entity_id=1"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 四、三校验规则(导入时自动执行)
|
||||
|
||||
| # | 规则 | 逻辑 | 不通过时 |
|
||||
|---|------|------|----------|
|
||||
| ① | **借贷平衡** | Σ借方 = Σ贷方,容差 **0.01** | 返回 `balance_check.passed=false`,errors 追加 `借贷不平衡: 借方合计X ≠ 贷方合计Y,差额Z`(仍入库其余行,部分成功模式) |
|
||||
| ② | **期间合计** | 按 `period`(YYYY-MM)汇总借方/贷方合计,返回 `period_totals`,供对账 | 不阻断,仅展示 |
|
||||
| ③ | **结转行识别** | 摘要含"结转" 或 科目名含"本年利润"/"结转" → `carry_forward=1`,**不参与现金流余额计算** | 不阻断,返回 `carry_forward_count` |
|
||||
|
||||
> 逐行校验(失败行记录 errors,不阻断整体):凭证日期为空/无法解析、凭证号为空、科目编码为空、科目名称为空、金额非数字、金额为负、借贷同时为0 → 该行跳过,其余行照常入库(**部分成功模式**)。
|
||||
|
||||
---
|
||||
|
||||
## 五、入库内容
|
||||
|
||||
- **voucher_details 表**(凭证明细):voucher_no / voucher_date / subject_code / subject_name / debit_amount / credit_amount / summary / carry_forward / period / batch(batch = 文件名_时间戳)
|
||||
- **import_logs 表**(导入日志):filename / batch / total_rows / success_rows / failed_rows / errors(明细JSON)/ period / import_type="vouchers" / created_by="finance-bot"
|
||||
|
||||
返回体示例:
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"total": 120, "success_rows": 118, "failed_rows": 2,
|
||||
"errors": [{"row": 5, "field": "voucher_date", "reason": "日期无法解析: xxx"}],
|
||||
"balance_check": {"passed": true, "debit_total": 123456.78, "credit_total": 123456.78, "diff": 0.0, "tolerance": 0.01},
|
||||
"period_totals": {"2026-08": {"debit_total": 123456.78, "credit_total": 123456.78}},
|
||||
"carry_forward_count": 2,
|
||||
"cash_balance": 88.88,
|
||||
"kpi_updates": [{"kpi_code": "EXT_069", "kpi_name": "库存现金", "period": "2026-08", "value": ...}, ...],
|
||||
"batch": "2026年08月网银流水_20260828153000",
|
||||
"entity_id": 1
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 六、现金流联动(导入成功自动触发)
|
||||
|
||||
1. **现金余额自动更新**:汇总 1001/1002 开头科目(排除结转行)`Σ借 - Σ贷` 得货币资金期末余额(元)→ `set_current_cash_balance`(万元)→ `GET /api/cma/cash/balance` 的 `current_cash` 实时反映
|
||||
2. **EXT_现金类KPI**:名称含"现金"/"货币资金"的 active KPI(排除 F_CASH_SAFETY)写入/更新 `kpi_values`(source_type="ledger"、data_status="verified"、remark 记录批次与明细口径)——库存现金类→1001余额,银行类→1002余额,其余→货币资金总额
|
||||
3. **F_CASH_SAFETY 现金安全垫KPI**(万元):不存在则自动创建(formula=货币资金余额-短期借款);短期借款取 EXT_139 最新实际值(单位元÷10000),无数据时安全垫=货币资金余额
|
||||
|
||||
---
|
||||
|
||||
## 七、常见错误处理
|
||||
|
||||
| 现象 | 原因 | 处理 |
|
||||
|------|------|------|
|
||||
| 返回 `借贷不平衡: 借方合计X ≠ 贷方合计Y`(balance_check.passed=false) | 流水有遗漏/金额录错 | 核对 Excel 借贷金额,修正后重新导入(余额按最新批次重算,重复导入不叠加) |
|
||||
| HTTP 400 `缺少必要列: ...` | 列名与模板不一致(如"日期"未被别名命中、列名带空格/全半角差异) | 对照模板列名重命名表头,或使用 `_VOUCHER_COL_ALIASES` 支持的别名 |
|
||||
| HTTP 400 `无法读取Excel文件` | 文件损坏/非xlsx/加密 | 用 WPS/Excel 另存为 .xlsx 后再传 |
|
||||
| HTTP 400 `Excel文件为空(无数据行)` | 工作表无数据 | 删除空sheet或填入数据 |
|
||||
| errors 含 `日期无法解析` | 凭证日期为文本/格式异常 | 改成标准日期格式(如 2026-08-01) |
|
||||
| errors 含 `金额不能为负` / `借贷金额不能同时为0` | 数据录入问题 | 修正对应行(失败行不会入库) |
|
||||
| errors 含 `凭证号为空` / `科目编码为空` / `科目名称为空` | 缺单元格 | 补全后重导 |
|
||||
| HTTP 401 | token 失效/未登录 | 重新登录拿 token;注意登录必须带 entity_id |
|
||||
| HTTP 403 | 账号未授权该 entity_id | 联系管理员在 user_entities 授权 |
|
||||
| 模板下载 404 | 模板文件缺失 | 检查 `backend/scripts/templates/网银流水导入模板.xlsx` 是否存在,或运行 `backend/scripts/gen_voucher_import_template.py` 重新生成 |
|
||||
|
||||
---
|
||||
|
||||
## 八、注意事项
|
||||
|
||||
- 导入是**增量写入**:同一文件重复导入会生成多条记录(batch 不同),余额按最新 batch 重算;对账以 `batch` 为粒度
|
||||
- 结转行不参与现金流余额计算,但会计入借贷平衡校验
|
||||
- 现金流联动失败不阻断入库(已入库数据保留,KPI 联动失败记 error 日志),可联系全栈Bot排查 `journalctl -u cma-backend`
|
||||
Reference in New Issue
Block a user