企业有很多类型的出入库操作,比如:生产入库、采购入库、销售出库、领料出库、调拨入库、调拨出库、盘盈入库、盘亏出库等。企业信息化系统,针对如此繁多的出入库类型,如果每个类型都需要单独实现,则代码量比较大,且不好维护、统一管理。为此,本文引入物料移动类型,统一实现仓库出入库操作。
原始单据可能是:
ware_id | ware_date | goods_id | batch | count | price | money | balance_count | balance_money | bwart_type | ref_billno | ref_detailsid |
---|---|---|---|---|---|---|---|---|---|---|---|
167 | 2019-05-20 | 592 | 0000000 | 2000 | 482.8 | 0.2414 | 2000 | 482.8 | 101 | RK201905200003 | 3450 |
177 | 2019-05-24 | 214 | 20190524SZ10 | 100 | 3018 | 30.18 | 721 | 17261.97 | 103 | RK201905240001 | 3452 |
ware_id: 仓库ID
ware_date: 出入库日期
goods_id: 物料ID
batch:物料批号
count:出入库数量
price:出入库价格
money:出入库金额
balance_count: 本次出入库明细对应的 结存数量
balance_money: 本次出入库明细对应的 结存金额
bwart_type:移动类型
ref_billno:参考单据号
ref_detailsid:参考单据明细sid
需要更新两张表的库存:批次表和仓库库存表。
ware_id | goods_id | batch | balance_count |
---|---|---|---|
167 | 592 | 0000000 | 2000 |
177 | 214 | 20190524SZ10 | 721 |
ware_id | goods_id | balance_count | balance_money | balance_price |
---|---|---|---|---|
167 | 592 | 2000 | 482.8 | 0.2414 |
177 | 214 | 721 | 17261.97 | 23.94 |
物料移动类型本质就是一张配置表,如采购入库对应的物料明细账如何记账,仓库库存如何更新(增加还是减少)等,wm_inout_type,关键信息如下:
biz_type | bwart_type | bwart_name | balance_mark | is_realprice | ref_url |
---|---|---|---|---|---|
入库 | 101 | 采购入库 | + | Y | /views/wm/rk/rk_detail.html |
入库 | 102 | 采购入库冲销 | - | Y | /views/wm/rk/rk_detail.html |
入库 | 103 | 生产入库 | + | N | /views/wm/rk/rk_detail.html |
… |
biz_type: 业务类型
bwart_type:移动类型代码
bwart_name:移动类型名称
balance_mark:库存 增加+/减少- 标记
is_realprice:价格计算依据,Y根据出入库明细影响库存价格,N采用库存物料价格
ref_url:物料明细查看原始单据详情url页面地址
编写一个统一的移动类型出入库函数,如下:
/**
* 移动类型.
* @param Map{bwart_type:string移动类型, rows:list物料明细}
*/
@SuppressWarnings({ "rawtypes", "unchecked" })
public void wmBwart(Map para) {
DataAdapter adapter = new DataAdapter();
String bwart_type = (String) para.get(BWART_KEY);
List<Map> list = (List<Map>) para.get("rows");
// 验证移动类型有效性
this._verifyBwartType(para);
// 验证仓库有效性
this._verifyWare(para);
para.put("ware_id", list.get(0).get("ware_id"));
// 根据移动类型,进行价格、库存增加、减少计算
for (Map n : list) {
Map t = CollectionUtil.cloneMap(n, "sid,ware_id,ware_date,batch,goods_id,goods_name,unit_name,memo");
t.put("is_realprice", bwartObj.get("is_realprice")); //是否采用实际价格
t.put("balance_mark", bwartObj.get("balance_mark"));
// 这里的价格需要根据取出的分子 分母进行调整
t.put("price", n.get("price"));
t.put(BWART_KEY, bwart_type);
t.put("ref_billno", n.get("bill_no"));
t.put("ref_detailsid", n.get("sid"));
t.put("user_id", para.get("user_id"));
t.put("write_time", new Date());
t.put("co_id", para.get("co_id"));
// 出入库明细 数量, 金额
t.put("count", n.get("count"));
t.put("money", n.get("money"));
// 库存影响标记
if ("0".equals(bwartObj.get("balance_mark"))) {
t.put("balance", BigDecimal.ZERO);
t.put("money_balance", BigDecimal.ZERO);
} else if ("+".equals(bwartObj.get("balance_mark"))) {
t.put("balance", n.get("count"));
t.put("money_balance", n.get("money"));
} else if ("-".equals(bwartObj.get("balance_mark"))) {
t.put("balance", BigDecimal.ZERO.subtract(TypeUtil.toBigDecimal(n.get("count"),BigDecimal.ZERO)));
t.put("money_balance",
BigDecimal.ZERO.subtract(TypeUtil.toBigDecimal(n.get("money"),BigDecimal.ZERO)));
}
insertList.add(t);
}
// 物料账期检查
this._wmAccountPeriod(adapter, para);
//出入库操作锁定检查
this._wmLockCheck(adapter, list.get(0));
// 物料明细入账、更新库存
List<Map> listCopy = CollectionUtil.cloneListMap(list);
this._updateWmMainStock(adapter, listCopy); //更新仓库总库存
this._updateWmBatchStock(adapter, listCopy); //更新批次库存
this._insertDetailStock(adapter, listCopy); //插入物料明细账
}
在采购入库、生产入库等具体业务操作里,调用wmBwart(para)即可,以生产入库为例:
public void auditRkBill(Map para) {
DataAdapter adapter = new DataAdapter();
// 更新入库单状态
String sql = "update wm_rk_bill set status = '已入库', write_uid = #user_id#, write_time = now() "
+ "where co_id = #co_id# and bill_no = #bill_no# and status = '录入'";
if (adapter.runSql(sql, para) < 1) {
throw new AppException("请核实该入库单是否已经审核过账?");
}
// 移动类型统一出入库
this.wmBwart(para);
}