# WarehouseManagerImpl.java

    package com.mycim.server.asm.manager.impl;

    import com.fa.sesa.exception.Assert;
    import com.fa.sesa.exception.Errors;
    import com.fa.sesa.threadlocal.LocalContext;
    import com.mycim.framework.jdbc.Page;
    import com.mycim.framework.utils.lang.BooleanUtils;
    import com.mycim.framework.utils.lang.StringUtils;
    import com.mycim.framework.utils.lang.collections.CollectionUtils;
    import com.mycim.framework.utils.lang.collections.MapUtils;
    import com.mycim.framework.utils.lang.math.NumberUtils;
    import com.mycim.server.asm.dao.WarehouseDAO;
    import com.mycim.server.asm.manager.LotInventoryManager;
    import com.mycim.server.asm.manager.MaterialManager;
    import com.mycim.server.asm.manager.WarehouseManager;
    import com.mycim.server.base.manager.NamedObjectManager;
    import com.mycim.server.base.manager.TransactionLogManager;
    import com.mycim.server.ems.manager.EquipmentBomManager;
    import com.mycim.server.prp.manager.OperationManager;
    import com.mycim.server.rcp.manager.RecipeManager;
    import com.mycim.server.wip.manager.DiffBatchQueryManager;
    import com.mycim.server.wip.manager.JobQueryManager;
    import com.mycim.server.wip.manager.LotBondingManager;
    import com.mycim.valueobject.MessageIdList;
    import com.mycim.valueobject.ObjectList;
    import com.mycim.valueobject.bas.EquipmentBomInfo;
    import com.mycim.valueobject.bas.TransactionLog;
    import com.mycim.valueobject.consts.BOMLossTypeConst;
    import com.mycim.valueobject.consts.TransactionNames;
    import com.mycim.valueobject.inv.*;
    import com.mycim.valueobject.inv.value.WarehouseNames;
    import com.mycim.valueobject.prp.Item;
    import com.mycim.valueobject.prp.Operation;
    import com.mycim.valueobject.prp.Recipe;
    import com.mycim.valueobject.wip.Job;
    import com.mycim.valueobject.wip.LotConsumesMaterialHistory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    import org.springframework.transaction.annotation.Transactional;

    import java.sql.Timestamp;
    import java.util.*;

    /**
     * @author shijie.deng
     * @date 2019/8/27
     **/
    @Service
    @Transactional
    public class WarehouseManagerImpl implements WarehouseManager {

        @Autowired
        private WarehouseDAO warehouseDAO;

        @Autowired
        private MaterialManager materialManager;

        @Autowired
        private TransactionLogManager transactionLogManager;

        @Autowired
        private LotInventoryManager lotInventoryManager;

        @Autowired
        private OperationManager operationManager;

        @Autowired
        private JobQueryManager jobQueryManager;

        @Autowired
        private NamedObjectManager namedObjectManager;

        @Autowired
        private EquipmentBomManager equipmentBomManager;

        @Autowired
        private LotBondingManager lotBondingManager;

        @Autowired
        private DiffBatchQueryManager diffBatchQueryManager;

        @Autowired
        private RecipeManager recipeManager;

        private static final String BOM_BANK = "BOM_BANK";

        private static final String MEASURABLE_TYPE = "MeasurableType";

        private static final String COUNTING_TYPE = "CountingType";

        private static final String BY_WAFER = "By Wafer";

        private static final String BY_LOT = "By Lot";

        private static final String BY_BATCH = "By Batch";

        private static final String ON = "ON";

        private static final String OFF = "OFF";

        @Override
        public long consumeItemQty(List<Map<String, Object>> consumedItems, String userId, long facilityRrn,
                                   Long checklistJobRrn) {
            TransactionLog transactionLog = transactionLogManager.startTransactionLog("CONSUME", userId);

            for (Map<String, Object> consumedItem : consumedItems) {
                Long itemRrn = MapUtils.getLong(consumedItem, "itemRrn");
                Double consumedQty = MapUtils.getDouble(consumedItem, "consumedQty");
                Item item = warehouseDAO.getItem(itemRrn);
                Long warehouseRrn = item.getDefaultWarehouseRrn();
                String lotNumber = MapUtils.getStringCheckNull(consumedItem, "lotNumber");
                String orderNumber = MapUtils.getStringCheckNull(consumedItem, "orderNumber");
                String ticketNumber = MapUtils.getStringCheckNull(consumedItem, "ticketNumber");
                Integer sequenceNumber = MapUtils.getInteger(consumedItem, "sequenceNumber");
                Long lotRrn = MapUtils.getLong(consumedItem, "lotRrn");
                Long stepSequence = MapUtils.getLong(consumedItem, "stepSequence");
                String comments = MapUtils.getStringCheckNull(consumedItem, "comments");

                warehouseDAO.increaseItemQty((double) 0, consumedQty, (double) 0, itemRrn);

                if (sequenceNumber == null) {
                    sequenceNumber = 0;
                }
                warehouseDAO
                        .increaseWareHouseInventory(itemRrn, warehouseRrn, null, null, (double) 0, consumedQty, (double) 0);
                warehouseDAO
                        .createInventoryTransactionHistory(transactionLog.getTransRrn(), sequenceNumber, itemRrn, lotNumber,
                                                           facilityRrn, transactionLog.getTransId(), consumedQty,
                                                           transactionLog.getTransStartTimestamp(), warehouseRrn, "", null,
                                                           "", null, "", null, "", "", ticketNumber, checklistJobRrn,
                                                           lotRrn, stepSequence, comments);

            }

            transactionLogManager.markTransactionLog(transactionLog);
            return transactionLog.getTransRrn();
        }

        @Override
        public List<MaterialDO> getWarehouseInventoryList(Long warehouseRrn, Map<String, String> condition) {
            List<WarehouseInventoryDO> warehouseInventoryList = warehouseDAO
                    .getWarehouseInventoryListForMaterial(warehouseRrn, condition);
            List<MaterialDO> results = new ArrayList<>();
            for (WarehouseInventoryDO warehouseInventoryDO : warehouseInventoryList) {
                MaterialDO materialDO = new MaterialDO();
                materialDO.setInstanceRrn(warehouseInventoryDO.getItemRrn());
                materialDO = materialManager.getMaterial(materialDO);
                if (materialDO != null) {
                    materialDO.setReceiptQty(warehouseInventoryDO.getReceiptQty());
                    materialDO.setIssueQty(warehouseInventoryDO.getIssueQty());
                    materialDO.setAdjustQty(warehouseInventoryDO.getAdjustQty());

                    results.add(materialDO);
                }
            }
            return results;
        }

        @Override
        public List<MaterialDO> getWarehouseInventoryListForMaterialWithExt(Long warehouseRrn,
                                                                            Map<String, String> condition) {
            List<WarehouseInventoryDO> warehouseInventoryList = warehouseDAO
                    .getWarehouseInventoryListForMaterialWithExt(warehouseRrn, condition);
            List<MaterialDO> results = new ArrayList<>();
            for (WarehouseInventoryDO warehouseInventoryDO : warehouseInventoryList) {
                MaterialDO materialDO = new MaterialDO();
                materialDO.setInstanceRrn(warehouseInventoryDO.getItemRrn());
                materialDO = materialManager.getMaterial(materialDO);
                if (materialDO != null) {
                    materialDO.setReceiptQty(warehouseInventoryDO.getReceiptQty());
                    materialDO.setIssueQty(warehouseInventoryDO.getIssueQty());
                    materialDO.setAdjustQty(warehouseInventoryDO.getAdjustQty());
                    results.add(materialDO);
                }
            }
            return results;
        }

        @Override
        public void setExpirationDateForReceive(Integer expirationLength, LotInventoryDO lotInventory) {
            if (lotInventory.getExpirationDate() == null) {
                int expirationLengthTemp = expirationLength == null ? 0 : expirationLength;
                Calendar c = Calendar.getInstance();
                c.setTime(lotInventory.getReceiptDate());
                c.add(Calendar.DAY_OF_MONTH, expirationLengthTemp);
                lotInventory.setExpirationDate(new Timestamp(c.getTime().getTime()));
                lotInventory.setAvailabilityDays(NumberUtils.toDouble(Integer.toString(expirationLengthTemp)));
            } else {
                long availabilityDays =
                        (lotInventory.getExpirationDate().getTime() - lotInventory.getReceiptDate().getTime()) / 1000;
                if (availabilityDays > 0) {
                    availabilityDays = availabilityDays / 3600 / 24;
                }
                lotInventory.setAvailabilityDays(NumberUtils.toDouble(Long.toString(availabilityDays)));
            }
        }

        @Override
        public void saveReceivedMaterialToWarehouse(Long facilityRrn, String userId, List<WarehouseTransBO> receiveTrans) {
            TransactionLog transactionLog = transactionLogManager.startTransactionLog(userId, TransactionNames.RECEIVE_KEY);
            transactionLog.setComments("库房接收");

            long transSequence = 1;
            for (WarehouseTransBO warehouseTrans : receiveTrans) {

                setLotInventoryQty(transactionLog.getTransId(), warehouseTrans);

                InventoryTransHistoryDO inventoryTransHistory = buildInventoryTransHistory(facilityRrn, warehouseTrans,
                                                                                           transactionLog, transSequence++);

                saveReceivedMaterialToWarehouse(warehouseTrans.getLotInventoryDO(), inventoryTransHistory);
            }
            transactionLogManager.markTransactionLog(transactionLog);
        }

        @Override
        public void saveConsumeMaterialFromWarehouse(Long facilityRrn, String userId,
                                                     List<WarehouseTransBO> consumptionTrans) {
            TransactionLog transactionLog = transactionLogManager
                    .startTransactionLog(userId, TransactionNames.MANUAL_CONSUME_KEY);
            transactionLog.setComments("库房手动消耗");

            long transSequence = 1;
            for (WarehouseTransBO warehouseTrans : consumptionTrans) {
                setLotInventoryQty(TransactionNames.MANUAL_CONSUME_KEY, warehouseTrans);
                InventoryTransHistoryDO inventoryTransHistory = buildInventoryTransHistory(facilityRrn, warehouseTrans,
                                                                                           transactionLog, transSequence++);

                saveConsumeMaterialFromWarehouse(warehouseTrans.getLotInventoryDO(), inventoryTransHistory);
            }

            transactionLogManager.markTransactionLog(transactionLog);

        }

        @Override
        public WarehouseInventoryDO getWarehouseInventory(Long warehouseRrn, Long materialRrn) {
            return warehouseDAO.getWarehouseInventory(warehouseRrn, materialRrn);
        }

        @Override
        public void saveConvertMaterialFromWarehouse(Long facilityRrn, String userId, List<WarehouseTransBO> convertTrans) {
            for (WarehouseTransBO warehouseTrans : convertTrans) {
                TransactionLog transactionLog = transactionLogManager
                        .startTransactionLog(userId, warehouseTrans.getTransType());
                transactionLog.setComments("物料类型转换");
                long transSequence = 1;

                // 消耗源物料
                setLotInventoryQty(TransactionNames.CONSUME_KEY, warehouseTrans);
                InventoryTransHistoryDO inventoryTransHistory = buildInventoryTransHistory(facilityRrn, warehouseTrans,
                                                                                           transactionLog, transSequence);

                LotInventoryDO lotInventory = warehouseTrans.getLotInventoryDO();
                saveConsumeMaterialFromWarehouse(lotInventory, inventoryTransHistory);

                // 接收新物料
                lotInventory.setLotNumber(warehouseTrans.getTargetLotNumber());
                lotInventory.setReceiptQty(warehouseTrans.getTransQty());
                lotInventory.setIssueQty(0D);
                lotInventory.setAdjustQty(0D);
                lotInventory.setIssuePriority(null);
                lotInventory.setStatus(WarehouseNames.ACTIVE_STATUS);
                lotInventory.setReceiptDate(new Timestamp(System.currentTimeMillis()));
                setExpirationDateForReceive(NumberUtils.toInt(Double.toString(lotInventory.getAvailabilityDays())),
                                            lotInventory);

                inventoryTransHistory.setMaterialLotNumber(lotInventory.getLotNumber());
                inventoryTransHistory.setTransSequence(++transSequence);

                saveReceivedMaterialToWarehouse(lotInventory, inventoryTransHistory);

                transactionLogManager.markTransactionLog(transactionLog);
            }
        }

        @Override
        public void saveStocktakeMaterialFromWarehouse(Long facilityRrn, String userId,
                                                       List<WarehouseTransBO> stocktakeTrans) {
            TransactionLog transactionLog = transactionLogManager.startTransactionLog(userId, TransactionNames.ADJUST_KEY);
            transactionLog.setComments("库房盘点");
            long transSequence = 1;
            for (WarehouseTransBO warehouseTrans : stocktakeTrans) {

                setLotInventoryQty(TransactionNames.ADJUST_KEY, warehouseTrans);
                InventoryTransHistoryDO inventoryTransHistory = buildInventoryTransHistory(facilityRrn, warehouseTrans,
                                                                                           transactionLog, transSequence++);

                //当前逻辑：ITEM表字段ISSUE_QTY=IssueQty+|TransQty|，需卡控不能超过字段ISSUE_QTY的最大精度
                Item item = warehouseDAO.getItem(warehouseTrans.getLotInventoryDO().getItemRrn());
                Assert.state(((Math.abs(warehouseTrans.getTransQty()) + item.getIssueQty()) <= 9999999999L),
                             Errors.create().key(MessageIdList.MATERIAL_ISSUEQTY_EXCEEDED)
                                   .content("Consumed quantity cannot exceed the maximum value:9999999999!").build());
                saveConsumeMaterialFromWarehouse(warehouseTrans.getLotInventoryDO(), inventoryTransHistory);
            }
            transactionLogManager.markTransactionLog(transactionLog);
        }

        @Override
        public void saveTransferMaterialFromWarehouse(Long facilityRrn, String userId,
                                                      List<WarehouseTransBO> transferTrans) {
            for (WarehouseTransBO warehouseTrans : transferTrans) {

                TransactionLog transactionLog = transactionLogManager
                        .startTransactionLog(userId, TransactionNames.TRANSFER_KEY);
                transactionLog.setComments("库存转移");
                long transSequence = 1;

                setLotInventoryQty(TransactionNames.ADJUST_KEY, warehouseTrans);
                InventoryTransHistoryDO inventoryTransHistory = buildInventoryTransHistory(facilityRrn, warehouseTrans,
                                                                                           transactionLog, transSequence);
                inventoryTransHistory.setTransId(TransactionNames.TRANSFEROUT_KEY);
                inventoryTransHistory.setComments(warehouseTrans.getFromComments());

                LotInventoryDO lotInventory = warehouseTrans.getLotInventoryDO();

                saveStocktakeMaterialFromWarehouse(lotInventory, inventoryTransHistory);

                lotInventory.setWarehouseRrn(warehouseTrans.getToWarehouseRrn());
                lotInventory.setWarehouseId(warehouseTrans.getToWarehouseId());
                lotInventory.setReceiptQty(inventoryTransHistory.getTransQty());
                lotInventory.setIssueQty(0D);
                lotInventory.setAdjustQty(0D);
                lotInventory.setIssuePriority(null);
                lotInventory.setStatus(WarehouseNames.ACTIVE_STATUS);
                lotInventory.setReceiptDate(new Timestamp(System.currentTimeMillis()));

                setExpirationDateForReceive(NumberUtils.toInt(Double.toString(lotInventory.getAvailabilityDays())),
                                            lotInventory);

                inventoryTransHistory.setTransId(TransactionNames.TRANSFERIN_KEY);
                inventoryTransHistory.setTransSequence(++transSequence);
                inventoryTransHistory.setComments(warehouseTrans.getToComments());

                saveReceivedMaterialToWarehouse(lotInventory, inventoryTransHistory);

                transactionLogManager.markTransactionLog(transactionLog);
            }
        }

        @Override
        public Page getInventoryTransHistoryForPage(Page page, Long warehouseRrn, Long materialRrn, String lotNumber,
                                                    String transId, Timestamp startDateTime, Timestamp endDateTime) {
            return warehouseDAO
                    .getInventoryTransHistoryForPage(page, warehouseRrn, materialRrn, lotNumber, transId, startDateTime,
                                                     endDateTime);
        }

        @Override
        public void consumeMaterialFromWarehouseForBOM(long facilityRrn, String userId, Long runRrn,
                                                       List<WarehouseTransBO> consumptionTrans) {

            TransactionLog transactionLog = transactionLogManager
                    .startTransactionLog(userId, TransactionNames.BOR_CONSUME_KEY);

            long transSequence = 1;
            for (WarehouseTransBO warehouseTrans : consumptionTrans) {
                LotInventoryDO lotInventory = warehouseTrans.getLotInventoryDO();

                if (BOMLossTypeConst.QUANTITY.equals(warehouseTrans.getBorLossType())) {
                    InventoryTransHistoryDO inventoryTransHistory = buildInventoryTransHistory(facilityRrn, warehouseTrans,
                                                                                               transactionLog,
                                                                                               transSequence);
                    inventoryTransHistory.setBorRrn(warehouseTrans.getBorRrn());
                    inventoryTransHistory.setBorVersion(warehouseTrans.getBorVersion());
                    inventoryTransHistory.setRunRrn(runRrn);

                    // 库房消耗
                    this.saveConsumeMaterialFromWarehouse(lotInventory, inventoryTransHistory);
                }

                // BOM消耗记录
                LotConsumesMaterialHistory lotConsumesMaterialHistory = new LotConsumesMaterialHistory();

                lotConsumesMaterialHistory.setTransRrn(transactionLog.getTransRrn());
                lotConsumesMaterialHistory.setTransSequence(transSequence);

                lotConsumesMaterialHistory.setBorRrn(warehouseTrans.getBorRrn());
                lotConsumesMaterialHistory.setBorVersion(warehouseTrans.getBorVersion());
                lotConsumesMaterialHistory.setBorResourceSeq(warehouseTrans.getBorResourceSeq());
                lotConsumesMaterialHistory.setBorLossType(warehouseTrans.getBorLossType());
                lotConsumesMaterialHistory.setBorBasisCode(warehouseTrans.getBorBasisCode());

                lotConsumesMaterialHistory.setLotNumber(lotInventory.getLotNumber());
                lotConsumesMaterialHistory.setItemRrn(lotInventory.getItemRrn());
                lotConsumesMaterialHistory.setWarehouseRrn(lotInventory.getWarehouseRrn());
                lotConsumesMaterialHistory.setMaterialType(lotInventory.getMaterialType());

                lotConsumesMaterialHistory.setRunRrn(runRrn);
                lotConsumesMaterialHistory.setLotRrns(warehouseTrans.getLotRrnsInfo());
                lotConsumesMaterialHistory.setLotIds(warehouseTrans.getLotIdsInfo());

                lotConsumesMaterialHistory.setConsumptionQty(lotInventory.getIssueQty());
                lotConsumesMaterialHistory.setConsumptionUserId(userId);

                this.insertLotConsumesMaterialHistory(lotConsumesMaterialHistory);

                transSequence++;
            }

            transactionLogManager.markTransactionLog(transactionLog);
        }

        @Override
        public Page getLotConsumesMaterialHistoryForPage(Page page, Map<String, Object> conditions) {
            return warehouseDAO.getLotConsumesMaterialHistoryForPage(page, conditions);
        }

        @Override
        public Page getReceiveHistoryForPage(Page page, Long warehouseRrn, Long materialRrn, String lotNumber,
                                             Timestamp startDateTime, Timestamp endDateTime) {
            return warehouseDAO
                    .getReceiveHistoryForPage(page, warehouseRrn, materialRrn, lotNumber, startDateTime, endDateTime);
        }

        @Override
        public void consumeMaterialFromWarehouse4Pms(long facilityRrn, String userId,
                                                     List<WarehouseTransBO> consumptionTrans) {
            TransactionLog transactionLog = transactionLogManager
                    .startTransactionLog(userId, TransactionNames.PMS_CONSUME_KEY);

            long transSequence = 1;
            for (WarehouseTransBO warehouseTrans : consumptionTrans) {
                LotInventoryDO lotInventory = warehouseTrans.getLotInventoryDO();
                InventoryTransHistoryDO inventoryTransHistory = buildInventoryTransHistory(facilityRrn, warehouseTrans,
                                                                                           transactionLog, transSequence);

                // 库房消耗
                this.saveConsumeMaterialFromWarehouse(lotInventory, inventoryTransHistory);

                transSequence++;
            }

            transactionLogManager.markTransactionLog(transactionLog);
        }

        /**
         * bom 消耗记录insert
         *
         * @param consumptionTrans
         * @param map
         */
        private void consumeMaterialFromWarehouse4Bom(List<WarehouseTransBO> consumptionTrans, Map map) {

            TransactionLog transactionLog = transactionLogManager
                    .startTransactionLog(LocalContext.getUserId(), TransactionNames.BOM_CONSUME);

            long transSequence = 1;
            String consumeMode = MapUtils.getString(map, "consumeMode");

            for (WarehouseTransBO warehouseTrans : consumptionTrans) {
                LotInventoryDO lotInventory = warehouseTrans.getLotInventoryDO();
                InventoryTransHistoryDO inventoryTransHistory = buildInventoryTransHistory(LocalContext.getFacilityRrn(),
                                                                                           warehouseTrans, transactionLog,
                                                                                           transSequence);
                if (BY_BATCH.equals(consumeMode)) {
                    inventoryTransHistory.setBatchId(MapUtils.getString(map, "batchId"));
                } else {
                    inventoryTransHistory.setLotRrn(MapUtils.getLong(map, "lotRrn"));
                }
                inventoryTransHistory.setStepSequence(MapUtils.getLong(map, "flowSeq"));
                //bom消耗记录
                LotStepBomHistoryDO lotStepBomHistoryDO = buildLotStepBom(inventoryTransHistory, map);
                materialManager.insertLotStepBomDao(lotStepBomHistoryDO);
                // 库房消耗
                this.saveConsumeMaterialFromWarehouse(lotInventory, inventoryTransHistory);

                transSequence++;
            }

            transactionLogManager.markTransactionLog(transactionLog);
        }

        /**
         * BOM 消耗
         *
         * @param material
         * @param consumeQty
         * @param map
         */
        @Override
        public void consumeMaterialFromWarehouseForBom(MaterialDO material, Integer consumeQty, Map map) {
            long facilityRrn = LocalContext.getFacilityRrn();
            WarehouseTransBO warehouseTrans;
            LotInventoryDO lotInventory;
            Operation warehouse = operationManager.getOperation(BOM_BANK, facilityRrn);
            List<WarehouseTransBO> convertTrans = new ArrayList<>();
            //找到需要消耗的物料的批次
            List<LotInventoryDO> lids = lotInventoryManager
                    .getAvailableLotInventory(material.getInstanceRrn(), warehouse.getInstanceRrn(), null);
            Assert.isFalse(lids == null || lids.isEmpty(),
                           Errors.create().key(MessageIdList.NO_HAVE_BOM_LOT).content("{} has no available material lot!")
                                 .args(material.getInstanceId()).build());
            String consumeMode = MapUtils.getString(map, "consumeMode");

            String lotNumber = "";
            int remainQty = consumeQty;
            int consume;

            //先消耗主料，集合按照主辅料进行排序
            Collections.sort(lids, new Comparator<LotInventoryDO>() {
                @Override
                public int compare(LotInventoryDO o1, LotInventoryDO o2) {
                    Integer o1MaterType = Integer
                            .parseInt(StringUtils.isNotBlank(o1.getAttributeData4()) ? o1.getAttributeData4() : "0");
                    Integer o2MaterType = Integer
                            .parseInt(StringUtils.isNotBlank(o2.getAttributeData4()) ? o2.getAttributeData4() : "0");
                    return o2MaterType.compareTo(o1MaterType);

                }
            });

            for (LotInventoryDO lid : lids) {
                //多个LotNumber消耗
                if (lid.getTotalQuantity() <= 0) {
                    continue;
                }
                if (remainQty <= 0) {
                    break;
                }
                if (lid.getTotalQuantity() > remainQty) {
                    consume = remainQty;
                    remainQty = remainQty - consumeQty;
                } else {
                    consume = lid.getTotalQuantity().intValue();
                    remainQty = remainQty - consume;
                }
                lotNumber = lid.getLotNumber();
                Double qty = NumberUtils.toDouble(Integer.toString(consume));
                lotInventory = lotInventoryManager
                        .getLotInventory(lotNumber, material.getInstanceRrn(), warehouse.getInstanceRrn());
                lotInventory.setReceiptQty(0D);
                lotInventory.setAdjustQty(0D);
                lotInventory.setIssueQty(qty);

                warehouseTrans = new WarehouseTransBO(lotInventory);
                warehouseTrans.setTransComments(consumeMode);
                warehouseTrans.setTransQty(qty);
                convertTrans.add(warehouseTrans);
            }
            consumeMaterialFromWarehouse4Bom(convertTrans, map);
        }

        /**
         * BOM 消耗计算
         *
         * @param job
         */
        @Override
        public void calculateConsumeQtyForBom(Job job) {
            List<Map> lotList = jobQueryManager.getJobList(job.getJobRrn(), ObjectList.JOB_KEY);
            Long eqptRrn = job.getEqptRrn();
            //获取设备上绑定的所有BOM
            List<EquipmentBomInfo> equipmentBomList = equipmentBomManager.getEquipmentBomList(eqptRrn, 0);
            if (CollectionUtils.isEmpty(equipmentBomList)) {
                return;
            }
            for (Map map : lotList) {
                Long lotRrn = MapUtils.getLong(map, "lotRrn");
                if (lotBondingManager.checkBondLotConsume(lotRrn)) {
                    continue;
                }
                String batchId = diffBatchQueryManager.getLotBatchId(lotRrn);
                String lotType = MapUtils.getString(map, "lotType");
                String recipeId = MapUtils.getString(map, "recipeId");
                Recipe recipe = recipeManager.getRecipe(recipeId, LocalContext.getFacilityRrn());
                long mainRecipeRrn = recipeManager.isMainRecipe(recipe.getInstanceRrn());
                Recipe mainRecipe = recipeManager.getRecipe(mainRecipeRrn);
                for (EquipmentBomInfo equipmentBom : equipmentBomList) {
                    Integer consumeQty = 0;
                    String consumeType = equipmentBom.getConsumeType();
                    String consumeMode = equipmentBom.getConsumeMode();
                    MaterialDO material = new MaterialDO();
                    material.setInstanceRrn(equipmentBom.getMaterialRrn());
                    material = new MaterialDO(equipmentBom.getMaterialId(), namedObjectManager
                            .getNamedSpace(LocalContext.getFacilityRrn(), ObjectList.ITEM_KEY), ObjectList.ITEM_KEY);
                    material = materialManager.getMaterial(material);
                    Assert.isFalse(material == null || material.getInstanceRrn() <= 0,
                                   Errors.create().key(MessageIdList.BOM_ID_NOT_EXIST).content("BOM ID not exist!")
                                         .args(equipmentBom.getMaterialId()).build());
                    int totalQuantity = materialManager.getMaterialTotalQuantity(material.getInstanceRrn());
                    String lotTypes = material.getAttribute3();
                    Boolean consumeFlag = Boolean.FALSE;
                    if (COUNTING_TYPE.equals(consumeType)) {
                        //check lotType
                        if (!StringUtils.isNotEmptyTrim(lotTypes)) {
                            continue;
                        }
                        consumeFlag = StringUtils.contains(lotTypes, lotType);
                        //check  consumeMode
                        if (consumeFlag) {
                            if (BY_WAFER.equals(consumeMode)) {
                                consumeQty += MapUtils.getInteger(map, "qty1");
                            } else if (BY_LOT.equals(consumeMode)) {
                                consumeQty += 1;
                            } else if ((BY_BATCH.equals(consumeMode)) && StringUtils.isNotBlank(batchId) &&
                                    BooleanUtils.isFalse(equipmentBom.getBatchFlag())) {
                                // 当选择by batch 方式消耗并且lot 在 batch 出站时进行消耗计算，通过batchFlag标记已经消耗过了
                                equipmentBom.setBatchFlag(true);
                                consumeQty += 1;
                            }
                        }
                    } else if (MEASURABLE_TYPE.equals(consumeType)) {
                        if (!equipmentBom.getRecipeId().equals(mainRecipe.getInstanceId())) {
                            continue;
                        }
                        //check lot type
                        if (ON.equals(equipmentBom.getLotTypeFlag())) {
                            consumeFlag = StringUtils.contains(lotTypes, lotType);
                        } else if (OFF.equals(equipmentBom.getLotTypeFlag())) {
                            consumeFlag = true;
                        }
                        //check  consumeMode
                        if (consumeFlag) {
                            if (BY_WAFER.equals(consumeMode)) {
                                consumeQty += MapUtils.getInteger(map, "qty1");
                            } else if (BY_LOT.equals(consumeMode)) {
                                consumeQty += 1;
                            } else if ((BY_BATCH.equals(consumeMode)) && StringUtils.isNotBlank(batchId) &&
                                    BooleanUtils.isFalse(equipmentBom.getBatchFlag())) {
                                // 当选择by batch 方式消耗并且lot 在 batch 出站时进行消耗计算，通过batchFlag标记已经消耗过了
                                equipmentBom.setBatchFlag(true);
                                consumeQty += 1;
                            }
                        }
                    }
                    totalQuantity = totalQuantity - consumeQty;
                    Assert.isFalse(totalQuantity < 0,
                                   Errors.create().key(MessageIdList.BOM_INVENTORY_NOT_ENOUGH).args(totalQuantity).build());
                    map.put("batchId", batchId);
                    map.put("consumeType", consumeType);
                    map.put("consumeMode", consumeMode);
                    //insert consume
                    consumeMaterialFromWarehouseForBom(material, consumeQty, map);
                }
            }
        }

        private void saveReceivedMaterialToWarehouse(LotInventoryDO lotInventory,
                                                     InventoryTransHistoryDO inventoryTransHistory) {
            materialManager.saveMaterialForReceived(inventoryTransHistory);

            saveWarehouseForReceived(inventoryTransHistory);

            saveLotInventoryForReceived(lotInventory, inventoryTransHistory);
        }

        private void saveLotInventoryForReceived(LotInventoryDO lotInventory,
                                                 InventoryTransHistoryDO inventoryTransHistory) {
            lotInventoryManager.saveLotInventory(lotInventory);
            insertInventoryTransHistory(inventoryTransHistory);
        }

        private void insertInventoryTransHistory(InventoryTransHistoryDO inventoryTransHistory) {
            warehouseDAO.insertInventoryTransHistory(inventoryTransHistory);
            warehouseDAO.insertInventoryTransHistoryExt(inventoryTransHistory);
        }

        private void saveWarehouseForReceived(InventoryTransHistoryDO inventoryTransHistory) {
            Boolean isExisted = warehouseDAO.checkMaterialIsExistedInWarehouse(inventoryTransHistory.getWarehouseRrn(),
                                                                               inventoryTransHistory.getItemRrn());
            if (isExisted) {
                warehouseDAO.updateWarehouseInventoryForReceiptQty(inventoryTransHistory.getWarehouseRrn(),
                                                                   inventoryTransHistory.getItemRrn(),
                                                                   inventoryTransHistory.getTransQty());
            } else {
                warehouseDAO.insertWarehouseInventory(buildWarehouseInventoryForInsert(inventoryTransHistory));
            }
            warehouseDAO.insertWarehouseInventoryHistory(inventoryTransHistory.getWarehouseRrn(),
                                                         inventoryTransHistory.getItemRrn(),
                                                         inventoryTransHistory.getTransRrn(),
                                                         inventoryTransHistory.getTransSequence(),
                                                         inventoryTransHistory.getTransQty());
        }

        private WarehouseInventoryDO buildWarehouseInventoryForInsert(InventoryTransHistoryDO inventoryTransHistory) {
            WarehouseInventoryDO warehouseInventory = new WarehouseInventoryDO();
            warehouseInventory.setItemRrn(inventoryTransHistory.getItemRrn());
            warehouseInventory.setWarehouseRrn(inventoryTransHistory.getWarehouseRrn());
            warehouseInventory.setWarehouseId(inventoryTransHistory.getWarehouseId());
            warehouseInventory.setReceiptQty(inventoryTransHistory.getTransQty());
            warehouseInventory.setIssueQty(0D);
            warehouseInventory.setAdjustQty(0D);
            return warehouseInventory;
        }

        private void setLotInventoryQty(String transId, WarehouseTransBO warehouseTrans) {
            LotInventoryDO lotInventory = warehouseTrans.getLotInventoryDO();

            if (TransactionNames.RECEIVE_KEY.equals(transId)) {
                lotInventory.setReceiptQty(warehouseTrans.getTransQty());
                lotInventory.setIssueQty(0D);
                lotInventory.setAdjustQty(0D);
            } else if (TransactionNames.ADJUST_KEY.equals(transId)) {
                lotInventory.setReceiptQty(0D);
                lotInventory.setIssueQty(0D);
                lotInventory.setAdjustQty(warehouseTrans.getTransQty());
            } else {
                lotInventory.setReceiptQty(0D);
                lotInventory.setIssueQty(warehouseTrans.getTransQty());
                lotInventory.setAdjustQty(0D);
            }
        }

        /**
         * 构建库存事务历史信息
         *
         * @param facilityRrn
         * @param warehouseTrans
         * @param transactionLog
         * @param transSequence
         * @return
         */
        private InventoryTransHistoryDO buildInventoryTransHistory(Long facilityRrn, WarehouseTransBO warehouseTrans,
                                                                   TransactionLog transactionLog, Long transSequence) {
            InventoryTransHistoryDO inventoryTransHistory = new InventoryTransHistoryDO();

            // Transaction info
            inventoryTransHistory.setTransRrn(transactionLog.getTransRrn());
            inventoryTransHistory.setTransSequence(transSequence);
            inventoryTransHistory.setTransId(transactionLog.getTransId());
            inventoryTransHistory.setTransQty(Math.abs(warehouseTrans.getTransQty()));
            inventoryTransHistory.setTransDate(transactionLog.getTransStartTimestamp());

            // lotInventory info
            inventoryTransHistory.setFacilityRrn(facilityRrn);
            inventoryTransHistory.setWarehouseId(warehouseTrans.getLotInventoryDO().getWarehouseId());
            inventoryTransHistory.setWarehouseRrn(warehouseTrans.getLotInventoryDO().getWarehouseRrn());
            inventoryTransHistory.setItemRrn(warehouseTrans.getLotInventoryDO().getItemRrn());
            inventoryTransHistory.setMaterialLotNumber(warehouseTrans.getLotInventoryDO().getLotNumber());
            inventoryTransHistory.setComments(warehouseTrans.getTransComments());

            return inventoryTransHistory;
        }

        private void saveConsumeMaterialFromWarehouse(LotInventoryDO lotInventory,
                                                      InventoryTransHistoryDO inventoryTransHistory) {
            materialManager.saveMaterialForConsumed(inventoryTransHistory);

            saveWarehouseForConsumed(inventoryTransHistory);

            saveLotInventoryForConsumed(lotInventory, inventoryTransHistory);
        }

        private void saveWarehouseForConsumed(InventoryTransHistoryDO inventoryTransHistory) {
            warehouseDAO.updateWarehouseInventoryForIssueQty(inventoryTransHistory.getWarehouseRrn(),
                                                             inventoryTransHistory.getItemRrn(),
                                                             inventoryTransHistory.getTransQty());
            warehouseDAO.insertWarehouseInventoryHistory(inventoryTransHistory.getWarehouseRrn(),
                                                         inventoryTransHistory.getItemRrn(),
                                                         inventoryTransHistory.getTransRrn(),
                                                         inventoryTransHistory.getTransSequence(),
                                                         inventoryTransHistory.getTransQty());
        }

        private void saveLotInventoryForConsumed(LotInventoryDO lotInventory,
                                                 InventoryTransHistoryDO inventoryTransHistory) {
            lotInventoryManager.updateLotInventoryForQty(lotInventory);

            insertInventoryTransHistory(inventoryTransHistory);
        }

        private void saveStocktakeMaterialFromWarehouse(LotInventoryDO lotInventory,
                                                        InventoryTransHistoryDO inventoryTransHistory) {
            materialManager.saveMaterialForStocktake(inventoryTransHistory);

            saveWarehouseForStocktake(inventoryTransHistory);

            saveLotInventoryForStocktake(lotInventory, inventoryTransHistory);
        }

        private void saveWarehouseForStocktake(InventoryTransHistoryDO inventoryTransHistory) {
            warehouseDAO.updateWarehouseInventoryForAdjustQty(inventoryTransHistory.getWarehouseRrn(),
                                                              inventoryTransHistory.getItemRrn(),
                                                              inventoryTransHistory.getTransQty());
            warehouseDAO.insertWarehouseInventoryHistory(inventoryTransHistory.getWarehouseRrn(),
                                                         inventoryTransHistory.getItemRrn(),
                                                         inventoryTransHistory.getTransRrn(),
                                                         inventoryTransHistory.getTransSequence(),
                                                         inventoryTransHistory.getTransQty());
        }

        private void saveLotInventoryForStocktake(LotInventoryDO lotInventory,
                                                  InventoryTransHistoryDO inventoryTransHistory) {
            lotInventoryManager.updateLotInventoryForQty(lotInventory);

            insertInventoryTransHistory(inventoryTransHistory);
        }

        private void insertLotConsumesMaterialHistory(LotConsumesMaterialHistory lotConsumesMaterialHistory) {
            lotInventoryManager.insertLotConsumesMaterialHistory(lotConsumesMaterialHistory);
        }

        private LotStepBomHistoryDO buildLotStepBom(InventoryTransHistoryDO inventoryTransHistory, Map map) {
            LotStepBomHistoryDO lotStepBomHistoryDO = new LotStepBomHistoryDO();
            lotStepBomHistoryDO.setTransRrn(inventoryTransHistory.getTransRrn());
            lotStepBomHistoryDO.setTransId(inventoryTransHistory.getTransId());
            lotStepBomHistoryDO.setBomConsumeMode(MapUtils.getString(map, "consumeMode"));
            lotStepBomHistoryDO.setBomConsumeType(MapUtils.getString(map, "consumeType"));
            lotStepBomHistoryDO.setComments(inventoryTransHistory.getComments());
            lotStepBomHistoryDO.setItemRrn(inventoryTransHistory.getItemRrn());
            lotStepBomHistoryDO.setLotRrn(inventoryTransHistory.getLotRrn());
            lotStepBomHistoryDO.setBatchId(inventoryTransHistory.getBatchId());
            lotStepBomHistoryDO.setFacilityRrn(LocalContext.getFacilityRrn());
            lotStepBomHistoryDO.setStepSequence(inventoryTransHistory.getStepSequence());
            lotStepBomHistoryDO.setMaterialLotNumber(inventoryTransHistory.getMaterialLotNumber());
            lotStepBomHistoryDO.setWarehouseId(inventoryTransHistory.getWarehouseId());
            lotStepBomHistoryDO.setWarehouseRrn(inventoryTransHistory.getWarehouseRrn());
            lotStepBomHistoryDO.setTransTime(inventoryTransHistory.getTransDate());
            lotStepBomHistoryDO.setTransQty(inventoryTransHistory.getTransQty());
            lotStepBomHistoryDO.setTransSequence(inventoryTransHistory.getTransSequence());
            return lotStepBomHistoryDO;
        }

    }