c63026bc
“wangming”
feat: 优化工资计算逻辑
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
|
# 库存平均单价计算逻辑说明
## 概述
采用**加权平均成本法(Weighted Average Cost Method)**来计算库存的平均单价,用于出库计价。
## 核心原则
1. **入库时**:根据当前平均单价和入库单价,计算新的平均单价
2. **出库时**:使用当前平均单价,出库后平均单价不变
3. **平均单价维护**:存储在 `lq_product.F_AveragePrice` 字段中
## 计算公式
### 入库时计算新平均单价
```
新平均单价 = (当前平均单价 × 当前可用库存数量 + 入库单价 × 入库数量) / (当前可用库存数量 + 入库数量)
```
其中:
- **当前可用库存数量** = 总库存数量 - 已领取数量
- **入库单价**:
- 采购入库:使用采购单价(`F_PurchaseUnitPrice`)
- 普通入库:使用产品价格(`F_Price`)
### 出库时使用平均单价
```
出库单价 = 产品平均单价(F_AveragePrice)
出库总金额 = 出库单价 × 出库数量
```
**注意**:出库后,平均单价不变(因为已经领取的库存已经按照当时的平均单价计价了)
## 示例说明
### 示例1:基本流程
**初始状态**:
- 商品A:当前平均单价 = 150
- 库存A:数量1,单价100
- 库存B:数量1,单价200
**步骤1:领取1个**
- 使用平均单价:150
- 领取后剩余:1个库存
- 平均单价不变:150
**步骤2:再次入库1个,单价50**
- 当前可用库存数量:1
- 当前库存总金额:150 × 1 = 150
- 入库金额:50 × 1 = 50
- **新平均单价** = (150 + 50) / (1 + 1) = **100**
### 示例2:多次入库
**初始状态**:
- 商品B:当前平均单价 = 100,当前可用库存 = 10
**步骤1:采购入库5个,单价120**
- 当前库存总金额:100 × 10 = 1000
- 入库金额:120 × 5 = 600
- **新平均单价** = (1000 + 600) / (10 + 5) = **106.67**
**步骤2:领取3个**
- 使用平均单价:106.67
- 领取后剩余:12个库存
- 平均单价不变:106.67
**步骤3:再次采购入库8个,单价110**
- 当前可用库存数量:12
- 当前库存总金额:106.67 × 12 = 1280.04
- 入库金额:110 × 8 = 880
- **新平均单价** = (1280.04 + 880) / (12 + 8) = **108.00**
## 数据库字段
### 产品表(lq_product)
- `F_AveragePrice`:平均单价(加权平均成本,用于出库计价)
### 库存表(lq_inventory)
- `F_PurchaseUnitPrice`:采购单价(采购入库时使用)
- `F_FinalAmount`:产品最终金额(优先用于计算单价)
- `F_Quantity`:库存数量
### 库存使用记录表(lq_inventory_usage)
- `F_UnitPrice`:使用时的单价(从产品平均单价获取)
- `F_TotalAmount`:使用总金额(单价 × 数量)
## 实现逻辑
### 1. 入库时(LqInventoryService.CreateAsync / UpdateAsync)
```csharp
// 计算当前可用库存数量
var currentAvailableQuantity = totalInventoryQuantity - totalUsageQuantity;
// 确定入库单价
decimal incomingPrice = stockInType == 2 && purchaseUnitPrice.HasValue
? purchaseUnitPrice.Value // 采购入库
: product.Price; // 普通入库
// 计算新平均单价
if (currentAvailableQuantity <= 0)
{
newAveragePrice = incomingPrice; // 没有库存时,直接使用入库单价
}
else
{
var currentTotalAmount = product.AveragePrice * currentAvailableQuantity;
var incomingAmount = incomingPrice * incomingQuantity;
newAveragePrice = (currentTotalAmount + incomingAmount) / (currentAvailableQuantity + incomingQuantity);
}
// 更新产品平均单价
product.AveragePrice = newAveragePrice;
```
### 2. 出库时(LqInventoryUsageService.CreateAsync / ConfirmReceiveAsync)
```csharp
// 使用产品的平均单价
var unitPrice = product.AveragePrice > 0 ? product.AveragePrice : product.Price;
var totalAmount = unitPrice * usageQuantity;
// 创建使用记录(平均单价不变)
usageEntity.UnitPrice = unitPrice;
usageEntity.TotalAmount = totalAmount;
```
## 初始化历史数据
对于已有库存的产品,根据当前库存计算初始平均单价:
```sql
平均单价 = SUM(库存金额) / SUM(库存数量)
```
其中,库存金额优先使用 `F_FinalAmount`,其次使用 `F_PurchaseUnitPrice × F_Quantity`。
## 注意事项
1. **平均单价维护**:每次入库时自动更新,出库时不变
2. **可用库存计算**:需要考虑已领取的数量
3. **普通入库**:如果没有采购单价,使用产品价格作为入库单价
4. **历史数据初始化**:首次添加字段时,需要根据现有库存计算初始平均单价
5. **数据一致性**:确保入库和出库操作在事务中执行,保证平均单价的一致性
|