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<BookCommandMeta> 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<BookCommand> 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<BookCommand> 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<String> 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;
|
}
|
|
}
|