# 仓库费用计算时间字段修改梳理 - 从使用时间改为领取时间 ## 📋 修改目标 将所有使用**使用时间(UsageTime)**进行仓库费用计算的地方,改为使用**领取时间(ReceiveTime)**,并且只统计**已领取(IsReceived = 1)**的记录。 --- ## 🔍 需要修改的地方 ### 一、薪酬计算相关 #### 1.1 店长工资计算(LqStoreManagerSalaryService.cs) **文件位置**: `netcore/src/Modularity/Extend/NCC.Extend/LqStoreManagerSalaryService.cs` **方法**: `CalculateStoreManagerSalary` **行数**: 第317-342行 **当前逻辑**: ```csharp // 1.9 产品物料统计(仓库领用金额,使用上月数据) var queryMonth = monthStr; if (month == 1) { queryMonth = $"{year - 1}12"; } else { queryMonth = $"{year}{(month - 1):D2}"; } var productMaterialSql = $@" SELECT F_StoreId as StoreId, COALESCE(SUM(F_TotalAmount), 0) as MaterialAmount FROM lq_inventory_usage WHERE F_IsEffective = 1 AND DATE_FORMAT(F_UsageTime, '%Y%m') = @queryMonth GROUP BY F_StoreId"; ``` **需要修改为**: ```csharp // 1.9 产品物料统计(仓库领用金额,使用上月数据,基于领取时间) var queryMonth = monthStr; if (month == 1) { queryMonth = $"{year - 1}12"; } else { queryMonth = $"{year}{(month - 1):D2}"; } var productMaterialSql = $@" SELECT u.F_StoreId as StoreId, COALESCE(SUM(u.F_TotalAmount), 0) as MaterialAmount FROM lq_inventory_usage u INNER JOIN lq_inventory_usage_application a ON u.F_UsageBatchId = a.F_UsageBatchId 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') = @queryMonth GROUP BY u.F_StoreId"; ``` **影响**: 店长工资计算中的产品物料(用于毛利计算) --- #### 1.2 主任工资计算(LqDirectorSalaryService.cs) **文件位置**: `netcore/src/Modularity/Extend/NCC.Extend/LqDirectorSalaryService.cs` **方法**: `CalculateDirectorSalary` **行数**: 第363-388行 **当前逻辑**: ```csharp // 1.9 产品物料统计(仓库领用金额,使用上月数据) var queryMonth = monthStr; if (month == 1) { queryMonth = $"{year - 1}12"; } else { queryMonth = $"{year}{(month - 1):D2}"; } var productMaterialSql = $@" SELECT F_StoreId as StoreId, COALESCE(SUM(F_TotalAmount), 0) as MaterialAmount FROM lq_inventory_usage WHERE F_IsEffective = 1 AND DATE_FORMAT(F_UsageTime, '%Y%m') = @queryMonth GROUP BY F_StoreId"; ``` **需要修改为**: ```csharp // 1.9 产品物料统计(仓库领用金额,使用上月数据,基于领取时间) var queryMonth = monthStr; if (month == 1) { queryMonth = $"{year - 1}12"; } else { queryMonth = $"{year}{(month - 1):D2}"; } var productMaterialSql = $@" SELECT u.F_StoreId as StoreId, COALESCE(SUM(u.F_TotalAmount), 0) as MaterialAmount FROM lq_inventory_usage u INNER JOIN lq_inventory_usage_application a ON u.F_UsageBatchId = a.F_UsageBatchId 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') = @queryMonth GROUP BY u.F_StoreId"; ``` **影响**: 主任工资计算中的产品物料(用于毛利计算) --- #### 1.3 事业部总经理/经理工资计算(LqBusinessUnitManagerSalaryService.cs) **文件位置**: `netcore/src/Modularity/Extend/NCC.Extend/LqBusinessUnitManagerSalaryService.cs` **方法**: `CalculateBusinessUnitManagerSalary` **行数**: 第275-300行 **当前逻辑**: ```csharp // 1.7 产品物料统计(仓库领用金额,使用上月数据) var queryMonth = monthStr; if (month == 1) { queryMonth = $"{year - 1}12"; } else { queryMonth = $"{year}{(month - 1):D2}"; } var productMaterialSql = $@" SELECT F_StoreId as StoreId, COALESCE(SUM(F_TotalAmount), 0) as MaterialAmount FROM lq_inventory_usage WHERE F_IsEffective = 1 AND DATE_FORMAT(F_UsageTime, '%Y%m') = @queryMonth GROUP BY F_StoreId"; ``` **需要修改为**: ```csharp // 1.7 产品物料统计(仓库领用金额,使用上月数据,基于领取时间) var queryMonth = monthStr; if (month == 1) { queryMonth = $"{year - 1}12"; } else { queryMonth = $"{year}{(month - 1):D2}"; } var productMaterialSql = $@" SELECT u.F_StoreId as StoreId, COALESCE(SUM(u.F_TotalAmount), 0) as MaterialAmount FROM lq_inventory_usage u INNER JOIN lq_inventory_usage_application a ON u.F_UsageBatchId = a.F_UsageBatchId 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') = @queryMonth GROUP BY u.F_StoreId"; ``` **影响**: 事业部总经理/经理工资计算中的产品物料(用于毛利计算) --- ### 二、股份统计相关 #### 2.1 门店股份统计 - 产品成本(LqShareStatisticsStoreService.cs) **文件位置**: `netcore/src/Modularity/Extend/NCC.Extend/LqShareStatisticsStoreService.cs` **方法**: `CalculateCost` **行数**: 第323-326行 **当前逻辑**: ```csharp // 1. 产品成本 = 仓库领用金额 entity.CostProduct = await _db.Queryable() .Where(x => x.StoreId == entity.StoreId && x.UsageTime >= startDate && x.UsageTime <= endDate && x.IsEffective == 1) .SumAsync(x => x.TotalAmount); ``` **需要修改为**: ```csharp // 1. 产品成本 = 仓库领用金额(基于领取时间,只统计已领取的记录) entity.CostProduct = await _db.Queryable( (u, a) => u.UsageBatchId == a.UsageBatchId) .Where((u, a) => u.StoreId == entity.StoreId && u.IsEffective == 1 && a.IsEffective == 1 && a.ApprovalStatus == "已通过" && a.IsReceived == 1 && a.ReceiveTime.HasValue && a.ReceiveTime.Value >= startDate && a.ReceiveTime.Value <= endDate) .SumAsync((u, a) => u.TotalAmount); ``` **影响**: 门店股份统计中的产品成本 --- #### 2.2 门店股份统计 - 福田成本(LqShareStatisticsStoreService.cs) **文件位置**: `netcore/src/Modularity/Extend/NCC.Extend/LqShareStatisticsStoreService.cs` **方法**: `CalculateCost` **行数**: 第328-344行 **当前逻辑**: ```csharp // 2. 福田成本 = 福田仓库领用(上一个月的成本) var prevMonthStart = startDate.AddMonths(-1); var prevMonthEnd = startDate.AddDays(-1); entity.CostFutian = await _db.Queryable( (u, p, a) => new JoinQueryInfos( JoinType.Inner, u.ProductId == p.Id, JoinType.Inner, u.UsageBatchId == a.UsageBatchId)) .Where((u, p, a) => p.Warehouse == "福田仓库" && u.StoreId == entity.StoreId && u.UsageTime >= prevMonthStart && u.UsageTime <= prevMonthEnd && u.IsEffective == 1 && a.IsEffective == 1 && a.ApprovalStatus == "已通过" && a.IsReceived == 1) .SumAsync((u, p, a) => u.TotalAmount); ``` **需要修改为**: ```csharp // 2. 福田成本 = 福田仓库领用(上一个月的成本,基于领取时间) var prevMonthStart = startDate.AddMonths(-1); var prevMonthEnd = startDate.AddDays(-1); entity.CostFutian = await _db.Queryable( (u, p, a) => new JoinQueryInfos( JoinType.Inner, u.ProductId == p.Id, JoinType.Inner, u.UsageBatchId == a.UsageBatchId)) .Where((u, p, a) => p.Warehouse == "福田仓库" && u.StoreId == entity.StoreId && u.IsEffective == 1 && a.IsEffective == 1 && a.ApprovalStatus == "已通过" && a.IsReceived == 1 && a.ReceiveTime.HasValue && a.ReceiveTime.Value >= prevMonthStart && a.ReceiveTime.Value <= prevMonthEnd) .SumAsync((u, p, a) => u.TotalAmount); ``` **影响**: 门店股份统计中的福田成本 **注意**: 这个已经关联了申请表,但使用的是 `UsageTime`,需要改为 `ReceiveTime` --- ## 📊 数据关联关系 ### 表结构关系 ``` lq_inventory_usage (使用记录表) ├── F_UsageBatchId (使用批次ID) └── F_TotalAmount (合计金额) lq_inventory_usage_application (申请表) ├── F_UsageBatchId (使用批次ID) ← 关联字段 ├── F_IsReceived (是否已领取: 1-已领取, 0-未领取) ├── F_ReceiveTime (领取时间) └── F_ApprovalStatus (审批状态: "已通过") ``` ### 关联逻辑 - **使用记录表** (`lq_inventory_usage`) 通过 `F_UsageBatchId` 关联 **申请表** (`lq_inventory_usage_application`) - **领取时间** (`F_ReceiveTime`) 在申请表中 - **是否已领取** (`F_IsReceived`) 在申请表中 --- ## 🔧 修改要点 ### 1. SQL查询修改要点 **原查询**: ```sql FROM lq_inventory_usage WHERE F_IsEffective = 1 AND DATE_FORMAT(F_UsageTime, '%Y%m') = @queryMonth ``` **新查询**: ```sql FROM lq_inventory_usage u INNER JOIN lq_inventory_usage_application a ON u.F_UsageBatchId = a.F_UsageBatchId 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') = @queryMonth ``` ### 2. ORM查询修改要点 **原查询**: ```csharp await _db.Queryable() .Where(x => x.StoreId == storeId && x.UsageTime >= startDate && x.UsageTime <= endDate && x.IsEffective == 1) .SumAsync(x => x.TotalAmount); ``` **新查询**: ```csharp await _db.Queryable( (u, a) => u.UsageBatchId == a.UsageBatchId) .Where((u, a) => u.StoreId == storeId && u.IsEffective == 1 && a.IsEffective == 1 && a.ApprovalStatus == "已通过" && a.IsReceived == 1 && a.ReceiveTime.HasValue && a.ReceiveTime.Value >= startDate && a.ReceiveTime.Value <= endDate) .SumAsync((u, a) => u.TotalAmount); ``` --- ## 📝 修改清单 ### 薪酬计算(3处) | 序号 | 文件 | 方法 | 行数 | 说明 | |------|------|------|------|------| | 1 | `LqStoreManagerSalaryService.cs` | `CalculateStoreManagerSalary` | 330-337 | 店长工资 - 产品物料计算 | | 2 | `LqDirectorSalaryService.cs` | `CalculateDirectorSalary` | 376-383 | 主任工资 - 产品物料计算 | | 3 | `LqBusinessUnitManagerSalaryService.cs` | `CalculateBusinessUnitManagerSalary` | 288-295 | 事业部总经理/经理工资 - 产品物料计算 | ### 股份统计(2处) | 序号 | 文件 | 方法 | 行数 | 说明 | |------|------|------|------|------| | 4 | `LqShareStatisticsStoreService.cs` | `CalculateCost` | 324-326 | 门店股份统计 - 产品成本 | | 5 | `LqShareStatisticsStoreService.cs` | `CalculateCost` | 334-344 | 门店股份统计 - 福田成本 | --- ## ⚠️ 注意事项 ### 1. 数据完整性 - 确保所有使用记录都有对应的申请记录(通过 `UsageBatchId` 关联) - 如果使用记录没有对应的申请记录,这些记录将不会被统计(符合"未领取不计算"的要求) ### 2. 时间规则保持不变 - **薪酬计算**: 仍然使用上月数据(计算12月工资,使用11月的领取时间数据) - **股份统计**: - 产品成本:使用当月数据(基于领取时间) - 福田成本:使用上月数据(基于领取时间) ### 3. 筛选条件 必须同时满足以下条件: - ✅ 使用记录有效 (`u.F_IsEffective = 1`) - ✅ 申请记录有效 (`a.F_IsEffective = 1`) - ✅ 审批状态为"已通过" (`a.F_ApprovalStatus = '已通过'`) - ✅ 已领取 (`a.F_IsReceived = 1`) - ✅ 领取时间不为空 (`a.F_ReceiveTime IS NOT NULL`) - ✅ 领取时间在指定月份范围内 ### 4. 旧数据处理 - 对于没有申请记录的历史数据(旧数据),将不会被统计 - 这是符合业务逻辑的:如果没有领取,就不应该计入成本 --- ## 🧪 测试建议 ### 1. 单元测试 - 测试已领取的记录是否被正确统计 - 测试未领取的记录是否被排除 - 测试审批未通过的记录是否被排除 - 测试领取时间为空的记录是否被排除 - 测试时间范围筛选是否正确 ### 2. 集成测试 - 测试店长工资计算:产品物料是否正确 - 测试主任工资计算:产品物料是否正确 - 测试事业部总经理/经理工资计算:产品物料是否正确 - 测试门店股份统计:产品成本和福田成本是否正确 ### 3. 数据验证 - 对比修改前后的计算结果 - 验证只有已领取的记录被统计 - 验证时间筛选基于领取时间而不是使用时间 --- ## 📋 修改步骤 1. **修改店长工资计算** (`LqStoreManagerSalaryService.cs`) 2. **修改主任工资计算** (`LqDirectorSalaryService.cs`) 3. **修改事业部总经理/经理工资计算** (`LqBusinessUnitManagerSalaryService.cs`) 4. **修改门店股份统计 - 产品成本** (`LqShareStatisticsStoreService.cs`) 5. **修改门店股份统计 - 福田成本** (`LqShareStatisticsStoreService.cs`) 6. **测试验证** 7. **更新相关文档** --- **文档版本**: v1.0 **创建日期**: 2026-01-09 **适用范围**: 仓库费用计算时间字段修改