1 那些与成本有关
根据企业会计准则,存货成本包括购买价款+相关税费+其他可归属于存货采购成本的费用。其中相关税费指的是属于该存货成本的进口关税、消费税、资源税和不能抵扣的增值税进项税额等,增值税是价外税,因此增值税不会计入到成本中。因此税额与成本计算无关
商品折扣与成本是什么关系呢?商品折扣影响利润,也影响库存商品的价值,因此商品折扣应该计入成本。采购订单中采购明细的折扣可以理解,应该计入成本。
那整单再做优惠,是否影响成本呢?这里的优惠按照现金折扣,计入财务费用损益类科目,因此也不计入到成本中。
2 移动加权平均算法
根据企业会计准则中描述,移动加权平均法是指以每次进货的成本加上原来库存存货的成本,除以每次进货数量+原有库存存货的数量,据以计算加权平均单位成本,作为在下次进货前计算各次发出存货成本依据的一种方法。
存货单位成本=(原有库存存货的实际成本+本次发货前的实际成本)/(原有库存存货数量+本次进货数量)
本次发出存货的成本=本次发出存货的数量*本次发货前的存货单位成本
本月月末库存存货成本=月末库存存货的数量*本月月末存货单位成本
将上面的算法转化为程序,需要考虑单据的新增、修改和删除,不同的操作,算法逻辑有不一样的地方
2.1 新增采购入库
分仓核算,当会计准则转换到程序代码,考虑的情况就有很多了,因为你不确定用户在什么时候会新增或者插入。
private void inAdd(List<PsiIoBalance> psiIoBalances, PsiIoBalance first,String costWay){
// 获取实时库存
PsiInvReal psiInvReal = psiInvRealService.select(first.getAsId(),first.getStockId(),first.getWarehouseId());
/**
* 因录入采购订单日期也是不确定的,因此要取上一条收发明细数据
* 分仓根据仓库取单价,总仓根据所有仓库一起来核算单价
*/
PsiIoBalance nearest = psiIoBalanceService.selectPreNearest(first.getAsId(), first.getBillDate(), first.getStockId()
,OrderConstants.CostWay.MASTER.equals(costWay)?null:first.getWarehouseId());
// 入库累计
BigDecimal inCount = BigDecimal.ZERO;
BigDecimal inCost = BigDecimal.ZERO;
for (PsiIoBalance psiIoBalance: psiIoBalances){
// 结存成本=最近一次收发结存+当前采购数据
psiIoBalance.setFinalCount(nearest.getFinalCount().add(psiIoBalance.getInCount()));
psiIoBalance.setFinalCost(nearest.getFinalCost().add(psiIoBalance.getInCost()));
// 购货时重新计算成本
psiIoBalance.setFinalUnitCost(psiIoBalance.getFinalCount().compareTo(BigDecimal.ZERO)==0?BigDecimal.ZERO:psiIoBalance.getFinalCost().divide(psiIoBalance.getFinalCount()));
// 累计
inCount = inCount.add(psiIoBalance.getInCount());
inCost = inCost.add(psiIoBalance.getInCost());
}
// 实时库存计算
updateReal(psiInvReal,inCount,inCost);
// 保存当前的采购收发明细
psiIoBalanceService.saveBatch(psiIoBalances);
// 向后作用
updateAfter(psiIoBalances.get(psiIoBalances.size()-1),inCount,inCost);
}
2.2 删除采购出库
private void inRemove(List<PsiIoBalance> psiIoBalances, PsiIoBalance first,String costWay){
// 实时库存
PsiInvReal psiInvReal = psiInvRealService.select(first.getAsId(),first.getStockId(),first.getWarehouseId());
BigDecimal count = BigDecimal.ZERO;
BigDecimal cost = BigDecimal.ZERO;
for (PsiIoBalance psiIoBalance: psiIoBalances){
count = count.add(psiIoBalance.getInCount());
cost = cost.add(psiIoBalance.getInCost());
}
// 实时库存
updateReal(psiInvReal,BigDecimal.ZERO.subtract(count),BigDecimal.ZERO.subtract(cost));
//
psiIoBalanceService.delete(psiIoBalances);
// 向后作用,反向操作成本和数量
updateAfter(psiIoBalances.get(psiIoBalances.size()-1),BigDecimal.ZERO.subtract(count),BigDecimal.ZERO.subtract(cost));
}
2.3 编辑采购入库
编辑的情况有一个新旧数据的比对,通过根据这个差额去向后作用,因为当前单据日期之后是有单据的,需要同步更正。
private void inEdit(List<PsiIoBalance> psiIoBalances, PsiIoBalance first,String costWay){
List<String> idList = psiIoBalances.stream().map(p->p.getId()).collect(Collectors.toList());
List<PsiIoBalance> oldPsiIoBalances = psiIoBalanceService.select(first.getAsId(),idList);
Map<String,PsiIoBalance> oldBalanceMap = oldPsiIoBalances.stream().collect(Collectors.toMap(b->b.getId(), b->b));
// 实时库存
PsiInvReal psiInvReal = psiInvRealService.select(first.getAsId(),first.getStockId(),first.getWarehouseId());
// 当前订单的库存
BigDecimal count = BigDecimal.ZERO;
BigDecimal cost = BigDecimal.ZERO;
for (PsiIoBalance newVal: psiIoBalances){
// 原始收发记录
PsiIoBalance oldVal = oldBalanceMap.get(newVal.getId());
// 入库数量前后差额
BigDecimal diffCount = newVal.getInCount().subtract(oldVal.getInCount());
// 入库成本
newVal.setInCost(newVal.getInCount().multiply(newVal.getInUnitCost()).setScale(2,BigDecimal.ROUND_HALF_UP));
// 入库成本差额
BigDecimal diffCost = newVal.getInCost().subtract(oldVal.getInCost());
// 当前库存
count = count.add(diffCount);
cost = cost.add(diffCost);
// 结存数量
newVal.setFinalCount(oldVal.getFinalCount().add(diffCount));
// 结存成本
newVal.setFinalCost(oldVal.getFinalCost().add(diffCost));
// 重新计算结存单位成本
newVal.setFinalUnitCost(newVal.getFinalCount().compareTo(BigDecimal.ZERO)==0?BigDecimal.ZERO:newVal.getFinalCost().divide(newVal.getFinalCount()));
}
// 更新实时库存
updateReal(psiInvReal,count,cost);
// 更新商品收发明细表
psiIoBalanceService.updateBatchById(psiIoBalances);
// 编辑向后作用
updateAfter(psiIoBalances.get(psiIoBalances.size()-1),count,cost);
}
3 先进先出算法
根据企业会计准则,先进先出是指以先购入的存货应先发出(销售或耗用)这样一种存货实物流转假设未前提,兑发出存货进行计价的一种方法。先进先出法下,当期期末存货成本接近于市价,如果存货的市价呈上升趋势则发出成本偏低,会高估企业当期利润和库存存货价值,反之,会低估企业当期利润和库存存货价值。
先进先出使用计算起来,并不简单,因为涉及到可能爻遍历所有的数据,因为要找到每个商品最上一次的商品采购订单,直接取最小日期是不对的,因为销售的时候,你找到的可能已经出库了。
收入存货时:逐笔登记收入存货的数量、单价和金额。
发出存货时:按照先进先出的原则逐笔登记存货的发出成本和结存金额