在财务或会计中统计库存商品的成本方法有很多种:移动平均,全月平均,先进先出,后进先出,个别计价法,计划成本法,毛利率法,零售价法等。在没有管理软件出现以前移动平均,先进先出,后进先出这几种方法实现都比较困难。自从有了管理软件后,这些实现起来就比较简单了。这篇文章主要思考移动平均法,即每次库存数量的变化都会重新计算平均单价。
在一个ERP或WMS系统中有多种类型的出入库,每种出入库都会对库存商品的成本产生影响。我们主要分为四类,采购类,销售类,领用类,盘点类。
采购类:采购入库及撤销入库,采购退货出库及撤销出库
销售类:销售出库及撤销出库,销售退货入库及撤销入库
领用类:生产领料出库及撤销出库,生产退料入库及撤销入库。
盘点类:盘盈入库及撤销,盘亏出库及撤销。
由于软件本身的特点,所以每个出入库动作都会有撤销的动作,尤其是撤销动作造成的影响很大。并且会存在负库存的情况,有些情况下必须允许负库存的情况存在。
下面我们来分析一下采购入库:当发生采购入库时,移动平均法计算方法是:(本批采购金额+库存金额)/(本批采购数量+库存数量),此时会出现什么情况呢?如果两个数量都为正,不会出现意外;如果两个数量都为负(负库存,及红字采购单)也不会出现意外;但是一正一负时就会出现意外。
如果正负数量刚好相抵,那么新的库存数量为0,但是金额不一定能抵得了,就会出现库存数量为0,但库存金额不为0,这是不合理的,同时这个成本单价都不知如何算(除数为0)。
如果正负数量没有相抵,新的库存数量为负,但新的库存金额为正。算出的单价为负,单价不可能为负。就算取成绝对值,也是不合理的。举例:
原库存:-10个 单价:10元 金额-100元
现入库:8个 单价:13元 金额104元
结果:库存-2,金额4元,算出单价为-2元。取绝对值2元,也是不合理的。
再分析一下撤销采购入库。这个动作有点特殊性,撤销发生的时间点可能是入库后马上发生,也可能是过一段时间才发生。那么在这段时间内,库存商品的成本单价可能早已发生了变化不再是入库时的了。所以仍然会存在如下:
原库存:10个 单价10元 金额100元。
撤销入库:8个 单价13元 金额104元,撤销是做减法
结果:库存2个,金额-4元,算出单价-2元。取绝对值2元,不合理。
其他出入库类不再分析,出结论:即每次出库不能简单的做加减,必须修正。为此在存储库存信息的表中增加一字段记录修正值,我这里用adjust_amount。代码片段为PHP语法。
$new_quantity = $old_quantity + $this_quantity;
$new_amount = $old_amount + $this_amount;
if($new_quantity==0)
{
if($old_quantity>0)//表明新库存数量为0是由于本次入库为负引起的
{
$new_price = $old_price;
}
else//表明新库存数量为0是由于原库存为负引起的,原库存为负意味着出库时暂借了未来的入库,所以单价取未来入库单价,即本次单价
{
$new_price = $this_price;
}
$adjust_amount += $new_amount;//由于库存数量为0,库存金额必须修正为0,有可能库存金额不为0,把他累计到调修正金额字段;
$new_amount = 0;
}
else
{
$new_price = round(($new_amount+$adjust_amount) / $new_quantity,6);//恢复修正金额试算单价
if($new_price>0)//可以恢复
{
$new_amount += $adjust_amount;
$adjust_amount=0;//修正金额归0
}
else//不可恢复
{
$new_price = round($new_amount / $new_quantity,6);//不恢复修正金额试算单价
if($new_price<=0)//需修正单价及金额
{
if($old_quantity>0)//证明负单价是由本次入库引起的
{
$new_price = $old_price;//成本单价取原库存单价
}
else//证明负单价是由原库存引起的
{
$new_price = $this_price;//成本单价取原库存单价
}
$amount = round($new_price*$new_quantity,2);
$adjust_amount += $new_amount-$amount;//计算修正金额并累加库
$new_amount = $amount;
}
}
}
$sqlck = "update ckzk set shuliang='".$new_quantity."',cbdanjia='".$new_price."',cbjine='".$new_amount."',adjust_amount='".$adjust_amount."' where productid='".$productid."' and ckid='".$ckid."'";
$result= $obj->exec( $sqlck );
}
说明:ckzk为存储库存的表,ckid为仓库的ID,productid为商品的ID