# 财务ERP - 进销存模块（采购与台账）详细开发流程文档

## 文档说明
本文档为20260618进销存模块梳理后的详细开发流程文档，面向开发团队，明确开发的**顺序、依赖、任务拆解、开发步骤及验收标准**。重点覆盖采购模块与财务模块的互通，以及台账管理的完整实现。

> **重要说明**：本文档仅包含业务需求、流程设计、接口契约和验收标准，不包含具体代码实现。请根据现有系统架构自行设计代码结构，避免与现有代码产生冲突。


## 版本历史
| 版本 | 日期 | 修改内容 | 作者 |
|------|------|---------|------|
| v1.0 | 2026-06-25 | 初稿，基于20260618需求梳理 | 架构组 |


## 1. 模块依赖关系与开发顺序

### 1.1 模块依赖图
```
┌─────────────────────────────────────────────────────────────────────────────┐
│                          开发依赖关系（自上而下）                           │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                             │
│  ┌─────────────────────┐                                                   │
│  │   基础配置模块       │  ◄── 第1周：无依赖，可优先开发                    │
│  │  - 采购账户配置      │                                                   │
│  │  - 供应商收款账户    │                                                   │
│  │  - 进销存全局配置    │                                                   │
│  └──────────┬──────────┘                                                   │
│             │                                                               │
│             ▼                                                               │
│  ┌─────────────────────┐                                                   │
│  │   采购订单（会计版）  │  ◄── 第2周：依赖基础配置 + ERP供应商/SKU数据     │
│  │  - 订单创建/查询     │                                                   │
│  │  - 付款/退款/入库    │                                                   │
│  └──────────┬──────────┘                                                   │
│             │                                                               │
│             ▼                                                               │
│  ┌─────────────────────┐  ◄── 第3周：依赖采购订单数据                       │
│  │   采购账户台账       │                                                   │
│  │  - 台账列表查询      │                                                   │
│  │  - 台账付款          │                                                   │
│  │  - 批量对账单上传    │                                                   │
│  └──────────┬──────────┘                                                   │
│             │                                                               │
│             ▼                                                               │
│  ┌─────────────────────┐  ◄── 第4周：依赖采购订单 + 税局API                │
│  │   发票台账           │                                                   │
│  │  - 发票同步          │                                                   │
│  │  - 发票入账          │                                                   │
│  │  - 供应商台账查询    │                                                   │
│  └──────────┬──────────┘                                                   │
│             │                                                               │
│             ▼                                                               │
│  ┌─────────────────────┐  ◄── 第5周：依赖库存变动数据                       │
│  │   库存台账           │                                                   │
│  │  - 移动加权平均计算  │                                                   │
│  │  - 汇总账/明细账     │                                                   │
│  │  - 勾稽校验          │                                                   │
│  └─────────────────────┘                                                   │
│                                                                             │
└─────────────────────────────────────────────────────────────────────────────┘
```

### 1.2 开发顺序总览
| 阶段 | 周期 | 模块 | 产出物 | 依赖 |
|------|------|------|--------|------|
| **阶段一** | 第1周 | 基础配置模块 | 采购账户CRUD、供应商收款账户管理、配置校验逻辑 | 现有科目表、辅助核算表 |
| **阶段二** | 第2-3周 | 采购订单（会计版） | 订单创建、付款、退款、入库、状态管理、凭证生成 | 阶段一 + ERP供应商/SKU数据 |
| **阶段三** | 第3-4周 | 采购账户台账 | 台账列表、台账付款、批量上传对账单、图表统计 | 阶段二 |
| **阶段四** | 第4-5周 | 发票台账与供应商台账 | 发票同步、发票入账、供应商汇总查询、未开票导出 | 阶段二 + 税局API |
| **阶段五** | 第5-6周 | 库存台账（本地仓库） | 移动加权平均计算、汇总账、明细账、勾稽校验 | 阶段二 + 阶段四 |
| **阶段六** | 第6周 | 联调与测试 | 端到端流程测试、性能测试、Bug修复 | 全部模块 |


## 2. 数据库设计（新增表）

### 2.1 新增表清单

| 表名 | 说明 | 关联模块 |
|------|------|---------|
| `fin_purchase_account` | 采购账户表 | 基础配置 |
| `fin_supplier_bank` | 供应商收款账户表 | 基础配置 |
| `fin_purchase_order` | 采购订单表（会计版） | 采购订单 |
| `fin_purchase_payment` | 采购付款明细表 | 采购订单/台账 |
| `fin_invoice` | 发票台账表 | 发票台账 |
| `fin_warehouse_stock` | 库存余额表 | 库存台账 |
| `fin_inventory_transaction` | 库存变动明细表 | 库存台账 |

### 2.2 表结构设计要点

#### 2.2.1 采购账户表 `fin_purchase_account`

| 字段 | 类型 | 说明 | 校验规则 |
|------|------|------|---------|
| `id` | bigint unsigned | 主键 | 自增 |
| `groupid` | bigint unsigned | 租户ID | 必填，索引 |
| `account_name` | varchar(100) | 账户名称 | 必填，唯一（同租户下） |
| `account_type` | tinyint | 1-现金，2-账期 | 必填，默认1 |
| `related_subject_id` | bigint unsigned | 对应会计科目ID | 必填，关联fin_accounting_subjects |
| `config_json` | json | 完整配置 | 见下方结构 |
| `is_enabled` | tinyint | 是否启用 | 默认1 |
| `created_time` | datetime | 创建时间 | 自动 |
| `updated_time` | datetime | 更新时间 | 自动 |

**config_json 结构：**
```json
{
  "feeTypes": [
    { "code": "GOODS", "name": "货款", "isSupplierRelated": true },
    { "code": "FREIGHT", "name": "运费", "isSupplierRelated": true },
    { "code": "SERVICE", "name": "跨境直采手续费", "isSupplierRelated": false }
  ],
  "paySubjectMapping": {
    "GOODS": { "debitSubjectId": 101, "creditSubjectId": 201 },
    "FREIGHT": { "debitSubjectId": 102, "creditSubjectId": 201 }
  },
  "refundMapping": {
    "GOODS": { "debitSubjectId": 201, "creditSubjectId": 101 }
  },
  "creditPayAccounts": [
    { "cashAccountId": 1, "subjectMapping": { "debit": 301, "credit": 201 } }
  ]
}
```

#### 2.2.2 供应商收款账户表 `fin_supplier_bank`

| 字段 | 类型 | 说明 | 校验规则 |
|------|------|------|---------|
| `id` | bigint unsigned | 主键 | 自增 |
| `groupid` | bigint unsigned | 租户ID | 必填 |
| `supplier_id` | bigint unsigned | 供应商ID | 必填，索引 |
| `bank_name` | varchar(100) | 开户银行 | 可选 |
| `account_name` | varchar(100) | 收款账户名称 | 必填 |
| `account_no` | varchar(50) | 收款账号 | 必填 |
| `is_default` | tinyint | 是否默认 | 默认0，同供应商仅一个默认 |
| `is_enabled` | tinyint | 是否启用 | 默认1 |
| `created_time` | datetime | 创建时间 | 自动 |

#### 2.2.3 采购订单表 `fin_purchase_order`

| 字段 | 类型 | 说明 | 校验规则 |
|------|------|------|---------|
| `id` | bigint unsigned | 主键 | 自增 |
| `groupid` | bigint unsigned | 租户ID | 必填，索引 |
| `supplier_id` | bigint unsigned | 供应商ID | 必填，关联t_erp_supplier |
| `purchase_account_id` | bigint unsigned | 采购账户ID | 必填 |
| `sku` | varchar(50) | SKU编码 | 必填 |
| `warehouse_code` | varchar(50) | 入库仓库编码 | 必填 |
| `order_qty` | decimal(12,2) | 订单数量 | >0 |
| `unit_price` | decimal(18,4) | 采购单价 | ≥0 |
| `total_amount` | decimal(18,2) | 订单金额 | = order_qty × unit_price |
| `paid_amount` | decimal(18,2) | 累计已付 | ≥0，≤total_amount |
| `inventory_value` | decimal(18,2) | 库存价值 | ≥0 |
| `invoiced_amount` | decimal(18,2) | 已开票金额 | ≥0，≤total_amount |
| `status_order` | tinyint | 1-草稿，2-已确认，3-已完成，4-已取消 | 默认1 |
| `status_pay` | tinyint | 1-未付款，2-部分付款，3-已付款，4-退款中，5-已退款 | 默认1 |
| `status_inv` | tinyint | 1-未开票，2-部分开票，3-已开票 | 默认1 |
| `status_stock` | tinyint | 1-未收货，2-部分收货，3-已收货 | 默认1 |

#### 2.2.4 采购付款明细表 `fin_purchase_payment`

| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | bigint unsigned | 主键 |
| `purchase_order_id` | bigint unsigned | 关联采购订单 |
| `purchase_account_id` | bigint unsigned | 使用的采购账户 |
| `voucher_id` | bigint unsigned | 生成的凭证ID |
| `fee_type` | varchar(50) | 费用类型（货款/运费/手续费） |
| `is_supplier_related` | tinyint | 是否归属供应商款项 |
| `amount` | decimal(18,2) | 支付金额 |
| `payment_date` | date | 付款日期 |
| `is_refund` | tinyint | 是否退款 |
| `refund_original_id` | bigint unsigned | 退款对应的原付款明细ID |
| `status` | tinyint | 0-正常，1-已退款 |

#### 2.2.5 发票台账表 `fin_invoice`

| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | bigint unsigned | 主键 |
| `invoice_no` | varchar(100) | 数电发票号码（唯一） |
| `invoice_type` | varchar(20) | 数电专票/普票/传统 |
| `groupid` | bigint unsigned | 入账账套ID |
| `supplier_id` | bigint unsigned | 匹配的供应商ID |
| `seller_name` | varchar(200) | 销方名称 |
| `seller_tax_no` | varchar(50) | 销方税号 |
| `invoice_date` | date | 开票日期 |
| `amount_with_tax` | decimal(18,2) | 价税合计 |
| `amount_without_tax` | decimal(18,2) | 不含税金额 |
| `tax_amount` | decimal(18,2) | 税额 |
| `status` | varchar(20) | NORMAL/CANCELLED/RED_ALL/RED_PART/ABNORMAL |
| `posting_status` | tinyint | 0-未入账，1-已入账 |
| `voucher_id` | bigint unsigned | 入账凭证ID |

#### 2.2.6 库存余额表 `fin_warehouse_stock`

| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | bigint unsigned | 主键 |
| `groupid` | bigint unsigned | 租户ID |
| `sku` | varchar(50) | SKU编码 |
| `warehouse_code` | varchar(50) | 仓库编码 |
| `warehouse_type` | tinyint | 1-本地，2-FBA |
| `current_qty` | decimal(12,2) | 当前数量 |
| `current_amount` | decimal(18,2) | 当前金额 |
| `unit_cost` | decimal(18,4) | 移动平均单价 |
| `period` | varchar(6) | 会计期间（YYYYMM） |

#### 2.2.7 库存变动明细表 `fin_inventory_transaction`

| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | bigint unsigned | 主键 |
| `groupid` | bigint unsigned | 租户ID |
| `sku` | varchar(50) | SKU编码 |
| `warehouse_code` | varchar(50) | 仓库编码 |
| `trans_date` | date | 变动日期 |
| `trans_type` | varchar(50) | PURCHASE_IN/SALE_OUT/TRANSFER_IN/ADJUST |
| `source_doc_id` | varchar(100) | 来源单据号 |
| `qty_change` | decimal(12,2) | 正为入库，负为出库 |
| `amount_change` | decimal(18,2) | 金额变动 |
| `unit_cost` | decimal(18,4) | 发生时单价 |
| `voucher_id` | bigint unsigned | 关联凭证ID |


## 3. 服务层开发任务（按顺序执行）

### 3.1 阶段一：基础配置模块（第1周）

#### 3.1.1 任务清单
| 序号 | 任务 | 服务类 | 方法/职责 | 验收标准 |
|------|------|--------|---------|---------|
| 1 | 采购账户CRUD | `PurchaseAccountService` | 创建、更新、删除、查询列表 | 可正常增删改查，配置JSON正确序列化 |
| 2 | 采购账户配置校验 | `PurchaseAccountValidator` | 校验费用类型、科目映射完整性 | 配置不完整时拒绝保存并提示 |
| 3 | 供应商收款账户管理 | `SupplierBankService` | 保存、更新、设置默认、查询 | 支持多账户、默认账户唯一 |
| 4 | 进销存全局配置 | `InventoryConfigService` | 获取配置、更新配置 | 支持配置成本方法（仅移动加权） |

#### 3.1.2 开发步骤
1. **创建实体类**：映射新增表，使用MyBatis-Plus注解，JSON字段配置TypeHandler
2. **创建Mapper接口**：继承BaseMapper，定义必要的自定义查询方法
3. **实现Service层**：
   - 创建：校验参数完整性、唯一性，保存配置JSON
   - 更新：校验是否被引用，合并配置
   - 删除：软删除或校验无引用后删除
   - 列表：支持按账套、类型筛选
4. **实现Controller层**：定义REST接口，参数校验，统一响应格式
5. **单元测试**：覆盖CRUD各场景

#### 3.1.3 接口契约

**创建采购账户**
- `POST /api/finance/purchase/account/create`
- 请求体：
```json
{
  "groupid": 1001,
  "accountName": "公户现金",
  "accountType": 1,
  "relatedSubjectId": 2384,
  "configJson": { ... }
}
```
- 响应：`{ "code": 200, "data": { "id": 1 } }`

**查询采购账户列表**
- `GET /api/finance/purchase/account/list?groupid=1001&type=1`
- 响应：账户列表JSON

#### 3.1.4 测试用例
| 测试场景 | 预期结果 |
|---------|---------|
| 创建现金类账户，配置完整 | 保存成功 |
| 创建账期类账户，未配置cashAccount列表 | 校验失败，提示配置不完整 |
| 删除正在使用的账户 | 返回错误，提示账户已被引用 |
| 设置默认收款账户 | 该账户is_default=1，其他同供应商账户=0 |

### 3.2 阶段二：采购订单（会计版）（第2-3周）

#### 3.2.1 任务清单
| 序号 | 任务 | 服务类 | 方法/职责 | 验收标准 |
|------|------|--------|---------|---------|
| 1 | 采购订单创建 | `PurchaseOrderService` | 创建订单，初始状态为草稿 | 订单保存，状态正确 |
| 2 | 采购订单确认 | `PurchaseOrderService` | 确认订单，状态变为已确认 | 仅草稿状态可确认 |
| 3 | 采购订单付款 | `PurchasePaymentService` | 付款，生成凭证，更新状态 | 凭证生成正确，状态更新 |
| 4 | 采购订单退款 | `PurchasePaymentService` | 退款，生成红字凭证，更新状态 | 凭证方向相反，状态更新 |
| 5 | 采购入库 | `PurchaseOrderService` | 入库，更新库存，生成凭证 | 库存更新，凭证生成 |
| 6 | 采购退货 | `PurchaseOrderService` | 退货，更新库存，生成凭证 | 库存回退，凭证生成 |
| 7 | 订单列表查询 | `PurchaseOrderService` | 分页查询，多维度筛选 | 筛选条件生效，状态展示正确 |

#### 3.2.2 订单状态机设计

**订单状态（status_order）流转：**
- `1-草稿` → 确认 → `2-已确认` → 付款+入库完成 → `3-已完成`
- `1-草稿` → `4-已取消`（仅草稿可取消）
- `2-已确认` → 任何状态 → `3-已完成`（所有子状态均为完成）

**付款状态（status_pay）流转：**
- `1-未付款` → 付款金额>0且<总额 → `2-部分付款`
- `1-未付款` 或 `2-部分付款` → 付款金额=总额 → `3-已付款`
- `3-已付款` → 发起退款 → `4-退款中` → 退款完成 → `5-已退款`

**库存状态（status_stock）流转：**
- `1-未收货` → 入库数量>0且<订单数量 → `2-部分收货`
- `1-未收货` 或 `2-部分收货` → 入库数量=订单数量 → `3-已收货`

**发票状态（status_inv）流转：**
- `1-未开票` → 开票金额>0且<总额 → `2-部分开票`
- `1-未开票` 或 `2-部分开票` → 开票金额=总额 → `3-已开票`

#### 3.2.3 凭证生成规则

**场景A：现金类账户付款**
- 付款凭证：
  - 借：预付账款-ERP采购供应商（供应商辅助核算）
  - 贷：对应现金科目（如银行存款-浦发）
- 在途库存凭证（同时生成）：
  - 借：在途库存_XX本地仓库（存货辅助核算）
  - 贷：预付账款-ERP在途发票

**场景B：账期类账户付款**
- 付款凭证：
  - 借：预付账款-ERP采购供应商（供应商辅助核算）
  - 贷：应付账款_XXX（账期账户对应科目）
- 在途库存凭证（同时生成）：
  - 借：在途库存_XX本地仓库（存货辅助核算）
  - 贷：预付账款-ERP在途发票

**场景C：入库**
- 借：库存商品_XX本地仓库（存货辅助核算）
- 贷：在途库存_XX本地仓库

**场景D：发票入账**
- 借：预付账款-ERP在途发票
- 贷：预付账款-ERP采购供应商（供应商辅助核算）

#### 3.2.4 接口契约

**采购订单付款**
- `POST /api/finance/purchase/order/pay`
- 请求体：
```json
{
  "orderId": 1001,
  "accountId": 5,
  "feeType": "GOODS",
  "amount": 10000.00,
  "paymentDate": "2026-06-25"
}
```
- 响应：`{ "code": 200, "data": { "voucherId": 123, "status": "PAID" } }`

**采购入库**
- `POST /api/finance/purchase/order/receive`
- 请求体：
```json
{
  "orderId": 1001,
  "qty": 100,
  "receiveDate": "2026-06-25"
}
```
- 响应：`{ "code": 200, "data": { "voucherId": 124, "newUnitCost": 95.50 } }`

#### 3.2.5 异常场景处理
| 异常场景 | 错误码 | 提示信息 |
|---------|--------|---------|
| 付款金额超过未付金额 | 4001 | "付款金额超过未付金额" |
| 订单已付款完成 | 4002 | "订单已付款完成，不可重复付款" |
| 订单状态不可付款 | 4003 | "当前订单状态不可付款" |
| 入库数量超过订单数量 | 4004 | "入库数量超过订单未入库数量" |
| 采购账户科目映射缺失 | 5001 | "采购账户科目映射不完整，请联系管理员" |

#### 3.2.6 测试用例
| 测试场景 | 预期结果 |
|---------|---------|
| 创建订单，初始状态 | status_order=1（草稿），所有子状态均为1 |
| 现金付款，全额 | 生成2张凭证（付款+在途），status_pay=3（已付款） |
| 账期付款，全额 | 生成2张凭证（应付+在途），status_pay=3，现金未扣 |
| 部分付款 | status_pay=2（部分付款），待付金额正确 |
| 入库数量等于订单数量 | status_stock=3（已收货） |
| 部分入库 | status_stock=2（部分收货） |
| 全额退款 | 生成红字凭证，status_pay=5（已退款） |

### 3.3 阶段三：采购账户台账（第3-4周）

#### 3.3.1 任务清单
| 序号 | 任务 | 服务类 | 方法/职责 | 验收标准 |
|------|------|--------|---------|---------|
| 1 | 台账列表查询 | `LedgerQueryService` | 分页查询，汇总行计算 | 数据准确，汇总正确 |
| 2 | 台账付款（批量） | `LedgerPaymentService` | 勾选多条，生成凭证，更新状态 | 凭证生成，状态更新 |
| 3 | 对账单上传 | `LedgerBatchService` | 解析Excel，预览确认，批量付款 | 解析成功，预览准确 |
| 4 | 退款处理 | `LedgerPaymentService` | 生成红字凭证，更新台账 | 凭证正确，台账更新 |
| 5 | 图表统计 | `LedgerStatisticsService` | 返回日期X轴+金额Y轴数据 | 图表数据正确 |

#### 3.3.2 台账列表查询字段

| 字段组 | 包含字段 | 计算逻辑 |
|--------|---------|---------|
| 订单信息 | 账簿、采购账户、采购订单号、SKU、供应商、收款账号 | 直接从订单表关联查询 |
| 付款状态 | 订单金额、请款金额、已付金额、待付金额 | 请款=归属供应商的费用类型金额合计；已付=累计付款；待付=请款-已付 |
| 库存状态 | 库存价值、已入库价值、待入库价值 | 库存价值=采购订单的inventory_value；已入库=SUM(入库变动金额)；待入库=库存价值-已入库 |
| 发票状态 | 应开票金额、已开票金额、待开票金额 | 应开票=归属供应商的请款金额；已开票=sum(invoiced_amount)；待开票=应开票-已开票 |

**重要限制**：
- 非供应商款项（如跨境直采手续费）不计入请款金额
- 退款金额按原方向反冲，减少请款金额

#### 3.3.3 台账付款核心约束

1. **同账户约束**：一次付款操作，所有选中的记录必须使用同一个账期采购账户
2. **账户类型约束**：仅账期类型账户支持台账付款
3. **现金账户约束**：选中的现金账户必须在账期账户的`creditPayAccounts`配置中
4. **金额约束**：付款金额不超过各订单待付金额之和
5. **尾差处理**：按比例分摊，差额归入最后一条记录

#### 3.3.4 Excel批量上传模板

| 列名 | 类型 | 说明 | 校验规则 |
|------|------|------|---------|
| 采购订单号 | 文本 | 订单唯一标识 | 必填，存在于系统 |
| SKU | 文本 | SKU编码 | 必填，与订单匹配 |
| 应付金额 | 数字 | 订单应付金额 | 必填，≥0 |
| 本次付款金额 | 数字 | 本次实际支付 | 必填，>0，≤应付-已付 |
| 费用类型 | 文本 | 货款/运费/其他 | 必填，在配置中 |

**上传流程**：
1. 用户下载模板
2. 填写后上传
3. 系统解析并校验
4. 返回预览数据（含校验结果）
5. 用户确认后提交
6. 批量生成付款记录和凭证

#### 3.3.5 接口契约

**台账列表查询**
- `GET /api/finance/ledger/purchase/page`
- 参数：`groupid`, `accountId`, `startDate`, `endDate`, `payStatus`, `page`, `size`
- 响应：分页数据 + 汇总行（订单金额、已付金额、待付金额汇总）

**台账付款**
- `POST /api/finance/ledger/purchase/ledger-pay`
- 请求体：
```json
{
  "orderIds": [1001, 1002, 1003],
  "cashAccountId": 5,
  "paymentDate": "2026-06-25"
}
```
- 响应：`{ "code": 200, "data": { "voucherIds": [123, 124, 125], "totalAmount": 50000 } }`

**上传对账单**
- `POST /api/finance/ledger/purchase/upload-pay`
- 请求：`multipart/form-data`，file字段
- 响应：预览数据

#### 3.3.6 测试用例
| 测试场景 | 预期结果 |
|---------|---------|
| 查询台账列表，带筛选条件 | 返回正确数据，汇总行金额正确 |
| 勾选3条账期记录，台账付款 | 生成3张凭证，订单待付金额归零 |
| 上传对账单Excel | 解析成功，预览显示待付款明细 |
| 确认上传的对账单 | 批量生成凭证，更新订单状态 |
| 部分退款 | 生成红字凭证，订单状态变为退款中/已退款 |

### 3.4 阶段四：发票台账与供应商台账（第4-5周）

#### 3.4.1 任务清单
| 序号 | 任务 | 服务类 | 方法/职责 | 验收标准 |
|------|------|--------|---------|---------|
| 1 | 发票同步（税局API） | `InvoiceSyncService` | 拉取发票数据，写入fin_invoice | 去重，匹配供应商 |
| 2 | 发票台账查询 | `InvoiceQueryService` | 多维度筛选，显示入账状态 | 筛选条件生效 |
| 3 | 发票入账 | `InvoicePostingService` | 生成凭证，更新发票和订单状态 | 凭证生成，状态更新 |
| 4 | 供应商台账查询 | `SupplierLedgerService` | 按供应商汇总各金额字段 | 汇总数据正确 |
| 5 | 未开票明细导出 | `SupplierLedgerService` | 导出Excel | 格式正确，数据完整 |

#### 3.4.2 发票同步流程

1. **触发方式**：
   - 定时任务：每日凌晨2点自动执行
   - 手动触发：用户在页面点击"同步"按钮
2. **数据来源**：税局电子发票API（或金蝶接口）
3. **处理逻辑**：
   - 按日期范围拉取发票列表
   - 按发票号码去重（已存在则跳过）
   - 按销方税号匹配内部供应商（`t_erp_supplier`）
   - 匹配成功则记录`supplier_id`，否则留空
   - 写入`fin_invoice`，状态为"正常"，入账状态为"未入账"
4. **异常处理**：单条失败不影响整体，记录日志

#### 3.4.3 发票入账流程

1. **入口**：发票台账页面勾选未入账发票 → 点击"入账"
2. **或**：供应商台账页面勾选待开票记录 → 选择发票 → 提交入账
3. **处理逻辑**：
   - 校验发票未入账
   - 校验供应商与订单供应商一致
   - 获取该供应商下待开票订单列表
   - 按订单待开票金额比例分摊发票金额
   - 生成凭证（借：预付账款-ERP在途发票，贷：预付账款-ERP采购供应商）
   - 更新发票：`posting_status=1`，关联`voucher_id`
   - 更新订单：`invoiced_amount`增加，`status_inv`重新计算

#### 3.4.4 供应商台账查询字段

| 字段 | 计算逻辑 |
|------|---------|
| 供应商名称 | 从t_erp_supplier关联 |
| 应付金额 | SUM(归属供应商的请款金额) |
| 已付金额 | SUM(已付款金额，仅归属供应商部分) |
| 待付金额 | 应付金额 - 已付金额 |
| 库存价值 | SUM(inventory_value) |
| 应开票金额 | SUM(归属供应商的请款金额) |
| 已开票金额 | SUM(invoiced_amount) |
| 待开票金额 | 应开票金额 - 已开票金额 |

#### 3.4.5 接口契约

**发票同步**
- `POST /api/finance/ledger/invoice/sync`
- 请求体（可选）：
```json
{
  "groupid": 1001,
  "startDate": "2026-06-01",
  "endDate": "2026-06-25"
}
```
- 响应：`{ "code": 200, "data": { "success": 50, "failed": 2 } }`

**发票入账**
- `POST /api/finance/ledger/invoice/post`
- 请求体：
```json
{
  "invoiceIds": [101, 102],
  "orderIds": [1001, 1002, 1003],
  "voucherDate": "2026-06-25"
}
```
- 响应：`{ "code": 200, "data": { "voucherId": 200 } }`

**供应商台账查询**
- `GET /api/finance/ledger/supplier/page`
- 参数：`groupid`, `supplierIds`, `page`, `size`
- 响应：分页数据

**导出未开票明细**
- `GET /api/finance/ledger/supplier/export-uninvoiced`
- 参数：`groupid`, `supplierId`
- 响应：Excel文件流

#### 3.4.6 测试用例
| 测试场景 | 预期结果 |
|---------|---------|
| 手动触发发票同步 | 拉取到新发票，写入fin_invoice，匹配供应商 |
| 发票入账（全额匹配） | 生成凭证，订单待开票金额归零 |
| 发票入账（部分匹配） | 分摊到多条订单，待开票金额减少 |
| 供应商台账查询 | 汇总数据正确，与订单明细一致 |
| 导出未开票明细 | Excel包含未开票订单及金额 |

### 3.5 阶段五：库存台账（本地仓库）（第5-6周）

#### 3.5.1 任务清单
| 序号 | 任务 | 服务类 | 方法/职责 | 验收标准 |
|------|------|--------|---------|---------|
| 1 | 移动加权平均计算 | `MovingAverageCostCalculator` | 计算新单价、出库成本 | 计算精确，保留4位小数 |
| 2 | 采购入库更新库存 | `InventoryService` | 更新余额表、插入变动明细、生成凭证 | 余额正确，明细完整 |
| 3 | 销售出库更新库存 | `InventoryService` | 更新余额表、插入变动明细、生成凭证 | 余额正确，明细完整 |
| 4 | 库存汇总账查询 | `InventoryQueryService` | 按期间展示期初/入库/出库/期末 | 数据准确 |
| 5 | 库存明细账查询 | `InventoryQueryService` | 逐笔变动明细，联查凭证 | 明细完整，可联查 |
| 6 | 勾稽校验 | `InventoryCheckService` | 对比库存余额与科目余额 | 提示差异明细 |

#### 3.5.2 移动加权平均核算规则

**核心公式：**
```
新移动单价 = (当前结存金额 + 本次入库金额) / (当前结存数量 + 本次入库数量)
出库成本 = 出库数量 × 当前移动单价
```

**重要规则：**
1. 只有采购入库（`PURCHASE_IN`）、盘盈入库（`ADJUST_IN`）会改变移动单价
2. 销售出库（`SALE_OUT`）、调拨出库（`TRANSFER_OUT`）不改变单价，仅减少数量
3. 库存余额不通过"单价×数量"反算，采用滚动加减法：
   - 新余额 = 旧余额 + 入库金额 - 出库成本
4. 单价保留4位小数，金额保留2位小数
5. 尾差处理：出库时先乘后减，避免单价反算误差

**期间核算：**
- 按月汇总，每月初从上月期末结转
- 同一SKU在同一仓库同一期间仅有一条库存余额记录

#### 3.5.3 库存变动类型

| 类型编码 | 说明 | 数量方向 | 是否影响单价 | 来源 |
|---------|------|---------|-------------|------|
| `PURCHASE_IN` | 采购入库 | 正 | 是 | 采购订单收货 |
| `SALE_OUT` | 销售出库 | 负 | 否 | 销售订单发货 |
| `TRANSFER_IN` | 调拨入库 | 正 | 否（沿用原单价） | 仓库调拨 |
| `TRANSFER_OUT` | 调拨出库 | 负 | 否 | 仓库调拨 |
| `ADJUST_IN` | 盘盈入库 | 正 | 是 | 盘点调整 |
| `ADJUST_OUT` | 盘亏出库 | 负 | 否 | 盘点调整 |

#### 3.5.4 汇总账查询结构

| 列 | 说明 |
|----|------|
| 期间 | YYYYMM |
| 账簿 | groupid对应账套名称 |
| SKU | SKU编码 |
| 仓库 | 仓库编码 |
| 期初数量 | 上期期末数量 |
| 期初金额 | 上期期末金额 |
| 本期入库数量 | SUM(入库变动数量) |
| 本期入库金额 | SUM(入库变动金额) |
| 本期出库数量 | SUM(出库变动数量绝对值) |
| 本期出库金额 | SUM(出库变动金额绝对值) |
| 期末数量 | 期初数量 + 入库数量 - 出库数量 |
| 期末金额 | 期初金额 + 入库金额 - 出库金额 |
| 期末单价 | 期末金额 / 期末数量 |

#### 3.5.5 明细账查询结构

| 列 | 说明 |
|----|------|
| 日期 | 变动日期 |
| SKU | SKU编码 |
| 仓库 | 仓库编码 |
| 变动类型 | PURCHASE_IN/SALE_OUT等 |
| 数量 | 正为入库，负为出库 |
| 单价 | 发生时使用的单价 |
| 金额 | 数量 × 单价 |
| 来源单据号 | 采购订单号/销售订单号等 |
| 凭证号 | 关联凭证编号 |

#### 3.5.6 勾稽校验逻辑

**校验目标**：库存台账的期末金额与总账中"库存商品"科目余额一致

**校验步骤**：
1. 查询指定期间内所有SKU的期末库存金额合计
2. 查询总账中"库存商品"科目（及所有子科目）的期末余额
3. 对比两者差异
4. 若差异不为零，列出差异明细（按SKU或按仓库）

**差异处理建议**：
- 差异 < 100元：可接受，记录为"微小差异"
- 差异 ≥ 100元：需要生成调整凭证

#### 3.5.7 接口契约

**库存汇总账查询**
- `GET /api/finance/ledger/stock/summary`
- 参数：`groupid`, `period`（YYYYMM）, `sku`（可选模糊搜索）, `warehouseCode`（可选）
- 响应：分页数据

**库存明细账查询**
- `GET /api/finance/ledger/stock/detail`
- 参数：`groupid`, `sku`, `warehouseCode`, `startDate`, `endDate`
- 响应：分页明细数据

**勾稽校验**
- `GET /api/finance/ledger/stock/check`
- 参数：`groupid`, `period`
- 响应：
```json
{
  "stockAmount": 1000000.00,
  "ledgerAmount": 1000050.00,
  "diff": -50.00,
  "status": "BALANCED",  // 或 "DIFF"
  "details": []  // 差异明细
}
```

#### 3.5.8 测试用例
| 测试场景 | 预期结果 |
|---------|---------|
| 采购入库，库存为空 | 新增库存余额记录，单价=入库金额/数量 |
| 采购入库，库存已有 | 更新余额，重新计算移动单价 |
| 销售出库 | 按当前单价计算成本，减少库存 |
| 盘点调整 | 调整后单价重新计算 |
| 勾稽校验，无差异 | 返回BALANCED状态 |
| 勾稽校验，有差异 | 返回DIFF状态，列出差异明细 |


## 4. 联调与测试（第6周）

### 4.1 端到端流程测试

| 测试场景 | 操作步骤 | 验证点 |
|---------|---------|--------|
| 完整采购流程（现金） | 创建订单→付款→入库→发票入账 | 凭证正确，台账更新，状态流转正确 |
| 完整采购流程（账期） | 创建订单→账期付款→台账付款→入库→发票入账 | 凭证正确，台账更新，状态流转正确 |
| 部分付款+部分入库 | 创建订单→部分付款→部分入库→余款付款→余货入库 | 状态正确，金额正确 |
| 退款流程（已付款未入库） | 创建订单→全额付款→全额退款 | 红字凭证，状态变为已退款 |
| 退款流程（已付款已入库） | 创建订单→付款→入库→退货→退款 | 退货凭证，退款凭证，库存回退 |
| 批量对账单上传 | 上传Excel→预览→确认 | 批量生成凭证和付款明细 |
| 月末结账 | 调汇→结转损益→结账 | 期间关闭，不可修改 |

### 4.2 性能测试

| 测试项 | 数据量 | 性能要求 |
|--------|--------|---------|
| 台账列表查询 | 10万条订单 | 响应时间 ≤ 3秒 |
| 发票同步 | 单次1万条 | 处理时间 ≤ 5分钟 |
| 库存汇总账 | 1万条SKU记录 | 响应时间 ≤ 5秒 |
| 批量对账单上传 | 500条记录 | 处理时间 ≤ 30秒 |
| 移动加权平均计算 | 单次1000条变动 | 计算时间 ≤ 5秒 |

### 4.3 数据一致性测试

| 测试项 | 验证方法 |
|--------|---------|
| 采购订单金额 = 付款明细合计 | 查询统计对比 |
| 订单库存价值 = 入库明细合计 | 查询统计对比 |
| 订单已开票金额 = 发票入账金额合计 | 查询统计对比 |
| 库存余额 = 期初 + 入库 - 出库 | 滚动计算校验 |
| 库存余额对应总账科目余额 | 勾稽校验接口 |


## 5. 数据迁移方案（第6周）

### 5.1 迁移范围
- 历史采购订单（从ERP采购单表）
- 历史库存数据（从现有库存表）
- 历史发票数据（从金蝶/税局）

### 5.2 迁移顺序
1. **采购账户**：手动创建历史使用的采购账户
2. **采购订单**：按时间分批迁移，每批1000条
3. **付款明细**：从ERP付款记录中提取
4. **库存余额**：从当前库存表初始化
5. **发票台账**：从税局API拉取历史发票

### 5.3 迁移校验
- 迁移完成后，运行勾稽校验
- 对比迁移前后的总额数据，误差 < 100元为通过


## 6. 风险与应对

| 风险 | 影响 | 应对措施 |
|------|------|---------|
| 税局API不稳定 | 发票同步失败 | 支持手动导入Excel，保留重试机制 |
| 历史数据量过大 | 迁移耗时长 | 分批迁移，错峰执行 |
| 科目配置复杂 | 凭证生成错误 | 提供配置校验工具，预置常用模板 |
| 多账套数据隔离 | 权限混乱 | 所有查询强制带groupid，使用多租户拦截器 |
| 移动加权计算精度 | 尾差累积 | 保留4位小数，定期勾稽校验 |


## 7. 里程碑与交付物

| 里程碑 | 时间 | 交付物 |
|--------|------|--------|
| M1：基础配置上线 | 第1周末 | 采购账户CRUD、供应商收款账户管理 |
| M2：采购订单上线 | 第3周末 | 采购订单创建、付款、退款、入库、凭证生成 |
| M3：台账模块上线 | 第4周末 | 采购账户台账、台账付款、批量对账单 |
| M4：发票模块上线 | 第5周末 | 发票同步、发票入账、供应商台账 |
| M5：库存模块上线 | 第6周中 | 库存汇总账、明细账、勾稽校验 |
| M6：全量联调上线 | 第6周末 | 端到端测试通过，数据迁移完成 |


## 8. 附录：术语表

| 术语 | 说明 |
|------|------|
| 账期账户 | 采购时先挂账，后续通过台账付款实际支付的账户类型 |
| 现金账户 | 采购时直接扣款的账户类型，如银行账户、连连账户 |
| 台账付款 | 账期账户的还款操作，在台账页面批量处理 |
| 归属供应商款项 | 计入供应商应付/已付的金额（如货款、运费），不含手续费等 |
| 非供应商款项 | 不计入供应商应付的金额（如跨境直采手续费） |
| 移动加权平均 | 每笔入库后重新计算库存单价的成本核算方法 |
| 在途库存 | 已付款但尚未入库的存货，在途库存_XX本地仓库科目 |
| 预付账款-ERP采购供应商 | 用于记录已付款但未取得发票的供应商款项 |
| 预付账款-ERP在途发票 | 用于记录已取得发票但未入库的库存 |