# 仓库领用成本统计表逻辑梳理 ## 📋 需求概述 创建一个**仓库领用成本统计表**,按**月份**和**门店**两个维度进行统计。 --- ## 🔍 数据来源分析 ### 相关数据表 #### 1. `lq_inventory_usage`(仓库使用记录表) **关键字段**: - `F_Id`:使用记录ID(主键) - `F_StoreId`:门店ID - `F_ProductId`:产品ID - `F_UsageBatchId`:使用批次ID(关联申请表) - `F_TotalAmount`:合计金额(单价×数量) - `F_UsageQuantity`:使用数量 - `F_UnitPrice`:单价 - `F_IsEffective`:是否有效(1-有效,0-无效) **说明**: - 一个申请批次可能包含多条使用记录(多个产品) - 每条记录都有金额(`F_TotalAmount`) #### 2. `lq_inventory_usage_application`(仓库使用申请表) **关键字段**: - `F_Id`:申请编号(主键) - `F_UsageBatchId`:使用批次ID(关联使用记录表) - `F_ApplicationStoreId`:申请门店ID - `F_ApprovalStatus`:审批状态(待审批/审批中/已通过/未通过/已退回) - `F_IsReceived`:是否已领取(1-已领取,0-未领取) - `F_ReceiveTime`:领取时间(**关键时间字段**) - `F_TotalAmount`:申请总金额(该批次所有商品的总价) - `F_IsEffective`:是否有效(1-有效,0-无效) **说明**: - 领取时间(`F_ReceiveTime`)是统计的时间依据 - 只有已领取(`F_IsReceived = 1`)且审批通过(`F_ApprovalStatus = '已通过'`)的记录才计入统计 #### 3. `lq_mdxx`(门店信息表) **关键字段**: - `F_Id`:门店ID - `dm`:门店名称 --- ## 📊 统计逻辑设计 ### 统计维度 1. **月份**:YYYYMM格式(如:202601) - 基于 `lq_inventory_usage_application.F_ReceiveTime`(领取时间) - 使用 `DATE_FORMAT(a.F_ReceiveTime, '%Y%m')` 进行分组 2. **门店**:门店ID(`F_StoreId`) - 来源:`lq_inventory_usage.F_StoreId` - 需要关联 `lq_mdxx` 获取门店名称 ### 统计指标 #### 核心指标(必选) 1. **领用成本金额**(`TotalCostAmount`) - **计算方式**:`SUM(u.F_TotalAmount)` - **含义**:该月份该门店所有已领取的仓库使用记录的总金额 - **数据来源**:`lq_inventory_usage.F_TotalAmount` 2. **领用次数**(`ReceiveCount`) - **计算方式**:`COUNT(DISTINCT a.F_UsageBatchId)` - **含义**:该月份该门店已领取的申请批次数量(按批次去重) - **数据来源**:`lq_inventory_usage_application.F_UsageBatchId` 3. **领用记录数**(`UsageRecordCount`) - **计算方式**:`COUNT(u.F_Id)` - **含义**:该月份该门店的使用记录明细数量(不按批次去重) - **数据来源**:`lq_inventory_usage.F_Id` #### 扩展指标(可选) 4. **平均领用金额**(`AvgReceiveAmount`) - **计算方式**:`TotalCostAmount / ReceiveCount` - **含义**:每次领用的平均金额 5. **平均单笔金额**(`AvgRecordAmount`) - **计算方式**:`TotalCostAmount / UsageRecordCount` - **含义**:每条使用记录的平均金额 6. **领用产品种类数**(`ProductVarietyCount`) - **计算方式**:`COUNT(DISTINCT u.F_ProductId)` - **含义**:该月份该门店领用的不同产品种类数量 --- ## 🔧 SQL 查询逻辑 ### 基础查询结构 ```sql SELECT DATE_FORMAT(a.F_ReceiveTime, '%Y%m') as StatisticsMonth, u.F_StoreId as StoreId, store.dm as StoreName, -- 核心指标 COALESCE(SUM(u.F_TotalAmount), 0) as TotalCostAmount, COUNT(DISTINCT a.F_UsageBatchId) as ReceiveCount, COUNT(u.F_Id) as UsageRecordCount, -- 扩展指标 CASE WHEN COUNT(DISTINCT a.F_UsageBatchId) > 0 THEN COALESCE(SUM(u.F_TotalAmount), 0) / COUNT(DISTINCT a.F_UsageBatchId) ELSE 0 END as AvgReceiveAmount, CASE WHEN COUNT(u.F_Id) > 0 THEN COALESCE(SUM(u.F_TotalAmount), 0) / COUNT(u.F_Id) ELSE 0 END as AvgRecordAmount, COUNT(DISTINCT u.F_ProductId) as ProductVarietyCount FROM lq_inventory_usage u INNER JOIN lq_inventory_usage_application a ON u.F_UsageBatchId = a.F_UsageBatchId LEFT JOIN lq_mdxx store ON u.F_StoreId = store.F_Id WHERE u.F_IsEffective = 1 AND a.F_IsEffective = 1 AND a.F_ApprovalStatus = '已通过' AND a.F_IsReceived = 1 AND a.F_ReceiveTime IS NOT NULL -- 时间筛选(可选) -- AND DATE_FORMAT(a.F_ReceiveTime, '%Y%m') = '202601' -- 门店筛选(可选) -- AND u.F_StoreId = '门店ID' GROUP BY DATE_FORMAT(a.F_ReceiveTime, '%Y%m'), u.F_StoreId, store.dm ORDER BY StatisticsMonth DESC, TotalCostAmount DESC ``` ### 筛选条件说明 1. **有效性筛选**: - `u.F_IsEffective = 1`:使用记录有效 - `a.F_IsEffective = 1`:申请记录有效 2. **状态筛选**: - `a.F_ApprovalStatus = '已通过'`:只统计审批通过的申请 - `a.F_IsReceived = 1`:只统计已领取的记录 3. **时间筛选**: - `a.F_ReceiveTime IS NOT NULL`:领取时间不能为空 - 基于 `F_ReceiveTime` 进行月份分组和筛选 --- ## 📈 统计场景 ### 场景1:查询指定月份所有门店的统计 **输入参数**: - `statisticsMonth`:统计月份(YYYYMM格式,如:202601) **返回数据**: - 该月份所有有领用记录的门店统计数据 - 按领用成本金额降序排列 ### 场景2:查询指定门店所有月份的统计 **输入参数**: - `storeId`:门店ID **返回数据**: - 该门店所有月份的统计数据 - 按月份降序排列 ### 场景3:查询指定月份和门店的统计 **输入参数**: - `statisticsMonth`:统计月份(YYYYMM格式) - `storeId`:门店ID **返回数据**: - 该月份该门店的统计数据(单条记录) ### 场景4:查询时间范围内的统计(按月汇总) **输入参数**: - `startMonth`:开始月份(YYYYMM格式) - `endMonth`:结束月份(YYYYMM格式) - `storeIds`:门店ID列表(可选) **返回数据**: - 时间范围内所有月份的统计数据 - 可按门店筛选 --- ## 🎯 接口实现 ### 接口信息 **接口路径**:`POST /api/Extend/LqInventoryUsage/get-store-receive-cost-statistics` **请求参数**: ```json { "statisticsMonth": "202601", // 可选,YYYYMM格式(优先使用) "startMonth": "202601", // 可选,开始月份(YYYYMM,与endMonth配合使用) "endMonth": "202612", // 可选,结束月份(YYYYMM,与startMonth配合使用) "storeId": "门店ID", // 可选,单个门店查询 "storeIds": ["门店ID1", "门店ID2"], // 可选,多个门店查询 "warehouse": "仓库名称", // 可选,筛选特定仓库 "currentPage": 1, // 可选,分页参数,默认1 "pageSize": 20, // 可选,分页参数,默认20 "sidx": "StatisticsMonth", // 可选,排序字段 "sort": "desc" // 可选,排序方式(asc/desc) } ``` **返回结构**: ```json { "code": 200, "data": { "list": [ { "StatisticsMonth": "202601", "StoreId": "门店ID", "StoreName": "门店名称", "ReceiveCount": 10, "ProductVarietyCount": 8, "AvgUnitPrice": 1543.21, "TotalCostAmount": 12345.67, "WarehouseCount": 2, "UsageRecordCount": 25, "AvgReceiveAmount": 1234.57, "AvgRecordAmount": 493.83, "TotalUsageQuantity": 150, "MaxRecordAmount": 5000.00, "MinRecordAmount": 50.00 } ], "pagination": { "total": 100, "pageIndex": 1, "pageSize": 20 } }, "message": "查询成功" } ``` ### 返回字段说明 | 字段名 | 类型 | 说明 | |--------|------|------| | **StatisticsMonth** | string | 统计月份(YYYYMM格式) | | **StoreId** | string | 门店ID | | **StoreName** | string | 门店名称 | | **ReceiveCount** | int | 领取数量(按批次去重,即领用次数) | | **ProductVarietyCount** | int | 领取品种数量(产品种类数) | | **AvgUnitPrice** | decimal | 平均单价(总价/领取品种数量) | | **TotalCostAmount** | decimal | 总价(领用成本金额) | | **WarehouseCount** | int | 涉及仓库数量(不同仓库的数量) | | **UsageRecordCount** | int | 领用记录数(明细记录数,不按批次去重) | | **AvgReceiveAmount** | decimal | 平均领用金额(总价/领取数量) | | **AvgRecordAmount** | decimal | 平均单笔金额(总价/领用记录数) | | **TotalUsageQuantity** | int | 领用总数量(所有产品的数量总和) | | **MaxRecordAmount** | decimal | 最大单笔金额 | | **MinRecordAmount** | decimal | 最小单笔金额 | --- ## ⚠️ 注意事项 ### 1. 时间字段选择 - ✅ **使用 `F_ReceiveTime`(领取时间)**:这是财务统计的正确时间点 - ❌ **不使用 `F_UsageTime`(使用时间)**:使用时间可能早于领取时间,不符合财务统计逻辑 ### 2. 数据完整性 - 必须关联申请表,因为领取时间和状态在申请表中 - 必须筛选 `F_IsReceived = 1`,只统计已领取的记录 - 必须筛选 `F_ApprovalStatus = '已通过'`,只统计审批通过的记录 ### 3. 金额计算 - 使用 `lq_inventory_usage.F_TotalAmount`(每条使用记录的金额) - 不要使用 `lq_inventory_usage_application.F_TotalAmount`(申请总金额),因为可能存在数据不一致 ### 4. 去重逻辑 - **领用次数**:按批次ID去重(`COUNT(DISTINCT a.F_UsageBatchId)`) - **领用记录数**:不去重(`COUNT(u.F_Id)`) - **产品种类数**:按产品ID去重(`COUNT(DISTINCT u.F_ProductId)`) ### 5. 门店信息关联 - 使用 `LEFT JOIN` 关联 `lq_mdxx` 表获取门店名称 - 如果门店不存在,门店名称为空或显示"未知门店" --- ## 📝 参考实现 可以参考以下现有实现: 1. **`LqStoreManagerSalaryService.CalculateStoreManagerSalary`**(第335-347行) - 产品物料统计逻辑 - 基于领取时间的月份筛选 2. **`LqShareStatisticsStoreService.CalculateCost`**(第323-334行) - 产品成本统计逻辑 - 基于领取时间的时间范围筛选 3. **`LqInventoryUsageService.GetStoreReceiveStatisticsAsync`**(第1642行开始) - 门店领取统计逻辑 - 可以参考其查询结构 --- ## ✅ 实现可行性 ### 数据完整性 - ✅ 数据表结构完整 - ✅ 关联关系清晰(通过 `F_UsageBatchId`) - ✅ 时间字段存在(`F_ReceiveTime`) - ✅ 状态字段完整(`F_IsReceived`、`F_ApprovalStatus`) ### 统计逻辑 - ✅ 可以按月份分组(`DATE_FORMAT(a.F_ReceiveTime, '%Y%m')`) - ✅ 可以按门店分组(`u.F_StoreId`) - ✅ 可以计算金额总和(`SUM(u.F_TotalAmount)`) - ✅ 可以统计次数(`COUNT(DISTINCT a.F_UsageBatchId)`) ### 性能考虑 - ✅ 建议在 `F_ReceiveTime` 和 `F_StoreId` 上建立索引 - ✅ 建议在 `F_UsageBatchId` 上建立索引(关联查询) - ✅ 大数据量时建议使用分页查询 --- ## 🎯 总结 **仓库领用成本统计表**可以实现,核心逻辑如下: 1. **数据来源**:`lq_inventory_usage` + `lq_inventory_usage_application` + `lq_mdxx` 2. **统计维度**:月份(YYYYMM)+ 门店(StoreId) 3. **时间依据**:`F_ReceiveTime`(领取时间) 4. **筛选条件**:已领取 + 审批通过 + 有效记录 5. **核心指标**:领用成本金额、领用次数、领用记录数 **建议实现方案**:创建一个新的接口,支持按月份、门店、时间范围等多种查询方式,返回分页数据。