库存平均单价计算逻辑说明.md 4.59 KB

库存平均单价计算逻辑说明

概述

采用加权平均成本法(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)

// 计算当前可用库存数量
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)

// 使用产品的平均单价
var unitPrice = product.AveragePrice > 0 ? product.AveragePrice : product.Price;
var totalAmount = unitPrice * usageQuantity;

// 创建使用记录(平均单价不变)
usageEntity.UnitPrice = unitPrice;
usageEntity.TotalAmount = totalAmount;

初始化历史数据

对于已有库存的产品,根据当前库存计算初始平均单价:

平均单价 = SUM(库存金额) / SUM(库存数量)

其中,库存金额优先使用 F_FinalAmount,其次使用 F_PurchaseUnitPrice × F_Quantity

注意事项

  1. 平均单价维护:每次入库时自动更新,出库时不变
  2. 可用库存计算:需要考虑已领取的数量
  3. 普通入库:如果没有采购单价,使用产品价格作为入库单价
  4. 历史数据初始化:首次添加字段时,需要根据现有库存计算初始平均单价
  5. 数据一致性:确保入库和出库操作在事务中执行,保证平均单价的一致性