package book.stock; import java.math.BigDecimal; import java.util.Date; import java.util.List; import java.util.Set; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import foundation.action.ActionProvider; import foundation.dao.DataPackage; import foundation.dao.DataSource; import foundation.dao.DataWriter; import foundation.dao.Filter; import foundation.data.entity.Entity; import foundation.data.entity.EntitySet; import foundation.data.object.DataObject; import foundation.data.object.EntitySaver; import foundation.persist.NamedSQL; import foundation.persist.SQLRunner; import foundation.util.ID; import foundation.util.Util; public class BookWriter extends ActionProvider { private static Logger logger; public static final String FieldName_BatchMark = "batch_mark"; static { logger = LogManager.getLogger(BookWriter.class); } public BookWriter() { } @Override protected void publishMethod() { //1. 获取库存记账描述列表 addMethod("getBookCommandList"); //2. 获取库存记账描述 addMethod("getBookCommand"); //3. 记账 addMethod("write"); } public void getBookCommandList() { BookCommandBucket bucket = BookCommandBucket.getInstance(); dataWriter.addValue("data", bucket); } public void getBookCommand() { String commandCode = dataReader.getString("code"); List metas = BookCommandBucket.getMeta(commandCode); if (metas == null) { logger.error("book command [{}] can not find: ", commandCode); return; } dataWriter.addValue("data", metas); } public void write() throws Exception { String commandCode = context.getStepParam(); List bookCommands = BookCommandBucket.get(commandCode); if (bookCommands == null || (bookCommands.isEmpty())) { throw new Exception("book command [{" + commandCode + "}] can not find: "); } DataPackage dataPackage = dataReader.getDataPackage(); //2. 获取公司及仓库 for (BookCommand bookCommand: bookCommands) { getOrgAndWarehouse(dataPackage, bookCommand); } //3. 账本加锁 lockStockBook(bookCommands); //4. 创建流水表(FlowTable)并进行校验 boolean isContinue = true; for (BookCommand bookCommand: bookCommands) { isContinue = isContinue && getFlowTableAndValidate(bookCommand, bookCommand.getDataSource()); } if (!isContinue) { logger.error("validate not success, skip..."); return; } //5. 根据Command和FlowTable记账 for (BookCommand bookCommand: bookCommands) { doWriteBook(bookCommand); } } private void getOrgAndWarehouse(DataPackage document, BookCommand bookCommand) throws Exception { DataWriter dataWriter = context.getDataWriter(); //1. 设置单据到Command bookCommand.setDocument(document); //2. 获取公司 Entity entity = document.getMasterEntity(); String direction = bookCommand.getWarehouseDirection(); String accountField = "account_id"; String orgField = "customer_id"; String org_id = entity.getString(orgField); if (Util.isEmpty(org_id)) { orgField = accountField; } if (!Util.isEmpty(direction) && "to".equalsIgnoreCase(direction)) { orgField = direction + "_" + orgField; } String customer_id = entity.getString(orgField); if (Util.isEmpty(customer_id)) { task.terminate(); dataWriter.reportOneError("stock.write", "客户ID没有获取"); return; } DataObject dataObject = DataObject.getInstance("md_org_account"); Entity org = dataObject.getTableEntity(customer_id); bookCommand.setOrg(org); if (org == null) { task.terminate(); dataWriter.reportOneError("stock.write", "客户没有获取"); return; } //3. 获取仓库 dataObject = DataObject.getInstance("wm_warehouse"); Entity warehouse = null; String warehouseField = "warehouse_id"; if (!Util.isEmpty(direction)) { warehouseField = direction + "_" + warehouseField; } String warehouseId = entity.getString(warehouseField); if (!Util.isEmpty(warehouseId)) { warehouse = dataObject.getTableEntity(warehouseId); } else { Filter filter = new Filter("account_id", customer_id).add("type_code", "Master"); warehouse = dataObject.getTableEntity(filter); } if ((warehouse == null || warehouse.isEmpty()) && Util.isEmpty(bookCommand.getWarehouseSource())) { dataWriter.reportOneError("库存记账", "仓库不存在"); task.terminate(); return; } bookCommand.setWarehouse(warehouse); } private void lockStockBook(List bookCommands) throws Exception { for (BookCommand bookCommand: bookCommands) { NamedSQL lockStockBook = NamedSQL.getInstance("lockStockBook"); lockStockBook.setParam("batch_mark", bookCommand.getBatchMark()); lockStockBook.setParam("detailTableName", bookCommand.getDocumentDetailTableName()); lockStockBook.setParam("document_id", bookCommand.getDocumentId()); lockStockBook.setParam("warehouse_id", bookCommand.getWarehouseID()); SQLRunner.execSQL(lockStockBook); } } private boolean getFlowTableAndValidate(BookCommand bookCommand, DataSource dataSource) throws Exception { if (DataSource.File == dataSource) { return getFlowTableAndValidateFromFile(bookCommand); } return getFlowTableAndValidate(bookCommand); } private boolean getFlowTableAndValidate(BookCommand bookCommand) throws Exception { boolean result = true; String value = null; Entity warehouse = bookCommand.getWarehouse(); boolean detailedWarehouse = bookCommand.getDetailedWarehouse(); //1. 根据【单据】+【明细账】获取记账工作表 NamedSQL getStockBookFlow = NamedSQL.getInstance("getStockFlowBasicFields"); getStockBookFlow.setParam("tableName", bookCommand.getDocumentTableName()); getStockBookFlow.setParam("detailTableName", bookCommand.getDocumentDetailTableName()); getStockBookFlow.setParam("document_id", bookCommand.getDocumentId()); getStockBookFlow.setParam("org_id", bookCommand.getOrgID()); getStockBookFlow.setParam("batch_mark", bookCommand.getBatchMark()); if (detailedWarehouse) { getStockBookFlow.setParam("warehouse_id", bookCommand.getWarehouseSource() + "_id"); getStockBookFlow.setParam("warehouse_code", bookCommand.getWarehouseSource() + "_code"); getStockBookFlow.setParam("warehouse_name", bookCommand.getWarehouseSource() + "_name"); } else { getStockBookFlow.setParam("warehouse_id", Util.quotedStr(warehouse.getId())); getStockBookFlow.setParam("warehouse_code", Util.quotedStr(warehouse.getString("code"))); getStockBookFlow.setParam("warehouse_name", Util.quotedStr(warehouse.getString("name"))); } getStockBookFlow.setParam("document_doc_type", bookCommand.getDocumentDocTypeSegment()); getStockBookFlow.setParam("document_order_right", bookCommand.getDocumentOrderRightSegment()); getStockBookFlow.setParam("document_stock_type", bookCommand.getDocumentStockTypeSegment()); //@he getStockBookFlow.setParam("document_qty", IStockLetter.Table_DocumentDetail + "." + bookCommand.getDocumentQtySegment()); Filter filter = new Filter(); filter.add("document_detail.parent_id", bookCommand.getDocumentId()); filter.add(bookCommand.getFilter()); getStockBookFlow.setFilter(filter); //1.6. 获取数据 EntitySet entitySet = SQLRunner.getEntitySet(getStockBookFlow); //1.6.1 植入撤销调整获取明细数据 if (entitySet == null || entitySet.isEmpty()) { NamedSQL getStockBookImplatnFlow = NamedSQL.getInstance("getStockFlowBasicFieldsByImplantCancel"); Set nameList = getStockBookFlow.getVariantNames(); for (String name : nameList) { value = getStockBookFlow.getParam(name); getStockBookImplatnFlow.setParam(name, value); } getStockBookImplatnFlow.setParam("host_id", bookCommand.getDocument().getMasterEntity().getString("host_id", "")); filter = new Filter(); filter.add("document.host_id", bookCommand.getDocument().getMasterEntity().getString("host_id", "")); filter.add(bookCommand.getFilter()); getStockBookImplatnFlow.setFilter(filter); entitySet = SQLRunner.getEntitySet(getStockBookImplatnFlow); } FlowTable flowTable = bookCommand.getFlowTable(); flowTable.setEntitySet(entitySet); return result; } private boolean getFlowTableAndValidateFromFile(BookCommand bookCommand) throws Exception { boolean result = true; DataPackage dataPackage = bookCommand.getDocument(); DataPackage host = dataPackage.getHost(); host.loadOneDataFromFile(); bookCommand.setDocument(host); String detailName = bookCommand.getDocumentDetailTableName(); Entity master = dataPackage.getMasterEntity(); Entity hostMaster = host.getMasterEntity(DataSource.File); EntitySet detailSet = host.getItemEntitySet(detailName, DataSource.File); NamedSQL namedSQL = NamedSQL.getInstance("getFlowFields"); namedSQL.setTableName(IStockLetter.Table_BookFlow); EntitySet flowSet = SQLRunner.getEntitySet(namedSQL); Entity oneFlow; boolean detailedWarehouse = bookCommand.getDetailedWarehouse(); for (Entity entity: detailSet) { BigDecimal qty = entity.getBigDecimal(bookCommand.getDocumentQtySegment(), BigDecimal.ZERO); if (BigDecimal.ZERO.compareTo(qty) == 0) { continue; } oneFlow = flowSet.newEntity(); oneFlow.set(IStockLetter.Field_DocumentDocType, bookCommand.getDocumentDocType()); oneFlow.set(IStockLetter.Field_DocCode, master.getString("code")); oneFlow.set(IStockLetter.Field_DocDate, master.getString(IStockLetter.Field_DocDate)); oneFlow.set(IStockLetter.Field_BuId, hostMaster.getString(IStockLetter.Field_BuId)); oneFlow.set(IStockLetter.Field_BuName, hostMaster.getString(IStockLetter.Field_BuName)); oneFlow.set(IStockLetter.Field_DocDetailId, entity.getId()); oneFlow.set(IStockLetter.Field_ProductId, entity.getString(IStockLetter.Field_ProductId)); oneFlow.set(IStockLetter.Field_ProductCode, entity.getString(IStockLetter.Field_ProductCode)); oneFlow.set(IStockLetter.Field_ProductName, entity.getString(IStockLetter.Field_ProductName)); oneFlow.set(IStockLetter.Field_BatchNo, entity.getString(IStockLetter.Field_BatchNo)); oneFlow.set(IStockLetter.Field_BatchSn, entity.getString(IStockLetter.Field_BatchSn)); oneFlow.set(IStockLetter.Field_ValidFrom, entity.getString(IStockLetter.Field_ValidFrom)); oneFlow.set(IStockLetter.Field_ValidTo, entity.getString(IStockLetter.Field_ValidTo)); oneFlow.set(IStockLetter.Field_Qty, qty); if (detailedWarehouse) { oneFlow.set(IStockLetter.Field_WarehouseId, entity.getString(IStockLetter.Field_WarehouseId)); oneFlow.set(IStockLetter.Field_WarehouseCode, entity.getString(IStockLetter.Field_WarehouseCode)); oneFlow.set(IStockLetter.Field_WarehouseName, entity.getString(IStockLetter.Field_WarehouseName)); oneFlow.set(IStockLetter.Field_DocumentOrderRight, entity.getString(IStockLetter.Field_DocumentOrderRight)); oneFlow.set(IStockLetter.Field_DocumentStockType, entity.getString(IStockLetter.Field_DocumentStockType)); } else { oneFlow.set(IStockLetter.Field_WarehouseId, hostMaster.getString(IStockLetter.Field_WarehouseId)); oneFlow.set(IStockLetter.Field_WarehouseCode, hostMaster.getString(IStockLetter.Field_WarehouseCode)); oneFlow.set(IStockLetter.Field_WarehouseName, hostMaster.getString(IStockLetter.Field_WarehouseName)); oneFlow.set(IStockLetter.Field_DocumentOrderRight, hostMaster.getString(IStockLetter.Field_DocumentOrderRight)); oneFlow.set(IStockLetter.Field_DocumentStockType, hostMaster.getString(IStockLetter.Field_DocumentOrderRight)); } if (Util.isEmpty(oneFlow.getString(IStockLetter.Field_WarehouseId))) { Entity warehouse = bookCommand.getWarehouse(); oneFlow.set(IStockLetter.Field_WarehouseId, warehouse.getString("id")); oneFlow.set(IStockLetter.Field_WarehouseCode, warehouse.getString("code")); oneFlow.set(IStockLetter.Field_WarehouseName, warehouse.getString("name")); } flowSet.append(oneFlow); } FlowTable flowTable = bookCommand.getFlowTable(); flowTable.setEntitySet(flowSet); return result; } public void doWriteBook(BookCommand bookCommand) throws Exception { //1. 记库存流水 writeStockFlow(bookCommand); //2. 记库存明细 writeStockDetail(bookCommand); } private void writeStockFlow(BookCommand bookCommand) throws Exception { QtyCommand qtyCommand = bookCommand.getQtyCommand(); if (qtyCommand.isDoNothing()) { return; } //1. 记录流水账 DataObject dataObject = DataObject.getInstance("wm_book_flow"); String batchMark = bookCommand.getBatchMark(); Entity org = bookCommand.getOrg(); String docType = bookCommand.getDocumentDocType(); OrderRight orderRight = bookCommand.getOrderRight(); StockType stockType = bookCommand.getStockType(); Entity warehouse = bookCommand.getWarehouse(); FlowTable flowTable = bookCommand.getFlowTable(); for (Entity flow : flowTable) { BigDecimal qty = flow.getBigDecimal("qty"); EntitySaver saver = dataObject.createEntitySaver(); saver.set("id", ID.newValue()); saver.set("batch_mark", batchMark); saver.set(IStockLetter.Field_DocDetailId, flow.getValue(IStockLetter.Field_DocDetailId)); saver.set("document_doc_type", flow.getValue("document_doc_type")); saver.set("config_doc_type", bookCommand.getDocumentDocType()); saver.set("doc_type", tranferDocumentDocType(IfEnpty(flow.getString("document_doc_type"), docType))); saver.set(IStockLetter.Field_DocCode, flow.getValue(IStockLetter.Field_DocCode)); saver.set(IStockLetter.Field_DocDate, flow.getValue(IStockLetter.Field_DocDate)); saver.set("book_date", bookCommand.getBookDate()); saver.set("org_id", org.getId()); saver.set("org_code", org.getString("code")); saver.set("org_name", org.getString("account_name")); saver.set("account_id", org.getId()); saver.set("warehouse_id", flow.getValue("warehouse_id")); saver.set("warehouse_code", flow.getValue("warehouse_code")); saver.set("warehouse_name", flow.getValue("warehouse_name")); saver.set("document_order_right", flow.getValue("document_order_right")); saver.set("config_order_right", orderRight.name()); saver.set("order_right_code", IfEnpty(flow.getString("document_order_right"), orderRight.name())); saver.set("document_stock_type", flow.getValue("document_stock_type")); saver.set("config_stock_type", stockType.name()); saver.set("stock_type_code", IfEnpty(flow.getString("stock_type_code"), stockType.name())); saver.set("product_id", flow.getValue("product_id")); saver.set("product_code", flow.getString("product_code")); saver.set("product_name", flow.getString("product_name")); saver.set("bu_id", flow.getValue("bu_id")); saver.set("bu_name", flow.getValue("bu_name")); saver.set("batch_no", IfEnpty(flow.getString("batch_no"),"N/A")); saver.set("batch_sn", flow.getString("batch_sn", "")); saver.set("valid_from", flow.getValue("valid_from")); saver.set("valid_to", flow.getValue("valid_to")); //增加仓库类型 saver.set("type_code", warehouse.getString("type_code", "")); saver.set("type_name", warehouse.getString("type_name", "")); boolean qtyChanged = false; boolean negativeConver = bookCommand.isNegativeConvert(); if (qtyCommand.isSetAdd()) { BigDecimal qty_add = qty.multiply(qtyCommand.getAddOperator()); if (qty_add.compareTo(BigDecimal.ZERO) < 0 && negativeConver) { saver.set("qty_delete", BigDecimal.ZERO.subtract(qty_add)); } else { saver.set("qty_add", qty_add); } qtyChanged = true; } if (qtyCommand.isSetDelete()) { BigDecimal qty_delete = qty.multiply(qtyCommand.getDeleteOperator()); if (qty_delete.compareTo(BigDecimal.ZERO) < 0 && negativeConver) { saver.set("qty_add", BigDecimal.ZERO.subtract(qty_delete)); } else { saver.set("qty_delete", qty_delete); } qtyChanged = true; } FreezeCondition freezeCondition = bookCommand.getFreezeCondition(); if (qtyCommand.isSetFreeze()) { BigDecimal qty_freeze = qty.multiply(qtyCommand.getFreezeOperator()); //1. 例如:库存调整单,只有调减审批的时候才需要正数记冻结,只有调减审批不通过才能用负数记冻结 if (freezeCondition.isCompatible(qty_freeze)) { saver.set("qty_freeze", qty_freeze); qtyChanged = true; } } if (qtyCommand.isSetUnfreeze()) { BigDecimal qty_unfreeze = qty.multiply(qtyCommand.getUnfreezeOperator()); //2. 例如:库存调整单,只有调减审批通过的时候才需要减少冻结 if (freezeCondition.isCompatible(qty_unfreeze)) { saver.set("qty_unfreeze", qty_unfreeze); qtyChanged = true; } } if(qtyChanged) { saver.insert(); } } NamedSQL writeStockFlowBookFields = NamedSQL.getInstance("writeStockFlowBookFields"); writeStockFlowBookFields.setParam("batch_mark", batchMark); writeStockFlowBookFields.execute(); } private void writeStockDetail(BookCommand bookCommand) throws Exception { BookResult result = new BookResult(); String batchMark = bookCommand.getBatchMark(); Date bookDate = bookCommand.getBookDate(); Entity org = bookCommand.getOrg(); //1. 计入需要增加的库存明细 NamedSQL writeInsertStockDetail = NamedSQL.getInstance("writeInsertStockDetail"); writeInsertStockDetail.setParam("batch_mark", batchMark); writeInsertStockDetail.setParam("org_id", org.getId()); writeInsertStockDetail.setParam("org_code", org.getString("code")); writeInsertStockDetail.setParam("org_name", org.getString("account_name")); writeInsertStockDetail.setParam("create_time", bookDate); writeInsertStockDetail.setParam("update_time", bookDate); int insertCount = SQLRunner.execSQL(writeInsertStockDetail); result.setInsertCount(insertCount); //2. 计入需要修改数量的库存明细 NamedSQL writeUpdateStockDetail = NamedSQL.getInstance("writeUpdateStockDetail"); writeUpdateStockDetail.setParam("batch_mark", batchMark); writeUpdateStockDetail.setParam("update_time", bookDate); int updateCount = SQLRunner.execSQL(writeUpdateStockDetail); result.setUpdateCount(updateCount); //3. 检查负库存 NamedSQL getNegativeStockDetail = NamedSQL.getInstance("getNegativeStockDetail"); getNegativeStockDetail.setParam("batch_mark", batchMark); EntitySet negativeStock = SQLRunner.getEntitySet(getNegativeStockDetail); if (negativeStock.size() > 0) { dataWriter.reportOneError("库存校验", "库存不足,无法过账"); dataWriter.addValue("negative_stock", negativeStock); throw new Exception("库存不足,无法过账"); } //4. 补充明细账字段(产品分类编码、产品分类名称、产品编码、产品名称、OrderRightName、StockTypeName) NamedSQL resetStockDetailOtherFields = NamedSQL.getInstance("resetStockDetailOtherFields"); resetStockDetailOtherFields.setParam("batch_mark", bookCommand.getBatchMark()); resetStockDetailOtherFields.execute(); //5. 删除数量为0的库存明细 NamedSQL writeDeleteStockDetail = NamedSQL.getInstance("writeDeleteStockDetail"); writeDeleteStockDetail.setParam("batch_mark", batchMark); int deleteCount = SQLRunner.execSQL(writeDeleteStockDetail); result.setDeleteCount(deleteCount); //6. 日志并校验 logger.debug("库存明细>>计划记账{}条, 插入{} 条,修改{}条, 删除{}条;批次号:{}", result.getPlanCount(), result.getInsertCount(), result.getUpdateCount(), result.getDeleteCount(), batchMark); if (!result.isValid()) { // throw new Exception("库存明细记账时存在错误,操作已经终止并回滚"); } } private String IfEnpty(String one, String another) { if (!Util.isEmpty(one)) { return one; } return another; } public Object tranferDocumentDocType(Object doc_type) { if ("普通销售出库".equals(doc_type)) { return "普通销售入库"; } return doc_type; } }