package com.prolog.upcloud.base.wh.operate.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.EnumUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.codingapi.tx.annotation.TxTransaction;
import com.google.common.collect.Lists;
import com.prolog.cs.basic.chain.model.cargo.BaseCargoOwnerMst;
import com.prolog.cs.basic.common.model.BaseCommonDict;
import com.prolog.cs.basic.item.model.BaseMtlItemMst;
import com.prolog.cs.basic.item.model.vo.ItemBaseInfoVO;
import com.prolog.cs.basic.item.model.vo.WaveItemInfoVO;
import com.prolog.cs.basic.warehouse.model.BaseWmsWarehouseArea;
import com.prolog.cs.basic.warehouse.model.BaseWmsWarehouseLocator;
import com.prolog.cs.basic.warehouse.model.BaseWmsWarehouseMst;
import com.prolog.cs.basic.warehouse.model.vo.ContainerQueryVO;
import com.prolog.cs.basic.warehouse.model.vo.WarehouseLocatorBasicInfo;
import com.prolog.cs.basic.warehouse.model.vo.WhLocatorQueryVO;
import com.prolog.cs.biz.outbound.model.OmsDoBillHDO;
import com.prolog.cs.biz.outbound.model.constant.MoveTaskStatus;
import com.prolog.cs.biz.outbound.model.dto.MoveTaskDODTO;
import com.prolog.cs.biz.outbound.model.dto.MoveTaskStatusSyncDTO;
import com.prolog.cs.biz.outbound.model.entity.MoveTaskDO;
import com.prolog.entity.vo.MarkTaskItemVO;
import com.prolog.entity.vo.OutBoundRequestItemVO;
import com.prolog.entity.vo.OutBoundRequestVO;
import com.prolog.enums.OutBoundRequestEnum;
import com.prolog.framework.authority.core.dto.UserBizModel;
import com.prolog.framework.authority.core.dto.UserDTO;
import com.prolog.framework.authority.user.UserTemplate;
import com.prolog.framework.bz.common.integration.IntegrationApi;
import com.prolog.framework.bz.common.integration.IntegrationSearchData;
import com.prolog.framework.bz.common.sequence.SequenceApi;
import com.prolog.framework.bz.core.model.ModelUtils;
import com.prolog.framework.common.message.RestMessage;
import com.prolog.framework.core.exception.BizException;
import com.prolog.framework.core.exception.ParameterException;
import com.prolog.framework.core.exception.PrologException;
import com.prolog.framework.core.pojo.Page;
import com.prolog.framework.core.restriction.Criteria;
import com.prolog.framework.core.restriction.FieldSelector;
import com.prolog.framework.dao.pagehelper.PageHelper;
import com.prolog.framework.dao.util.PageUtils;
import com.prolog.framework.utils.MapUtils;
import com.prolog.framework.utils.StringUtils;
import com.prolog.framework.utils.UUIDUtils;
import com.prolog.inte.EisHandler;
import com.prolog.upcloud.base.inventory.enums.StockStatusEnum;
import com.prolog.upcloud.base.inventory.model.WmsInvStockLot;
import com.prolog.upcloud.base.inventory.vo.*;
import com.prolog.upcloud.base.strategy.algorithm.ReplenishAlgorithm;
import com.prolog.upcloud.base.strategy.dto.ReplenishRuleDTO;
import com.prolog.upcloud.base.strategy.dto.stock.ReplenishDataDTO;
import com.prolog.upcloud.base.strategy.dto.stock.StockLocatorDTO;
import com.prolog.upcloud.base.strategy.dto.stock.StockReplenishTaskDTO;
import com.prolog.upcloud.base.strategy.model.BusinessControl;
import com.prolog.upcloud.base.wh.operate.constant.EisBusinessType;
import com.prolog.upcloud.base.wh.operate.constant.EisWorkType;
import com.prolog.upcloud.base.wh.operate.constant.WhOperataConstant;
import com.prolog.upcloud.base.wh.operate.convert.ReplenishConvert;
import com.prolog.upcloud.base.wh.operate.core.RestMsgHelper;
import com.prolog.upcloud.base.wh.operate.dao.EisLogMapper;
import com.prolog.upcloud.base.wh.operate.dao.WmsReplenishDMapper;
import com.prolog.upcloud.base.wh.operate.dao.WmsReplenishHMapper;
import com.prolog.upcloud.base.wh.operate.dto.AreaInfoByAreaCategoryDTO;
import com.prolog.upcloud.base.wh.operate.dto.ReplenishIncidentallyDTO;
import com.prolog.upcloud.base.wh.operate.dto.ReplenishPassiveDTO;
import com.prolog.upcloud.base.wh.operate.enums.*;
import com.prolog.upcloud.base.wh.operate.model.EisLog;
import com.prolog.upcloud.base.wh.operate.model.WmsReplenishD;
import com.prolog.upcloud.base.wh.operate.model.WmsReplenishH;
import com.prolog.upcloud.base.wh.operate.resource.*;
import com.prolog.upcloud.base.wh.operate.service.WmsReplenishService;
import com.prolog.upcloud.base.wh.operate.utils.BeanConvertUtils;
import com.prolog.upcloud.base.wh.operate.utils.IdUtil;
import com.prolog.upcloud.base.wh.operate.utils.SearchApi;
import com.prolog.upcloud.base.wh.operate.utils.StockHelper;
import com.prolog.upcloud.base.wh.operate.vo.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static com.prolog.upcloud.base.wh.operate.constant.AreaCategoryConstant.AGVLHK;
import static com.prolog.upcloud.base.wh.operate.constant.BillNoConstant.REPLENISH_LINE_D;
import static com.prolog.upcloud.base.wh.operate.constant.BizTemplate.*;
import static com.prolog.upcloud.base.wh.operate.constant.EisConstant.*;
import static com.prolog.upcloud.base.wh.operate.enums.ReplenishBusinessType.INCIDENTALLY;
import static com.prolog.upcloud.base.wh.operate.enums.ReplenishMoveFlag.UNSHELVE;
import static com.prolog.upcloud.base.wh.operate.enums.StoreRoom.ZJK;

/**
 * @author wujunjie
 * @date 2021-05-14
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class ReplenishServiceImpl implements WmsReplenishService {

    public static final String WMS_REPLENISH_AUTO_NUM = "wmsReplenishAutoNum";
    private final WmsReplenishDMapper dMapper;

    private final WmsReplenishHMapper hMapper;

    private final WarehouseClient warehouseClient;

    private final StockResource stockResource;

    private final ItemResource itemResource;

    private final UserTemplate userTemplate;

    private final StrategyClient strategyClient;

    private final OutBoundClient outBoundClient;

    private final ChainResource chainResource;

    @Autowired
    private WarehouseClient warehouseResource;
    @Autowired
    private DeviceLocatorHandler deviceLocatorHandler;

    @Value("${prolog.integration.instockCallId:288}")
    private String instockCallId;

    @Value("${prolog.integration.instockCallStatusId}")
    private String instockCallStatusId;

    @Value("${prolog.integration.instockTransforId:284}")
    private String instockTransforId;

    @Autowired
    private IntegrationApi integrationApi;

    @Resource
    private ReplenishConvert replenishConvert;

    @Resource
    private ControlClient controlClient;

    @Resource
    private CommonResource commonResource;

    @Resource
    private EisLogMapper eisLogMapper;

    @Resource
    private EisHandler eisHandler;

    @Autowired
    private SearchApi searchApi;

    @Autowired
    private StrategyClient strategyResource;


    private static final Logger logger = LoggerFactory.getLogger(ReplenishServiceImpl.class);

    @Override
    @TxTransaction
    @Transactional(rollbackFor = Exception.class)
    public List<ReplenishPassiveDTO> replenishPassive(ReplenishPassiveSaveVO saveVO) {
        if(saveVO == null){
            return null;
        }
        List<ReplenishPassiveSaveDetailVO> details = saveVO.getDetails();
        if(details == null || details.size() <= 0){
            return null;
        }
        //查询商品信息
        List<String> itemIds = details.stream().map(ReplenishPassiveSaveDetailVO::getItemId).distinct().collect(Collectors.toList());
        RestMessage<List<WaveItemInfoVO>> itemInfoMsg = itemResource.getWaveItemInfo(itemIds);
        if(itemInfoMsg == null || !itemInfoMsg.isSuccess()){
            throw new BizException("调用item服务查询商品信息失败!");
        }
        List<WaveItemInfoVO> waveItemInfoVOS = itemInfoMsg.getData();
        if(waveItemInfoVOS == null || waveItemInfoVOS.size() <= 0){
            throw new BizException("商品id:"+JSONObject.toJSONString(itemIds)+"不存在!");
        }
        //A品
        List<String> aTypeItems = waveItemInfoVOS.stream().filter(item -> "A".equals(item.getAbcType())).map(WaveItemInfoVO :: getItemId).collect(Collectors.toList());

        List<ReplenishPassiveSaveDetailVO> aVos = new ArrayList<>();
        List<ReplenishPassiveSaveDetailVO> bcVos = new ArrayList<>();
        if(aTypeItems != null && aTypeItems.size() > 0){
            aVos = details.stream().filter(item -> aTypeItems.contains(item.getItemId())).collect(Collectors.toList());
            bcVos = details.stream().filter(item -> !aTypeItems.contains(item.getItemId())).collect(Collectors.toList());
        }else {
            bcVos = details;
        }

        List<WmsReplenishD> ds = new ArrayList<>();
        if(aVos.size() > 0){
            saveVO.setDetails(aVos);
            ds.addAll(handleReplenishPassive(saveVO));
        }
        if(bcVos.size() > 0){
            saveVO.setDetails(bcVos);
            ds.addAll(handleReplenishPassive(saveVO));
        }
        return ds.stream().map(x -> {
            ReplenishPassiveDTO dto = new ReplenishPassiveDTO();
            dto.setId(x.getId());
            dto.setHeaderId(x.getHeaderId());
            dto.setDetailId(x.getDetailId());
            return dto;
        }).collect(Collectors.toList());
    }

    @Override
    @TxTransaction
    @Transactional(rollbackFor = Exception.class)
    public String replenishUrgent(ReplenishUrgentSaveVO saveVO) {
        log.info("紧急补货入参：{}",JSONObject.toJSONString(saveVO));
        //转换主信息
        WmsReplenishH wmsReplenishH = BeanConvertUtils.convertTo(saveVO, WmsReplenishH::new, (s, t) -> {
            t.setBillType(ReplenishBillType.REPLENISH.name());
            t.setSourceType(ReplenishSourceType.WMS.name());
            t.setBusinessType(ReplenishBusinessType.URGENT.name());
        });
        UserDTO userDTO = userTemplate.getUserDTO();
        if(userDTO==null){
            userDTO = new UserDTO();
            userDTO.setId("eis");
            userDTO.setUsername("eis");
            userDTO.setNickname("eis");
        }
        final UserDTO user = userDTO;
        ModelUtils.setBaseInfo(wmsReplenishH, ModelUtils.OpType.INSERT, user);

        //查找仓库信息
        RestMessage<List<BaseWmsWarehouseMst>> warehouseResult = warehouseClient.getWarehouseBasicInfo(new String[]{wmsReplenishH.getWarehouseId()});
        if (warehouseResult.isSuccess() && warehouseResult.getData() != null && !warehouseResult.getData().isEmpty()) {
            BaseWmsWarehouseMst warehouseMst = warehouseResult.getData().get(0);
            wmsReplenishH.setDcId(warehouseMst.getDcId());
        } else {
            throw new BizException("未找到有效的仓库信息");
        }
        wmsReplenishH.setBillDate(new Date());
        wmsReplenishH.setAutoBillNum(SequenceApi.getInstance().next("wmsReplenishAutoNum"));
        wmsReplenishH.setCustomBillNum(wmsReplenishH.getAutoBillNum());
        wmsReplenishH.setWaveId(saveVO.getWaveId());
        hMapper.save(wmsReplenishH);

        //商品编码
        String[] itemIds = saveVO.getDetails().stream().map(ReplenishUrgentSaveDetailVO::getItemId).toArray(String[]::new);

        RestMessage<List<ItemBaseInfoVO>> itemResult = itemResource.getItemBaseList(itemIds);
        if (!itemResult.isSuccess() || itemResult.getData() == null || itemResult.getData().isEmpty()) {
            throw new BizException("未找到有效的商品信息");
        }


        //库位Id
        String[] locatorId = Stream.concat(saveVO.getDetails().stream().map(ReplenishUrgentSaveDetailVO::getWhLocatorId), saveVO.getDetails().stream().map(ReplenishUrgentSaveDetailVO::getToWhLocatorId)).distinct().toArray(String[]::new);

        //查找货位限定
        RestMessage<List<WarehouseLocatorBasicInfo>> locatorResult = warehouseClient.getLocationBasicInfos(locatorId);

        //批次ID
        String[] batchIds = saveVO.getDetails().stream().map(ReplenishUrgentSaveDetailVO::getBatchId).toArray(String[]::new);
        RestMessage<List<BatchNumberListVO>> batchResult = stockResource.getBatchNumberBasicInfo(batchIds);

        AtomicInteger i = new AtomicInteger(1);
        List<ReplenishUrgentSaveDetailVO> details = new ArrayList<>();
        for (ReplenishUrgentSaveDetailVO detail : saveVO.getDetails()) {
            ItemBaseInfoVO itemVo = itemResult.getData().stream().filter(item -> item.getItemId().equals(detail.getItemId())).findFirst().orElse(null);
            if (null == itemVo){
                throw new BizException("获取商品信息失败！");
            }
//            BigDecimal pcs = detail.getPieceQuantity().divide(itemVo.getCaseConversionRate(), RoundingMode.UP);
//            BigDecimal totalQty = detail.getPieceQuantity();
//            BigDecimal caseConversionRate = itemVo.getCaseConversionRate();
//            for (int m = 0; m < pcs.intValue(); m ++){
//                ReplenishUrgentSaveDetailVO tempDetail = new ReplenishUrgentSaveDetailVO();
//                BeanUtil.copyProperties(detail, tempDetail);
//                if (totalQty.compareTo(caseConversionRate) >=0) {
//                    tempDetail.setPieceQuantity(caseConversionRate);
//                }else {
//                    tempDetail.setPieceQuantity(totalQty);
//                }
//                totalQty = totalQty.subtract(caseConversionRate);
//                details.add(tempDetail);
//            }
            ReplenishUrgentSaveDetailVO tempDetail = new ReplenishUrgentSaveDetailVO();
            BeanUtil.copyProperties(detail, tempDetail);
            tempDetail.setPieceQuantity(detail.getPieceQuantity());
            details.add(tempDetail);
        }
        //转换明细
        List<WmsReplenishD> dList = details.stream().map(x -> BeanConvertUtils.convertTo(x, WmsReplenishD::new, (s, t) -> {
            t.setHeaderId(wmsReplenishH.getHeaderId());
            //商品信息
            Optional<ItemBaseInfoVO> itemOptional = itemResult.getData().stream().filter(item -> item.getItemId().equals(x.getItemId())).findFirst();

            itemOptional.ifPresent(itemBaseInfoVO -> {
                t.setItemCode(itemBaseInfoVO.getItemCode());
                t.setItemName(itemBaseInfoVO.getItemName());
                t.setUom(itemBaseInfoVO.getCaseUom());
                t.setConversionRate(itemBaseInfoVO.getCaseConversionRate());
                //计算整件数  最小单位数/转换因子
                t.setQuantity(t.getPieceQuantity().divide(itemBaseInfoVO.getCaseConversionRate(), 0));

                t.setUom(itemBaseInfoVO.getCaseUom());

                if(itemBaseInfoVO.getLength().compareTo(BigDecimal.ZERO) == 0){
                    t.setLength(BigDecimal.ZERO);
                }else {
                    t.setLength(itemBaseInfoVO.getLength());
                }
                if(itemBaseInfoVO.getWidth().compareTo(BigDecimal.ZERO) == 0){
                    t.setWidth(BigDecimal.ZERO);
                }else {
                    t.setWidth(itemBaseInfoVO.getWidth());
                }
                if(itemBaseInfoVO.getHeight().compareTo(BigDecimal.ZERO) == 0){
                    t.setHeight(BigDecimal.ZERO);
                }else {
                    t.setHeight(itemBaseInfoVO.getHeight());
                }
                if(itemBaseInfoVO.getVolume().compareTo(BigDecimal.ZERO) == 0){
                    t.setVolume(BigDecimal.ZERO);
                }else {
                    t.setVolume(itemBaseInfoVO.getVolume());
                }
                if(itemBaseInfoVO.getWeight().compareTo(BigDecimal.ZERO) == 0){
                    t.setWeight(BigDecimal.ZERO);
                }else {
                    t.setWeight(itemBaseInfoVO.getWeight());
                }

                t.setIsCaseOrPiece(itemBaseInfoVO.getIsCaseOrPiece());
            });
            ModelUtils.setBaseInfo(t, ModelUtils.OpType.INSERT, user);

            if (locatorResult.isSuccess() && locatorResult.getData() != null) {
                //源货位  整件
                Optional<WarehouseLocatorBasicInfo> locatorOptional = locatorResult.getData().stream().filter(locator -> t.getWhLocatorId().equals(locator.getWarehouseLocatorId()) && locator.getWarehouseLocatorId().equals(x.getWhLocatorId())).findFirst();
                if (locatorOptional.isPresent()) {
                    WarehouseLocatorBasicInfo locatorBasicInfo = locatorOptional.get();
                    t.setWhAreaCategory(locatorBasicInfo.getWarehouseAreaCategory());
                    t.setWhAreaCode(locatorBasicInfo.getWarehouseAreaCode());
                    t.setWhAreaId(locatorBasicInfo.getWarehouseAreaId());
                    t.setWhLocatorCode(locatorBasicInfo.getWarehouseLocatorCode());
                    t.setWhLocatorId(locatorBasicInfo.getWarehouseLocatorId());
                    t.setWhWarehouseLocatorType(locatorBasicInfo.getWarehouseLocatorType());
                    t.setBldgId(locatorBasicInfo.getBldgId());
                    t.setBldgCode(locatorBasicInfo.getBldgCode());
                    t.setBldgName(locatorBasicInfo.getBldgName());
                    t.setFloorNum(locatorBasicInfo.getFloorNum());
                    t.setItemPlaceType(locatorBasicInfo.getItemPlaceType());
                }


                //目的库位  散件
                Optional<WarehouseLocatorBasicInfo> tolocatorOptional = locatorResult.getData().stream().filter(locator -> t.getToWhLocatorId().equals(locator.getWarehouseLocatorId()) && locator.getWarehouseLocatorId().equals(x.getToWhLocatorId())).findFirst();
                if (tolocatorOptional.isPresent()) {
                    WarehouseLocatorBasicInfo locatorBasicInfo = tolocatorOptional.get();
                    t.setToWhAreaCategory(locatorBasicInfo.getWarehouseAreaCategory());
                    t.setToWhAreaCode(locatorBasicInfo.getWarehouseAreaCode());
                    t.setToWhAreaId(locatorBasicInfo.getWarehouseAreaId());
                    t.setToWhLocatorCode(locatorBasicInfo.getWarehouseLocatorCode());
                    t.setToWhLocatorId(locatorBasicInfo.getWarehouseLocatorId());
                    t.setToWhWarehouseLocatorType(locatorBasicInfo.getWarehouseLocatorType());
                    t.setToFloorNum(locatorBasicInfo.getFloorNum());
                    t.setToItemPlaceType(locatorBasicInfo.getItemPlaceType());
                }
            }

            if (batchResult.isSuccess() && batchResult.getData() != null) {
                //批次信息
                Optional<BatchNumberListVO> batchOptional = batchResult.getData().stream().filter(batch -> batch.getBatchId().equals(x.getBatchId())).findFirst();
                if (batchOptional.isPresent()) {
                    BatchNumberListVO batchInfo = batchOptional.get();
                    t.setBatchNum(batchInfo.getBatchNum());
                    t.setBatchExpiredDate(batchInfo.getBatchExpiredDate());
                    t.setBatchProductionDate(batchInfo.getBatchProductionDate());
                    t.setManufacturerLotNum(batchInfo.getManufacturerLotNum());
                    t.setShelfLifeDays(batchInfo.getShelfLifeDays());
                    t.setBatchExpiredDateShow(batchInfo.getBatchExpiredDateShow());
                    t.setBatchProductionDateShow(batchInfo.getBatchProductionDateShow());
                }
            }
            if(!StringUtils.isBlank(x.getToContainerCode())){
                t.setToContainerCode(x.getToContainerCode());
            }
            if(!StringUtils.isBlank(x.getContainerCode())){
                t.setContainerCode(x.getContainerCode());
            }
            t.setLineNum(String.valueOf(i.getAndIncrement()));
            t.setStatus(ReplenishStatus.CREATE.getStatus());
            t.setOutboundInstruct(OUTBOUND_INSTRUCT_UN_DELIVERY);

        })).collect(Collectors.toList());

        dMapper.saveBatch(dList);
//        //补货数量审核
//        qtyCheck(dList);
        doMoveTask(wmsReplenishH, dList);

        doPreStock(wmsReplenishH.getWarehouseId(), dList);

        List<WmsReplenishD> mjkDList = dList.stream().filter(item -> "MJK".equals(item.getWhAreaCategory()) || "AGVZ".equals(item.getWhAreaCategory())
                    || "AGVQX".equals(item.getWhAreaCategory()) || "AGVSP".equals(item.getWhAreaCategory()) || "AGVZY".equals(item.getWhAreaCategory()))
                .collect(Collectors.toList());
        if(mjkDList != null && mjkDList.size() > 0) {
            if(!"N".equals(saveVO.getIsSendEis())){
                OutBoundRequestVO eisPushTaskV2VO = getEisPushTaskV2VO(wmsReplenishH, mjkDList);
                log.info("补货推送eis入参：{}", JSONUtil.toJsonStr(eisPushTaskV2VO));
                eisHandler.outBoundRequest(CollUtil.toList(eisPushTaskV2VO));
            }

        }
        List<String> detailIds = dList.stream().map(WmsReplenishD::getDetailId).collect(Collectors.toList());
        return String.join(",",detailIds);
    }

    private void qtyCheck(List<WmsReplenishD> dList) {
        List<WmsInvStockQtyQueryVO> queryVOS = new ArrayList<>();
        for (WmsReplenishD wmsReplenishD : dList) {
            WmsInvStockQtyQueryVO ls = new WmsInvStockQtyQueryVO();
            ls.setEnterpriseId(wmsReplenishD.getEnterpriseId());
            ls.setLocatorId(wmsReplenishD.getWhLocatorId());
            ls.setWarehouseId(wmsReplenishD.getWarehouseId());
            ls.setCargoOwnerId(wmsReplenishD.getCargoOwnerId());
            ls.setBatchId(wmsReplenishD.getBatchId());
            ls.setItemId(wmsReplenishD.getItemId());

        }
        List<WmsInvStockQtyVO> limitLocators = RestMsgHelper.unpack(stockResource.getAvailableQuantityList(queryVOS));

        for (WmsReplenishD detVO : dList) {
            for (WmsInvStockQtyVO limitLocator : limitLocators) {
                if (limitLocator.getItemId().equals(detVO.getItemId()) && limitLocator.getBatchId().equals(detVO.getBatchId()) && limitLocator.getLocatorId().equals(detVO.getWhLocatorId())) {
                    if (detVO.getPieceQuantity().compareTo(limitLocator.getAvailableQuantity()) > 0) {
                        throw new BizException("实际数量:" + limitLocator.getAvailableQuantity() + "，补货数量" + detVO.getPieceQuantity());
                    }
                }
            }
        }
    }

    @Override
    @TxTransaction
    @Transactional(rollbackFor = Exception.class)
    public void replenishInitiative(ReplenishInitiativeSaveVO saveVO) {

        WmsReplenishH wmsReplenishH = BeanConvertUtils.convertTo(saveVO, WmsReplenishH::new, (s, t) -> {
            t.setBillType(ReplenishBillType.REPLENISH.name());
            t.setSourceType(ReplenishSourceType.WMS.name());
            t.setBusinessType(ReplenishBusinessType.INITIATIVE.name());
        });
        UserDTO user = userTemplate.getUserDTO();
        ModelUtils.setBaseInfo(wmsReplenishH, ModelUtils.OpType.INSERT, user);

        List<BaseWmsWarehouseMst> warehouses = RestMsgHelper.unpack(warehouseClient.getWarehouseBasicInfo(new String[]{wmsReplenishH.getWarehouseId()}));
        Assert.notEmpty(warehouses, "未找到有效的仓库信息");

        wmsReplenishH.setDcId(warehouses.get(0).getDcId());
        wmsReplenishH.setBillDate(DateUtil.date());
        wmsReplenishH.setAutoBillNum(SequenceApi.getInstance().next("wmsReplenishAutoNum"));
        wmsReplenishH.setCustomBillNum(wmsReplenishH.getAutoBillNum());
        wmsReplenishH.setWaveId(saveVO.getWaveId());
        hMapper.save(wmsReplenishH);

        List<WmsReplenishD> dList = new ArrayList<>();

        List<BaseWmsWarehouseArea> areas = RestMsgHelper.unpack(warehouseClient.getByAreaCodes(wmsReplenishH.getWarehouseId(), new String[0]));
        Assert.notEmpty(areas, "获取库区信息失败");

        BaseWmsWarehouseArea area = areas.stream().filter(a -> AGVLHK.equals(a.getWarehouseAreaCategory())).findFirst().orElseThrow(() -> new BizException("请配置零货库区"));

        List<ReplenishRuleDTO> rules = RestMsgHelper.unpack(strategyClient.getReplenishRuleList(saveVO.getWarehouseId(), null, area.getWarehouseAreaId()));
        Assert.notEmpty(rules, "获取补货规则失败");

        String[] itemIds = saveVO.getDetails().stream().map(ReplenishInitiativeSaveDetailVO::getItemId).toArray(String[]::new);

        List<ItemBaseInfoVO> items = RestMsgHelper.unpack(itemResource.getItemBaseList(itemIds));

        Map<String, BigDecimal> locatorChooseQtyMap = new HashMap<>();

        List<ReplenishInitiativeSaveDetailVO> details = new ArrayList<>();
        for (ReplenishInitiativeSaveDetailVO detail : saveVO.getDetails()) {
            ItemBaseInfoVO itemVo = items.stream().filter(item -> item.getItemId().equals(detail.getItemId())).findFirst().orElse(null);
            if (null == itemVo){
                throw new BizException("获取商品信息失败！");
            }
//            BigDecimal pcs = detail.getPieceQuantity().divide(itemVo.getCaseConversionRate(), RoundingMode.UP);
//            BigDecimal totalQty = detail.getPieceQuantity();
//            BigDecimal caseConversionRate = itemVo.getCaseConversionRate();
//            for (int m = 0; m < pcs.intValue(); m ++){
//                ReplenishInitiativeSaveDetailVO tempDetail = new ReplenishInitiativeSaveDetailVO();
//                BeanUtil.copyProperties(detail, tempDetail);
//                if (totalQty.compareTo(caseConversionRate) >=0) {
//                    tempDetail.setPieceQuantity(caseConversionRate);
//                }else {
//                    tempDetail.setPieceQuantity(totalQty);
//                }
//                totalQty = totalQty.subtract(caseConversionRate);
//                details.add(tempDetail);
//            }
            ReplenishInitiativeSaveDetailVO tempDetail = new ReplenishInitiativeSaveDetailVO();
            BeanUtil.copyProperties(detail, tempDetail);
            tempDetail.setPieceQuantity(detail.getPieceQuantity());
            details.add(tempDetail);
        }

        for (ReplenishInitiativeSaveDetailVO detail : details) {

            ItemBaseInfoVO item = items.stream().filter(t -> t.getItemId().equals(detail.getItemId())).findFirst().orElseThrow(() -> new BizException("未找到有效的商品信息"));

            AtomicInteger i = new AtomicInteger(1);

            List<StockLocatorDTO> stockLocators = getStockLocators(saveVO, locatorChooseQtyMap, detail, item, rules);

            log.info("找到商品库存===> {}", JSONObject.toJSONString(stockLocators));

            List<StockLocatorDTO> limitLocators = getLimitLocators(saveVO, user, rules, detail, stockLocators, item);
            Assert.notEmpty(limitLocators, "商品【{}】补货失败，未匹配到有效的货位！", item.getItemName());

            List<StockReplenishTaskDTO> replenishTaskList = getListByReplenishRule(rules, detail, item, stockLocators, limitLocators);
            Assert.notEmpty(replenishTaskList, "商品【{}】补货失败，请检查货位限定或补货规则是否设置正确！", item.getItemName());

            String[] locatorId = Stream.concat(replenishTaskList.stream().map(StockReplenishTaskDTO::getSrcWarehouseLocatorId), replenishTaskList.stream().map(StockReplenishTaskDTO::getDestWarehouseLocatorId)).toArray(String[]::new);
            List<WarehouseLocatorBasicInfo> locators = RestMsgHelper.unpack(warehouseClient.getLocationBasicInfos(locatorId));

            String[] batchIds = replenishTaskList.stream().map(StockReplenishTaskDTO::getBatchId).toArray(String[]::new);
            List<BatchNumberListVO> batchNumbers = RestMsgHelper.unpack(stockResource.getBatchNumberBasicInfo(batchIds));

            for (StockReplenishTaskDTO task : replenishTaskList) {
                WmsReplenishD d = generateWmsReplenishD(wmsReplenishH, user, detail, i, item, locators, batchNumbers, task);
                dList.add(d);

                //缓存补货数量
                locatorChooseQtyMap.put(String.format("%s:%s", task.getSrcWarehouseLocatorId(), task.getBatchId()), task.getPieceQuantity());
            }
        }
        if (dList.isEmpty()) {
            throw new BizException("补货失败,请检查货位限定或补货规则是否设置正确");
        }

        dMapper.saveBatch(dList);
        //补货数量审核
        //qtyCheck(dList);
        doMoveTask(wmsReplenishH, dList);

        doPreStock(wmsReplenishH.getWarehouseId(), dList);

//        if (CollUtil.isNotEmpty(list)) {
//            mqProducer.pushTask(list);
//        }

        List<WmsReplenishD> mjkDList = dList.stream().filter(item -> "MJK".equals(item.getWhAreaCategory()) || "AGVZ".equals(item.getWhAreaCategory())
                || "AGVQX".equals(item.getWhAreaCategory()) || "AGVSP".equals(item.getWhAreaCategory()) || "AGVZY".equals(item.getWhAreaCategory()))
                .collect(Collectors.toList());
        if(mjkDList != null && mjkDList.size() > 0 && !"N".equals(saveVO.getIsSendEis())){
            OutBoundRequestVO eisPushTaskV2VO = getEisPushTaskV2VO(wmsReplenishH, mjkDList);
            log.info("补货推送eis入参：{}",JSONUtil.toJsonStr(eisPushTaskV2VO));
            eisHandler.outBoundRequest(CollUtil.toList(eisPushTaskV2VO));
        }

    }

    @Override
    public void replenishPre(List<ReplenishPreVO> vos) {
        log.info("预补货入参：{}",JSON.toJSONString(vos));
        //校验入参
        if(vos == null && vos.size() <= 0){
            throw new ParameterException("入参不能为空!");
        }
        UserDTO user = userTemplate.getUserDTO();
        if (user == null) {
            throw new BizException("请先登录!");
        }

        //查询商品信息
        List<String> itemIds = vos.stream().map(ReplenishPreVO::getItemId).distinct().collect(Collectors.toList());
        RestMessage<List<WaveItemInfoVO>> itemInfoMsg = itemResource.getWaveItemInfo(itemIds);
        if(itemInfoMsg == null || !itemInfoMsg.isSuccess()){
            throw new BizException("调用item服务查询商品信息失败!");
        }
        List<WaveItemInfoVO> waveItemInfoVOS = itemInfoMsg.getData();
        if(waveItemInfoVOS == null || waveItemInfoVOS.size() <= 0){
            throw new BizException("商品id:"+JSONObject.toJSONString(itemIds)+"不存在!");
        }
        //A品
        List<String> aTypeItems = waveItemInfoVOS.stream().filter(item -> "A".equals(item.getAbcType())).map(WaveItemInfoVO :: getItemId).collect(Collectors.toList());

        List<ReplenishPreVO> aVos = new ArrayList<>();
        List<ReplenishPreVO> bcVos = new ArrayList<>();
        if(aTypeItems != null && aTypeItems.size() > 0){
            aVos = vos.stream().filter(item -> aTypeItems.contains(item.getItemId())).collect(Collectors.toList());
            bcVos = vos.stream().filter(item -> !aTypeItems.contains(item.getItemId())).collect(Collectors.toList());
        }else {
            bcVos = vos;
        }

        if(aVos.size() > 0){
            List<ReplenishPreVO> initiatives = aReplenishPre(aVos);
            if(initiatives.size() >= 0){
                bcReplenishPre(initiatives);
            }
        }

        if(bcVos.size() > 0){
            bcReplenishPre(bcVos);
        }
    }

    @Override
    public void autoBcPre(Map<String,Object> voMap) {

        UserDTO user = userTemplate.getUserDTO();
        Integer isAutoPre = 0; // 默认自动波次下发
        RestMessage<Map<String, BusinessControl>> rest =
                strategyResource.getControlByCodes(new String[]{"is_auto_bc_pre"}, user.getUserBizModel().getDefaultWarehouseId(), null,
                        user.getEnterpriseId(), null);
        if (rest.isSuccess() && rest.getData() != null) {
            Map<String,BusinessControl> conMap = rest.getData();
            if (conMap.containsKey("is_auto_bc_pre")) {
                isAutoPre = Integer.valueOf(conMap.get("is_auto_bc_pre").getRegValue());
            }
        }
        if(isAutoPre == 0){
            return;
        }

        List<Map> searchForMap = null;
        try {
            Map<String, Object> map = MapUtils.put("warehouseId", user.getUserBizModel().getDefaultWarehouseId())
                    .put("currentUser.enterpriseId", user.getEnterpriseId()).getMap();
            log.info("BC品定时补货调用查询服务查询BC品容器库存列表入参：{}",JSONObject.toJSONString(map));
            searchForMap = searchApi.searchForMap("querylocator_b", map);
            log.info("BC品定时补货调用查询服务查询BC品容器库存返回结果：{}",JSONObject.toJSONString(searchForMap));
        }catch (Exception e){
            e.printStackTrace();
            log.info("调用查询服务：querylocator_b失败!");
        }

        if(searchForMap == null || searchForMap.size() <= 0){
            return;
        }
        ReplenishInitiativeSaveVO replenishInitiativeSaveVO = new ReplenishInitiativeSaveVO();
        replenishInitiativeSaveVO.setWarehouseId(user.getUserBizModel().getDefaultWarehouseId());
        List<ReplenishInitiativeSaveDetailVO> details = new ArrayList<>();
        Integer num =  (Integer) voMap.get("num");
        int count = num > searchForMap.size() ? searchForMap.size() : num;
        for (int i = 0; i < count; i++) {
            Map map = searchForMap.get(i);
            ReplenishInitiativeSaveDetailVO detailVO = new ReplenishInitiativeSaveDetailVO();
            detailVO.setCargoOwnerId((String) map.get("cargoOwnerId"));
            detailVO.setItemId((String) map.get("itemId"));
            detailVO.setPieceQuantity(new BigDecimal((Double) map.get("totalReplenishQuantity")));
            detailVO.setWarehouseAreaCategory((String) map.get("warehouseCategoryCode"));
            details.add(detailVO);
        }

        replenishInitiativeSaveVO.setDetails(details);
        log.info("BC品定时补货-主动补货入参：{}",JSONUtil.toJsonStr(replenishInitiativeSaveVO));
        try {
            this.replenishInitiative(replenishInitiativeSaveVO);
        }catch (Exception e){
            e.printStackTrace();
            log.info("BC品定时补货-主动补货失败：{}",e.getMessage());
        }
    }

    @Override
    public List<MarkTaskItemVO> exceedTask(List<MarkTaskItemVO> exceeds) {

        List<MarkTaskItemVO> exceedSubList = new ArrayList<>();

        List<String> taskLineIds = exceeds.stream().map(MarkTaskItemVO::getTaskLineId).collect(Collectors.toList());
        List<WmsReplenishD> wmsReplenishDS = dMapper.findByIds(WmsReplenishD.class, taskLineIds.toArray(), null);
        //查询品批货位可用库存
        List<WmsInvStockQtyQueryVO> qtyQueries = new ArrayList<>();
        for (WmsReplenishD wmsReplenishD : wmsReplenishDS) {
            WmsInvStockQtyQueryVO vo = new WmsInvStockQtyQueryVO();
            vo.setEnterpriseId(wmsReplenishD.getEnterpriseId());
            vo.setWarehouseId(wmsReplenishD.getWarehouseId());
            vo.setItemId(wmsReplenishD.getItemId());
            vo.setBatchId(wmsReplenishD.getBatchId());
            vo.setLocatorId(wmsReplenishD.getWhLocatorId());
            qtyQueries.add(vo);
        }

        RestMessage<List<WmsInvStockQtyVO>> availableQuantityListMsg = stockResource.getAvailableQuantityListV2(qtyQueries);
        log.info("超补，查询库存返回结果：{}", JSON.toJSONString(availableQuantityListMsg));
        if(availableQuantityListMsg == null || !availableQuantityListMsg.isSuccess()){
            log.info("调用inventory服务服务商品可用库存失败!");
            return exceedSubList;
        }
        List<WmsInvStockQtyVO> wmsInvStockQtyVOS = availableQuantityListMsg.getData();
        if(wmsInvStockQtyVOS == null || wmsInvStockQtyVOS.size() <= 0){
            log.info("可用库存不足，不允许超补!");
            return exceedSubList;
        }

        //更新库存预占入参列表
        List<Map<String, Object>> updateQtyVos = new ArrayList<>();

        for (MarkTaskItemVO exceed : exceeds) {
            WmsReplenishD replenishD = wmsReplenishDS.stream().filter(item -> item.getDetailId().equals(exceed.getTaskLineId())).findFirst().orElse(null);
            if(replenishD == null){
                log.info("补货明细id:"+exceed.getTaskLineId()+"不存在!");
                continue;
            }
            //查询补货明细同品同批同货位可用库存明细

            BigDecimal alvQty = wmsInvStockQtyVOS.stream().filter(item -> item.getItemId().equals(replenishD.getItemId())
                    && item.getLocatorId().equals(replenishD.getWhLocatorId())
                    && item.getBatchId().equals(replenishD.getBatchId())).map(WmsInvStockQtyVO::getAvailableQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
            if(exceed.getExceedQyt().compareTo(alvQty) > 0){
                log.info("商品："+replenishD.getItemCode()+"批次："+replenishD.getManufacturerLotNum()+"货位："+replenishD.getWhLocatorCode()
                        +"，可用库存:"+alvQty+"，超补数量："+exceed.getExceedQyt()+"，超补失败!");
                continue;
            }

            //更新补货明细数量
            replenishD.setPieceQuantity(replenishD.getPieceQuantity().add(exceed.getExceedQyt()));
            replenishD.setQuantity(replenishD.getPieceQuantity().divide(replenishD.getConversionRate()).setScale(2,BigDecimal.ROUND_HALF_UP));
            dMapper.updateMapById(replenishD.getDetailId(),MapUtils.put("pieceQuantity",replenishD.getPieceQuantity()).put("quantity",replenishD.getQuantity()).getMap(),WmsReplenishD.class);


            Map<String, Object> map = new HashMap<>();
            map.put("lineBillId",replenishD.getDetailId());
            map.put("qty",exceed.getExceedQyt());
            map.put("containerCode",exceed.getContainerNo());

            updateQtyVos.add(map);

            exceedSubList.add(exceed);
        }

        stockResource.addQty(updateQtyVos);
        outBoundClient.changeTaskQty(exceeds);

        return exceedSubList;
    }

    @Override
    public List<OutBoundRequestVO> getRepSendEisParamsByWaveId(String waveId) {
        List<WmsReplenishH> hs = hMapper.findByMap(MapUtils.put("waveId", waveId).getMap(), WmsReplenishH.class);
        if(hs == null){
            return null;
        }
        List<String> headerIds = hs.stream().map(WmsReplenishH::getHeaderId).collect(Collectors.toList());
        Criteria criteria = Criteria.forClass(WmsReplenishD.class);
        criteria.restrictionBuilder().in("headerId", headerIds.toArray());
        criteria.buildRestriction();
        List<WmsReplenishD> dList = dMapper.findByCriteria(criteria);
        if(dList == null){
            return null;
        }
        List<OutBoundRequestVO> list = new ArrayList<>();
        for (WmsReplenishH h : hs) {
            List<WmsReplenishD> replenishDS = dList.stream().filter(item -> item.getHeaderId().equals(h.getHeaderId())).collect(Collectors.toList());
            OutBoundRequestVO eisPushTaskV2VO = this.getEisPushTaskV2VO(h,replenishDS);
            list.add(eisPushTaskV2VO);
        }
        log.info("通过波次id获取补货任务推送eis入参列表：{}",JSON.toJSONString(list));
        return list;
    }

    //    private EisPushTaskVO getEisPushTaskVO(WmsReplenishH wmsReplenishH, WmsReplenishD d) {
//        EisPushTaskVO vo = new EisPushTaskVO();
//        vo.setWarehouseId(wmsReplenishH.getWarehouseId());
//        vo.setOwnerId(d.getCargoOwnerId());
//        vo.setTaskId(d.getHeaderId());
//        vo.setSeqNo(d.getDetailId());
//        vo.setWorkType(EisWorkType.WT_REPLENISH);
//        vo.setBusinessType(EisBusinessType.BT_REPLENISH);
//        vo.setItemId(d.getItemId());
//        vo.setQty(d.getPieceQuantity());
//        vo.setLotId(d.getBatchId());
//        vo.setPriority(0);
//        vo.setWmslocationCode(EIS_LOCATION);
//        vo.setMsgId(d.getDetailId());
//        vo.setContainerNo(d.getContainerCode());
//        return vo;
//    }

    /**
     * 补货推送eis
     * @param wmsReplenishH
     * @param dList
     * @return
     */
    private OutBoundRequestVO getEisPushTaskV2VO(WmsReplenishH wmsReplenishH, List<WmsReplenishD> dList){
        OutBoundRequestVO eisPushTaskV2VO = new OutBoundRequestVO();
        eisPushTaskV2VO.setMsgId(UUIDUtils.generateWithoutSeperator());
        eisPushTaskV2VO.setGroupOrderPolicy("0");
        eisPushTaskV2VO.setWarehouseId(wmsReplenishH.getWarehouseId());
        List<OutBoundRequestItemVO> subListBeans = new ArrayList<>();

        Set<String> itemIds = dList.stream().map(WmsReplenishD::getItemId).collect(Collectors.toSet());
        List<BaseMtlItemMst> items = RestMsgHelper.unpack(itemResource.getListByIds(itemIds));
        if (CollUtil.isEmpty(items)) {
            throw new BizException("补货查询商品信息失败！");
        }
        for (WmsReplenishD d : dList) {
            //商品信息
            BaseMtlItemMst itemMst = items.stream().filter(item -> item.getItemId().equals(d.getItemId())).findFirst().orElse(null);
            if (null == itemMst) {
                throw new BizException("根据商品ID【" + d.getItemId() + "】查询商品资料失败！");
            }
            OutBoundRequestItemVO subListBean = new OutBoundRequestItemVO();
            subListBean.setWorkType(OutBoundRequestEnum.INV_RTM.getWorkType());
            subListBean.setBusinessType(OutBoundRequestEnum.INV_RTM.getBusinessType());
            subListBean.setTaskId(d.getHeaderId());
            subListBean.setBillNo(wmsReplenishH.getAutoBillNum());
            subListBean.setStr1("1");
            subListBean.setStr2("1");
            if(!StringUtils.isBlank(d.getContainerCode())){
                subListBean.setContainerNo(d.getContainerCode());
            }
            subListBean.setTaskLineId(d.getDetailId());
            if(ReplenishBusinessType.URGENT.name().equals(wmsReplenishH.getBusinessType())){
                subListBean.setPriority(5d);
            }else if(ReplenishBusinessType.INITIATIVE.name().equals(wmsReplenishH.getBusinessType())){
                subListBean.setPriority(0d);
            }else if(ReplenishBusinessType.PASSIVE.name().equals(wmsReplenishH.getBusinessType())){
                subListBean.setPriority(10d);
            }
            subListBean.setOwnerId(d.getCargoOwnerId());
            subListBean.setEnterpriseId(d.getEnterpriseId());
            subListBean.setItemId(d.getItemId());
            String packId = "";
            if (CollUtil.isNotEmpty(itemMst.getBaseMtlItemUomConversionsList())) {
                packId = itemMst.getBaseMtlItemUomConversionsList().get(0).getItemUomId();
            }
            subListBean.setPackId(packId);
            subListBean.setQty(String.valueOf(d.getPieceQuantity()));
            subListBean.setLotId(d.getBatchId());
            if("MJK".equals(d.getWhAreaCategory())){
                subListBean.setWmsLocationCode("MJZ");
            }else {
                subListBean.setWmsLocationCode(d.getWhAreaCategory());
            }
//            if(!StringUtils.isBlank(isExceed) && "1".equals(isExceed)){
////                subListBean.setIsOver("1");
////            }else {
////                subListBean.setIsOver("0");
////            }
            subListBean.setIsOver("0");
            subListBean.setIsCheck("0");
            subListBeans.add(subListBean);
        }

        eisPushTaskV2VO.setSubList(subListBeans);
        return eisPushTaskV2VO;
    }

    private List<StockLocatorDTO> getStockLocators(ReplenishInitiativeSaveVO saveVO, Map<String, BigDecimal> locatorChooseQtyMap, ReplenishInitiativeSaveDetailVO detail, ItemBaseInfoVO itemBaseInfoVO, List<ReplenishRuleDTO> replenishRules) {

        List<StockLocatorDTO> stockLocators = RestMsgHelper.unpack(stockResource.getItemLocators(saveVO.getWarehouseId(), detail.getCargoOwnerId(), detail.getItemId(), StockStatusEnum.QUALIFIED.getCode()));
        Assert.notEmpty(stockLocators, "未找到商品【%s】库存信息", itemBaseInfoVO.getItemName());

        String[] locationIds = stockLocators.stream().map(StockLocatorDTO::getLocatorId).distinct().toArray(String[]::new);
        List<HashMap<String, Object>> locations = RestMsgHelper.unpack(warehouseClient.getLocationDetailInfos(locationIds));
        Assert.notEmpty(locations, "获取货位消息失败");

        Map<String, List<HashMap<String, Object>>> fixLocators = locations.stream().filter(l -> replenishRules.stream().map(ReplenishRuleDTO::getSrcWarehouseAreaId).collect(Collectors.toList()).contains(String.valueOf(l.get("warehouseAreaId")))).collect(Collectors.groupingBy(l -> String.valueOf(l.get("warehouseAreaId"))));
        Assert.notEmpty(fixLocators, "没有适合补货策略的货位");

        String[] areaIds = replenishRules.stream().sorted(Comparator.comparing(ReplenishRuleDTO::getSort)).map(ReplenishRuleDTO::getSrcWarehouseAreaId).distinct().toArray(String[]::new);
        List<HashMap<String, Object>> maps = Collections.emptyList();
        for (String areaId : areaIds) {
            maps = fixLocators.get(areaId);
            if (CollUtil.isNotEmpty(maps)) {
                break;
            }
        }

        List<HashMap<String, Object>> finalMaps = maps;
        List<StockLocatorDTO> result = stockLocators.stream().filter(s -> finalMaps.stream().map(m -> String.valueOf(m.get("warehouseLocatorId"))).collect(Collectors.toList()).contains(s.getLocatorId())).collect(Collectors.toList());

        return result;
    }

    private List<StockLocatorDTO> getLimitLocators(ReplenishInitiativeSaveVO saveVO, UserDTO user, List<ReplenishRuleDTO> replenishRules, ReplenishInitiativeSaveDetailVO detail, List<StockLocatorDTO> stockLocators, ItemBaseInfoVO item) {

        Set<String> srcAreaIds = stockLocators.stream().filter(x -> "0".equals(x.getLocatorType())).map(StockLocatorDTO::getWarehouseAreaId).collect(Collectors.toSet());

        String[] areaIds = replenishRules.stream().filter(x -> srcAreaIds.contains(x.getSrcWarehouseAreaId())).sorted(Comparator.comparing(ReplenishRuleDTO::getSort)).map(ReplenishRuleDTO::getDestWarehouseAreaId).distinct().toArray(String[]::new);
        Assert.notEmpty(areaIds, "没有匹配到合适的补货配置");

        StockLocatorDTO dto = stockLocators.stream().filter(s -> s.getItemId().equals(detail.getItemId())).findFirst().orElse(null);
        Assert.notNull(dto, "获取物料【{}】批次信息失败", item.getItemName());

        WmsInvStockQtyQueryVO queryVO = new WmsInvStockQtyQueryVO();
        queryVO.setEnterpriseId(user.getEnterpriseId());
        queryVO.setWarehouseId(saveVO.getWarehouseId());
        queryVO.setCargoOwnerId(detail.getCargoOwnerId());
        queryVO.setBatchId(dto.getBatchId());
        queryVO.setStatus(StockStatusEnum.QUALIFIED.getCode());
        queryVO.setItemId(detail.getItemId());

        List<WmsInvStockQtyVO> limitLocators = RestMsgHelper.unpack(stockResource.getAvailableQuantityList(Collections.singletonList(queryVO)));
        Assert.notEmpty(limitLocators, "没有找到匹配商品【{}】补货任务的货位", item.getItemName());

        for (String areaId : areaIds) {
            List<WmsInvStockQtyVO> resultList = limitLocators.stream().filter(l -> l.getWarehouseAreaId().equals(areaId)).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(resultList)) {
                List<StockLocatorDTO> list = new ArrayList<>(resultList.size());
                resultList.forEach(r -> {
                    list.add(replenishConvert.wmsInvStockQtyVO2StockLocatorDTO(r));
                });
                return list;
            }
        }
        //没有零货库存，则补空货位
        WmsInvStockQtyVO wmsInvStockQtyVO = limitLocators.get(0);
        //查询零货货位
        List<BaseWmsWarehouseLocator> wmsWarehouseLocators = getLocatorByAreaCode(wmsInvStockQtyVO.getEnterpriseId(),detail.getWarehouseAreaCategory());
        BaseWmsWarehouseLocator baseWmsWarehouseLocator = wmsWarehouseLocators.get(0);
        StockLocatorDTO stockLocatorDTO = new StockLocatorDTO();
        BeanUtil.copyProperties(wmsInvStockQtyVO,stockLocatorDTO);

        stockLocatorDTO.setAvailableQuantity(BigDecimal.ZERO);
        stockLocatorDTO.setQuantity(BigDecimal.ZERO);

        stockLocatorDTO.setWarehouseAreaId(baseWmsWarehouseLocator.getWarehouseAreaId());
        stockLocatorDTO.setWarehouseAreaCode(baseWmsWarehouseLocator.getWarehouseAreaCode());
        stockLocatorDTO.setWarehouseAreaCategory(baseWmsWarehouseLocator.getWarehouseAreaCategory());
        stockLocatorDTO.setLocatorId(baseWmsWarehouseLocator.getWarehouseLocatorId());
        stockLocatorDTO.setLocatorCode(baseWmsWarehouseLocator.getWarehouseLocatorCode());
        stockLocatorDTO.setLocatorName(baseWmsWarehouseLocator.getWarehouseLocatorName());
        if("AGVL".equals(detail.getWarehouseAreaCategory())){
            stockLocatorDTO.setContainerCode(null);
        }

        return CollUtil.toList(stockLocatorDTO);
    }

    private WmsReplenishD generateWmsReplenishD(WmsReplenishH wmsReplenishH, UserDTO user, ReplenishInitiativeSaveDetailVO detail, AtomicInteger i, ItemBaseInfoVO itemBaseInfoVO, List<WarehouseLocatorBasicInfo> locators, List<BatchNumberListVO> batchNumbers, StockReplenishTaskDTO task) {
        WmsReplenishD d = BeanUtil.copyProperties(detail, WmsReplenishD.class);
        d.setDetailId(IdUtil.getId(REPLENISH_LINE_D));
        d.setHeaderId(wmsReplenishH.getHeaderId());
        d.setPieceQuantity(task.getPieceQuantity());
        d.setWhLocatorId(task.getSrcWarehouseLocatorId());
        d.setToWhLocatorId(task.getDestWarehouseLocatorId());
        d.setBatchId(task.getBatchId());
        d.setContainerCode(task.getContainerCode());
        d.setToContainerCode(task.getToContainerCode());

        //商品信息
        d.setItemCode(itemBaseInfoVO.getItemCode());
        d.setItemName(itemBaseInfoVO.getItemName());
        d.setUom(itemBaseInfoVO.getCaseUom());
        d.setConversionRate(itemBaseInfoVO.getCaseConversionRate());
        //计算整件数  最小单位数/转换因子
        d.setQuantity(d.getPieceQuantity().divide(itemBaseInfoVO.getCaseConversionRate(), RoundingMode.UP));
        ModelUtils.setBaseInfo(d, ModelUtils.OpType.INSERT, user);

        locators.stream().filter(locator -> locator.getWarehouseLocatorId().equals(task.getSrcWarehouseLocatorId())).findFirst().ifPresent(t -> {
            d.setWhAreaCategory(t.getWarehouseAreaCategory());
            d.setWhAreaCode(t.getWarehouseAreaCode());
            d.setWhAreaId(t.getWarehouseAreaId());
            d.setWhLocatorCode(t.getWarehouseLocatorCode());
            d.setWhLocatorId(t.getWarehouseLocatorId());
            d.setWhWarehouseLocatorType(t.getWarehouseLocatorType());
            d.setFloorNum(t.getFloorNum());
            d.setItemPlaceType(t.getItemPlaceType());
            d.setBldgId(t.getBldgId());
            d.setBldgCode(t.getBldgCode());
            d.setBldgName(t.getBldgName());
            d.setSrcPickMode(t.getPickMode());
        });

        locators.stream().filter(locator -> locator.getWarehouseLocatorId().equals(task.getDestWarehouseLocatorId())).findFirst().ifPresent(t -> {
            d.setToWhAreaCategory(t.getWarehouseAreaCategory());
            d.setToWhAreaCode(t.getWarehouseAreaCode());
            d.setToWhAreaId(t.getWarehouseAreaId());
            d.setToWhLocatorCode(t.getWarehouseLocatorCode());
            d.setToWhLocatorId(t.getWarehouseLocatorId());
            d.setToWhWarehouseLocatorType(t.getWarehouseLocatorType());
            d.setToFloorNum(t.getFloorNum());
            d.setToItemPlaceType(t.getItemPlaceType());
            d.setToPickMode(t.getPickMode());
        });

        batchNumbers.stream().filter(batch -> batch.getBatchId().equals(task.getBatchId())).findFirst().ifPresent(t -> {
            d.setBatchNum(t.getBatchNum());
            d.setBatchExpiredDate(t.getBatchExpiredDate());
            d.setBatchProductionDate(t.getBatchProductionDate());
            d.setManufacturerLotNum(t.getManufacturerLotNum());
            d.setShelfLifeDays(t.getShelfLifeDays());
            d.setBatchExpiredDateShow(t.getBatchExpiredDateShow());
            d.setBatchProductionDateShow(t.getBatchProductionDateShow());
        });
        d.setLineNum(String.valueOf(i.getAndIncrement()));
        d.setStatus(ReplenishStatus.CREATE.getStatus());
        return d;
    }

    /**
     * 获取补货货位限定或补货规则
     */
    private List<StockReplenishTaskDTO> getListByReplenishRule(List<ReplenishRuleDTO> replenishRules, ReplenishInitiativeSaveDetailVO detail, ItemBaseInfoVO itemBaseInfoVO, List<StockLocatorDTO> stockLocators, List<StockLocatorDTO> limitLocators) {
        ReplenishDataDTO replenishDataDTO = new ReplenishDataDTO();
        replenishDataDTO.setLimitLocators(limitLocators);
        replenishDataDTO.setReplenishQty(detail.getPieceQuantity());
        replenishDataDTO.setAllLocatorList(stockLocators);
        replenishDataDTO.setReplenishRules(replenishRules);
        replenishDataDTO.setCaseConversionRate(itemBaseInfoVO.getCaseConversionRate());
        replenishDataDTO.setVolume(itemBaseInfoVO.getVolume());
        replenishDataDTO.setReplenishTaskList(new ArrayList<>());

        return ReplenishAlgorithm.compute(replenishDataDTO);
    }

    /**
     * 旧补货逻辑
     * @param confirmParamsVO
     */
    @Override
    @TxTransaction
    @Transactional(rollbackFor = Exception.class)
    public void confirmReplenishOld(MoveConfirmParamsVO confirmParamsVO) {
        logger.info("WmsReplenishH replenishH confirmParamsVO：" + JSONObject.toJSONString(confirmParamsVO));
        UserDTO user = userTemplate.getUserDTO();
        ReplenishStatus status;
        ReplenishStatus preStatus;
        if ("0".equals(confirmParamsVO.getMoveFlag().toString())) {
            status = ReplenishStatus.OUT;
            preStatus = ReplenishStatus.CREATE;
        } else if ("1".equals(confirmParamsVO.getMoveFlag().toString())) {
            status = ReplenishStatus.IN;
            preStatus = ReplenishStatus.OUT;
        } else {
            throw new BizException("未知的库存操作类型");
        }

        Criteria criteria = Criteria.forClass(WmsReplenishD.class);
        criteria.restrictionBuilder()
                .eq("enterpriseId", user.getEnterpriseId())
                .eq("status", preStatus.getStatus())
                .in("detailId", confirmParamsVO.getLineIds());
        criteria.buildRestriction();
        List<WmsReplenishD> dList = dMapper.findByCriteria(criteria);
        if (CollectionUtil.isEmpty(dList)) {
            throw new BizException("未找到有效的补货单");
        }
        WmsReplenishD replenishD = new WmsReplenishD();
        replenishD.setStatus(status.getStatus());
        dMapper.updateFieldsByCriteria(replenishD, FieldSelector.newInstance().include("status"), criteria);
        //查找头表warehouseId
        String[] headerIds = dList.stream().map(WmsReplenishD::getHeaderId).toArray(String[]::new);
        List<WmsReplenishH> hList = hMapper.findByIds(WmsReplenishH.class, headerIds, FieldSelector.newInstance().include(new String[]{"headerId", "warehouseId"}));

        logger.info("WmsReplenishH replenishH headerIds：" + JSONObject.toJSONString(headerIds));
        //需要上架的单据
        Set<String> inLineIds = new HashSet<>();
        //需要下架的单据
        Set<String> outLineIds = new HashSet<>();
        for (WmsReplenishH replenishH : hList) {
            List<WmsReplenishD> replenishDList = dList.stream().filter(x -> x.getHeaderId().equals(replenishH.getHeaderId())).collect(Collectors.toList());
            for (WmsReplenishD d : replenishDList) {
                RestMessage<List<ReplenishRuleDTO>> ruleList = strategyClient.getReplenishRuleList(replenishH.getWarehouseId(), d.getWhAreaId(), d.getToWhAreaId());
                if (!ruleList.isSuccess() || ruleList.getData() == null || ruleList.getData().isEmpty()) {
                    throw new BizException("未找到有效的补货规则");
                }
                //默认第一条补货规则
                ReplenishRuleDTO rule = ruleList.getData().get(0);
                if ((rule.getConfirmMode() == 1 && confirmParamsVO.getMoveFlag() == 0) ||
                        (rule.getConfirmMode() == 2 && confirmParamsVO.getMoveFlag() == 1)
                ) {
                    //一次确认 && 下架
                    //二次确认 && 上架
                    inLineIds.add(d.getDetailId());
                } else if (confirmParamsVO.getMoveFlag() == 0) {
                    //二次确认 && 下架
                    outLineIds.add(d.getDetailId());
                }
            }
        }
        logger.info("WmsReplenishH replenishH inLineIds：" + JSONObject.toJSONString(inLineIds));
        logger.info("WmsReplenishH replenishH outLineIds：" + JSONObject.toJSONString(outLineIds));
        //下架
        if (CollectionUtil.isNotEmpty(outLineIds)) {
            Criteria criteriaOut = Criteria.forClass(WmsReplenishD.class);
            criteriaOut.restrictionBuilder().eq("enterpriseId", user.getEnterpriseId()).in("detailId", outLineIds.toArray());
            criteriaOut.buildRestriction();
            List<WmsReplenishD> wmsReplenishDList = dMapper.findByCriteria(criteriaOut);
            String[] locatorIdList = wmsReplenishDList.stream().map(WmsReplenishD::getToWhLocatorId).toArray(String[]::new);
            RestMessage<List<WarehouseLocatorBasicInfo>> locationBasicInfos = warehouseResource.getLocationBasicInfos(locatorIdList);
            List<WmsReplenishD> deviceWmsReplenishDList = null;
            if (!locationBasicInfos.isSuccess() || locationBasicInfos.getData() == null) {
                throw new BizException("查询货位关联信息失败!请重试或联系管理员!");
            } else {
                Set<String> set = locationBasicInfos.getData().stream().filter(t -> WhOperataConstant.PICK_MODE_DEVICE.equals(t.getPickMode()))
                        .map(WarehouseLocatorBasicInfo::getWarehouseLocatorId).collect(Collectors.toSet());
                deviceWmsReplenishDList = wmsReplenishDList.stream().filter(t -> set.contains(t.getToWhLocatorId())).collect(Collectors.toList());
                logger.info("WmsReplenishH replenishH deviceWmsReplenishDList：" + JSONObject.toJSONString(deviceWmsReplenishDList));
                if (deviceWmsReplenishDList != null && deviceWmsReplenishDList.size() > 0) {
                    deviceLocatorHandler.transfor2Device(deviceWmsReplenishDList, confirmParamsVO);//调用接口平台,将拣货任务发送到AGV
                }
            }
            //更新状态
            if (deviceWmsReplenishDList == null || deviceWmsReplenishDList.size() == 0) {
                WmsReplenishD dOut = new WmsReplenishD();
                dOut.setStatus(ReplenishStatus.OUT.getStatus());
                dMapper.updateFieldsByCriteria(dOut, FieldSelector.newInstance().include("status"), criteriaOut);
            } else {
                String[] detailIds = deviceWmsReplenishDList.stream().map(WmsReplenishD::getDetailId).toArray(String[]::new);
                String[] wmsDetailIds = wmsReplenishDList.stream().filter(t -> !Arrays.asList(detailIds).contains(t.getDetailId())).map(WmsReplenishD::getDetailId).toArray(String[]::new);
                if (wmsDetailIds == null && wmsDetailIds.length > 0) {
                    criteriaOut.restrictionBuilder().eq("enterpriseId", user.getEnterpriseId()).in("detailId", wmsDetailIds);
                    WmsReplenishD dOut = new WmsReplenishD();
                    dOut.setStatus(ReplenishStatus.OUT.getStatus());
                    dMapper.updateFieldsByCriteria(dOut, FieldSelector.newInstance().include("status"), criteriaOut);
                }
                criteriaOut.restrictionBuilder().eq("enterpriseId", user.getEnterpriseId()).in("detailId", detailIds);
                WmsReplenishD dOut1 = new WmsReplenishD();
                dOut1.setStatus(ReplenishStatus.OUT.getStatus());
                dOut1.setType(1);//类型：0=wms 1=补货到设备货位
                dMapper.updateFieldsByCriteria(dOut1, FieldSelector.newInstance().include(new String[]{"status", "type"}), criteriaOut);
            }
            doMoveTaskCheck(new ArrayList<>(outLineIds), ReplenishStatus.OUT.getStatus());
        }

        //上架
        if (CollectionUtil.isNotEmpty(inLineIds)) {
            //补货上架 需扣减库存
            InvStockCheckMove invStockCheckMove = new InvStockCheckMove();
            invStockCheckMove.setLineBillIds(inLineIds.toArray(new String[]{}));
            invStockCheckMove.setMoveFlag(2);
            doStockCheck(invStockCheckMove);

            Criteria criteriaIn = Criteria.forClass(WmsReplenishD.class);
            criteriaIn.restrictionBuilder()
                    .eq("enterpriseId", user.getEnterpriseId())
                    .in("detailId", inLineIds.toArray());
            criteriaIn.buildRestriction();
            //更新状态
            WmsReplenishD dIn = new WmsReplenishD();
            dIn.setStatus(ReplenishStatus.IN.getStatus());
            dMapper.updateFieldsByCriteria(dIn, FieldSelector.newInstance().include("status"), criteriaIn);

            doMoveTaskCheck(new ArrayList<>(inLineIds), ReplenishStatus.IN.getStatus());
        }
    }


    @Override
    @TxTransaction(isStart = true)
    @Transactional(rollbackFor = Exception.class)
    public void confirmReplenish(MoveConfirmParamsVO vo) {

        logger.info("confirmReplenish.params:{}", JSONObject.toJSONString(vo));

        String[] dtlIds;
        if (CollUtil.isNotEmpty(vo.getReplenishDetailVOList())) {
            dtlIds = vo.getReplenishDetailVOList().stream().map(ReplenishDetailVO::getLineId).toArray(String[]::new);
        }else if (CollUtil.isNotEmpty(vo.getDtls())){
            dtlIds = vo.getDtls().stream().map(ReplenishDetailLocationVO::getLineId).toArray(String[]::new);
        }else {
            if (vo.getMoveFlag() == 0 || vo.getMoveFlag() == 2) {
                throw new BizException("请选择需要操作下架的数据");
            }else {
                throw new BizException("请选择需要操作上架的数据");
            }
        }
        List<WmsReplenishD> dList = dMapper.findByIds(WmsReplenishD.class, dtlIds, null);
        Assert.notEmpty(dList, "未找到有效的补货单明细");
        String[] hdrIds = dList.stream().map(WmsReplenishD::getHeaderId).distinct().toArray(String[]::new);
        List<WmsReplenishH> hList = hMapper.findByIds(WmsReplenishH.class, hdrIds, null);
        Assert.notEmpty(hList, "未找到有效的补货单汇总");
        ReplenishLineIdVO dto = getReplenishLineIdVO(hList, dList, vo.getMoveFlag());
        if (CollUtil.isNotEmpty(dto.getUnShelveList())) {
            handle4Unshelve(dto, vo);
        }
        if (CollUtil.isNotEmpty(dto.getPutawayList())) {
            handle4PutAway(dto, vo);
        }
    }

    @Override
    @TxTransaction(isStart = true)
    @Transactional(rollbackFor = Exception.class)
    public void autoConfirmReplenish(MoveConfirmParamsVO confirmParamsVO) {
        logger.info("WmsReplenishH replenishH confirmParamsVO：" + JSONObject.toJSONString(confirmParamsVO));

        UserDTO user = userTemplate.getUserDTO();
        if(user==null){
            user = new UserDTO();
            user.setId("eis");
            user.setUsername("eis");
            user.setNickname("eis");
        }

        Criteria criteria = Criteria.forClass(WmsReplenishD.class);
        String lineId = confirmParamsVO.getLineId();
        criteria.restrictionBuilder()
                .eq("enterpriseId", user.getEnterpriseId())
                .eq("status", ReplenishStatus.CREATE.getStatus())
                .in("detailId", lineId.split(","));
        criteria.buildRestriction();
        List<WmsReplenishD> dList = dMapper.findByCriteria(criteria);
        if (CollectionUtil.isEmpty(dList)) {
            throw new BizException("未找到有效的补货单明细");
        }
        //        List<WmsReplenishD> replenishDtls = getReplenishDltsByInParams(confirmParamsVO, dList);
        BigDecimal sumPieceQuantity = dList.stream().map(WmsReplenishD::getPieceQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
        if(confirmParamsVO.getMoveFlag() != null && 1 == confirmParamsVO.getMoveFlag()){
            confirmParamsVO.setInventoryQty(sumPieceQuantity.doubleValue());
        }
        Double inventoryQty = confirmParamsVO.getInventoryQty();
        if(inventoryQty == null || inventoryQty <= 0){
            throw new BizException("EIS回告数量不能为空或零！");
        }

        BigDecimal eisBackQty = new BigDecimal(inventoryQty);
        if(eisBackQty.compareTo(sumPieceQuantity) > 0){
            throw new BizException("补货确认数量不能大于补货明细数量!");
        }
        //确认补货明细行
        List<WmsReplenishD> confirmWmsReplenishDs = new ArrayList<>();
        for (WmsReplenishD model : dList) {
            if(eisBackQty.compareTo(BigDecimal.ZERO) <= 0){
                break;
            }
            WmsReplenishH wmsReplenishH = hMapper.findById(model.getHeaderId(), WmsReplenishH.class);
            if(wmsReplenishH == null){
                throw new BizException("根据补货汇总id："+model.getHeaderId()+"查询补货汇总失败!");
            }
            //如果EIS回告数量小于补货明细数量，并且是非被动补货
            if(eisBackQty.compareTo(model.getPieceQuantity()) < 0 && !ReplenishBusinessType.PASSIVE.name().equals(wmsReplenishH.getBusinessType())){
                //拆分补货明细并且重新生成一条补货明细
                WmsReplenishD replenishD = splitReplenishD(model, eisBackQty, confirmParamsVO.getContainerNo());
                //拆分补货预占
                List<Map<String, Object>> params = new ArrayList<>();
                Map<String, Object> map = new HashMap<>();
                map.put("lineBillId", model.getDetailId());
                map.put("qty", eisBackQty);
                map.put("newLineBillId", replenishD.getDetailId());
                map.put("containerCode", replenishD.getContainerCode());
                params.add(map);
                stockResource.splitByQty(params);
                //生成出库任务
                doMoveTask(wmsReplenishH, CollUtil.toList(replenishD));
                model = replenishD;
            }

            String toWarehouseAreaCode = confirmParamsVO.getToWarehouseAreaCode();
            if(!StringUtils.isBlank(toWarehouseAreaCode)){
                //查询货位
                WhLocatorQueryVO queryVO = new WhLocatorQueryVO();

                queryVO.setEnterpriseId(model.getEnterpriseId());
                queryVO.setWarehouseAreaCode(toWarehouseAreaCode);
                RestMessage<List<BaseWmsWarehouseLocator>> listRestMessage =
                        warehouseClient.locatorList(queryVO);
                if(listRestMessage == null || !listRestMessage.isSuccess() || listRestMessage.getData() == null || listRestMessage.getData().size() <= 0){
                    throw new BizException("查询货位失败!");
                }
                List<WmsReplenishD> modifyLocatorIdList = new ArrayList<>();

                String toWarehouseLocatorId = listRestMessage.getData().get(0).getWarehouseLocatorId();

                if(!toWarehouseLocatorId.equals(model.getToWhLocatorId())){
                    //更新移入货位
                    dMapper.updateMapById(model.getDetailId(), MapUtils.put("toWhLocatorId", toWarehouseLocatorId).getMap(), WmsReplenishD.class);
                    //更新预占信息
                    modifyLocatorIdList.add(model);
                }

                if (CollUtil.isNotEmpty(modifyLocatorIdList)){
                    //1、去修改库存预占表的货位
                    List<Map<String, Object>> params = new ArrayList<>();
                    for (WmsReplenishD replenishD : modifyLocatorIdList) {
                        Map<String, Object> map = new HashMap<>();
                        map.put("whLocatorId", replenishD.getToWhLocatorId());
                        map.put("lineBillId", replenishD.getDetailId());
                        map.put("billType", "IN");
                        params.add(map);
                    }
                    stockResource.updateLocator(params);
                }

            }

            //如果容器不能为空并且容器不等于补货明细容器号，则需要更新容器到补货明细，预占明细
            String containerNo = confirmParamsVO.getContainerNo();
            if(!StringUtils.isBlank(containerNo) && (StringUtils.isBlank(model.getContainerCode()) || !containerNo.equals(model.getContainerCode()))){
                //更新容器
                dMapper.updateMapById(model.getDetailId(), MapUtils.put("containerCode", containerNo).getMap(), WmsReplenishD.class);
                //更新预占
                List<Map<String, Object>> params = new ArrayList<>();
                Map<String, Object> map = new HashMap<>();
                map.put("lineBillId", model.getDetailId());
                map.put("containerCode", containerNo);
                params.add(map);
                stockResource.updateContainerCode(params);
            }

            if(ReplenishBusinessType.PASSIVE.name().equals(wmsReplenishH.getBusinessType())){
                eisBackQty = eisBackQty.subtract(model.getPieceQuantity());
            }
            confirmWmsReplenishDs.add(model);
        }
        if(confirmWmsReplenishDs.size() > 0){
            //更新状态
            FieldSelector fs = FieldSelector.newInstance().include("status");
            confirmWmsReplenishDs.forEach(t -> {
                t.setStatus(ReplenishStatus.IN.getStatus());
                dMapper.updateFields(t, fs);
            });
//        model.setStatus(ReplenishStatus.IN.getStatus());
//        dMapper.updateFields(model, fs);

            String[] lineIds = confirmWmsReplenishDs.stream().map(WmsReplenishD::getDetailId).distinct().toArray(String[]::new);
//        String[] lineIds = new String[]{model.getDetailId()};
            outBoundClient.updateTasksUnshelveByReplenish(MapUtils.put("ids", lineIds).put("status", MoveTaskStatus.PUTAWAY.getCode()).getMap());

            doStockCheck(lineIds);
        }
    }

    /**
     * 根据数量拆分补货明细
     * @param wmsReplenishD
     * @param qty
     */
    @Transactional
    public WmsReplenishD splitReplenishD(WmsReplenishD wmsReplenishD,BigDecimal qty,String containerNo){
        WmsReplenishD replenishD = new WmsReplenishD();
        BeanUtils.copyProperties(wmsReplenishD,replenishD);
        replenishD.setDetailId(IdUtil.getId(REPLENISH_LINE_D));
        replenishD.setPieceQuantity(qty);
        replenishD.setQuantity(replenishD.getPieceQuantity().divide(replenishD.getConversionRate()));
        replenishD.setContainerCode(containerNo);
        dMapper.save(replenishD);

        wmsReplenishD.setPieceQuantity(wmsReplenishD.getPieceQuantity().subtract(qty));
        wmsReplenishD.setQuantity(replenishD.getPieceQuantity().divide(replenishD.getConversionRate()));

        FieldSelector fs = FieldSelector.newInstance().include("quantity","pieceQuantity");
        dMapper.updateFields(wmsReplenishD, fs);

        //更新补货任务对应的出库任务
        MoveTaskDODTO moveTask = new MoveTaskDODTO();
        moveTask.setDetailId(wmsReplenishD.getDetailId());
        moveTask.setQty(wmsReplenishD.getPieceQuantity());
        outBoundClient.changeQty(CollUtil.toList(moveTask));

        return replenishD;
    }



    private List<WmsReplenishD> getReplenishDltsByInParams(MoveConfirmParamsVO confirmParamsVO, List<WmsReplenishD> dtls) {

        if (confirmParamsVO.getTaskId() == null) {
            return dtls;
        }

        WmsReplenishD model = dtls.get(0);

        List<WmsReplenishD> replenishDs = dMapper.findByMap(MapUtils.put("headerId", confirmParamsVO.getTaskId()).getMap(), WmsReplenishD.class);
        Assert.notEmpty(replenishDs, "根据补货ID【{}】获取补货明细失败", confirmParamsVO.getTaskId());

        List<WmsReplenishD> collect = replenishDs.stream().filter(t -> t.getItemId().equals(model.getItemId()) && t.getBatchId().equals(model.getBatchId()) && t.getToWhAreaCategory().equals(model.getToWhAreaCategory())).collect(Collectors.toList());
        Assert.notEmpty(collect, "根据补货ID【{}】获取补货明细失败", confirmParamsVO.getTaskId());
        return collect;
    }

    private String groupBy(MoveTaskDO t) {
        return StrUtil.join("###", t.getItemId(), t.getBatchId(), t.getDestWarehouseAreaNum());
    }

    /**
     * 封装业务参数
     */
    private ReplenishLineIdVO getReplenishLineIdVO(List<WmsReplenishH> hList, List<WmsReplenishD> dList, Integer moveFlag) {

        //需要上架的单据
        List<WmsReplenishD> putAwayList = Lists.newArrayList();
        //需要下架的单据
        List<WmsReplenishD> unShelveList = Lists.newArrayList();

        String warehouseId = hList.get(0).getWarehouseId();
        List<String> whAreaIds = dList.stream().map(WmsReplenishD::getWhAreaId).distinct().collect(Collectors.toList());
        List<String> toWhAreaIds = dList.stream().map(WmsReplenishD::getToWhAreaId).distinct().collect(Collectors.toList());
        List<ReplenishRuleDTO> rules = Collections.emptyList();

        boolean isIncidentally = hList.stream().anyMatch(h -> INCIDENTALLY.name().equals(h.getBusinessType()));
        if (isIncidentally) {
            for (WmsReplenishD d : dList) {
                putAwayList.add(d);
            }
        } else {

            rules = RestMsgHelper.unpack(strategyClient.getReplenishRuleListByArray(warehouseId, whAreaIds, toWhAreaIds));
            Assert.notEmpty(rules, "获取补货策略失败");

            for (WmsReplenishD d : dList) {

                ReplenishRuleDTO rule = rules.stream().filter(t -> t.getSrcWarehouseAreaId().equals(d.getWhAreaId()) && t.getDestWarehouseAreaId().equals(d.getToWhAreaId())).findFirst().orElse(null);
                Assert.notNull(rule, "未找到有效的补货规则");

                if (rule.getConfirmMode() == null) {
                    log.info("rule:{}", JSONObject.toJSONString(rule));
                    throw new BizException("补货规则的确认模式缺失，请检查");
                }

                if ((rule.getConfirmMode() == ReplenishConfirmMode.ONE.getCode().shortValue() && moveFlag.equals(UNSHELVE.getCode())) || (rule.getConfirmMode() == ReplenishConfirmMode.TWO.getCode().shortValue() && moveFlag.equals(ReplenishMoveFlag.PUTAWAY.getCode()))) {
                    //一次确认 && 下架
                    //二次确认 && 上架
                    putAwayList.add(d);
                } else if (moveFlag.equals(UNSHELVE.getCode())) {
                    //二次确认 && 下架
                    unShelveList.add(d);
                }
            }
        }

        ReplenishLineIdVO result = new ReplenishLineIdVO();
        result.setPutawayList(putAwayList);
        result.setUnShelveList(unShelveList);
        result.setRules(rules);
        result.setWarehouseId(warehouseId);
        return result;
    }

    /**
     * 上架确认
     */
    private void handle4PutAway(ReplenishLineIdVO dto, MoveConfirmParamsVO vo) {

        List<WmsReplenishD> putAwayList = dto.getPutawayList();

        //PC对应locationId,RF对应locationCode
//        boolean isSameLocator = isSameLocator(vo, putAwayList);
//        Assert.isTrue(isSameLocator, "指定上架货位与推荐货位不符，请检查");
        //支持修改上架货位

        Set<String> locatorIdSet = vo.getDtls().stream().map(ReplenishDetailLocationVO::getToLocationId).filter(StrUtil::isNotBlank).collect(Collectors.toSet());
        RestMessage<List<WarehouseLocatorBasicInfo>> locationBasicInfos = new RestMessage<>();
        if (CollUtil.isNotEmpty(locatorIdSet)) {
            String[] locatorIdList = locatorIdSet.toArray(new String[]{});
            locationBasicInfos = warehouseClient.getLocationBasicInfos(locatorIdList);
            if (!locationBasicInfos.isSuccess() || locationBasicInfos.getData() == null) {
                throw new BizException("查询货位关联信息失败!请重试或联系管理员!");
            }
        }
        //查询对应的移库单
        if (CollUtil.isNotEmpty(putAwayList)){
            List<WmsReplenishD> modifyLocatorIdList = new ArrayList<>();
            //循环比对移入货位是否修改
            for (ReplenishDetailLocationVO repD : vo.getDtls()) {
                if (StrUtil.isNotBlank(repD.getToLocationId())) {
                    for (WmsReplenishD d : putAwayList) {
                        WarehouseLocatorBasicInfo locatorBasicInfo = locationBasicInfos.getData().stream().filter(var -> repD.getToLocationId().equals(var.getWarehouseLocatorId())).findFirst().orElse(null);
                        //校验货位信息
                        if (locatorBasicInfo == null) {
                            throw new BizException("货位【" + repD.getToLocationId() + "】不存在！请检查");
                        }
                        if (locatorBasicInfo.getWarehouseAreaCategory().equals("AGVL")) {
                            throw new BizException("AGV货架库不能手动补货上架");
                        }
                        if (CollUtil.isNotEmpty(locatorIdSet) && repD.getLineId().equals(d.getDetailId()) && !repD.getToLocationId().equals(d.getToWhLocatorId())) {
                            //修改了上架货位，需要更新货位以及对应的预占信息

                            //当前货物是否满足货位分配策略
                            //当前移入货位不符合上架策略及货位限定约束的可上架货位
                            RestMessage<BaseMtlItemMst> itemMsg = itemResource.item(d.getItemId());
                            if (!itemMsg.isSuccess() || itemMsg.getData() == null) {
                                throw new BizException("商品不存在");
                            }
                            RestMessage<Page<WarehouseLocatorBasicInfo>> limitMsg = controlClient.getInboundLocatorLimit(d.getItemId(), itemMsg.getData().getCargoOwnerId(), locatorBasicInfo.getWarehouseId(), locatorBasicInfo.getWarehouseAreaCategory(), 1, 999);
                            Assert.isTrue(limitMsg.isSuccess() && null != limitMsg.getData() && CollUtil.isNotEmpty(limitMsg.getData().getList()), "当前移入货位【" + locatorBasicInfo.getWarehouseLocatorCode() + "】不符合上架策略及货位限定约束的可上架货位");
                            WarehouseLocatorBasicInfo warehouseLocatorBasicInfo = limitMsg.getData().getList().stream().filter(var -> locatorBasicInfo.getWarehouseLocatorId().equals(var.getWarehouseLocatorId())).findFirst().orElse(null);
                            Assert.isTrue(null != warehouseLocatorBasicInfo, "当前移入货位【" + locatorBasicInfo.getWarehouseLocatorCode() + "】不符合上架策略及货位限定约束的可上架货位");
                            //更新移入货位
                            dMapper.updateMapById(d.getDetailId(), MapUtils.put("toWhLocatorId", repD.getToLocationId()).getMap(), WmsReplenishD.class);
                            //更新预占信息
                            modifyLocatorIdList.add(d);
                        }
                    }
                }
            }

            if (CollUtil.isNotEmpty(modifyLocatorIdList)){
                //1、去修改库存预占表的货位
                List<Map<String, Object>> params = new ArrayList<>();
                for (WmsReplenishD replenishD : modifyLocatorIdList) {
                    Map<String, Object> map = new HashMap<>();
                    map.put("whLocatorId", replenishD.getToWhLocatorId());
                    map.put("lineBillId", replenishD.getDetailId());
                    map.put("billType", "IN");
                    params.add(map);
                }
                stockResource.updateLocator(params);
            }
        }
        String[] inLineIds = putAwayList.stream().map(WmsReplenishD::getDetailId).distinct().toArray(String[]::new);

        UserDTO user = userTemplate.getUserDTO();
        if(user==null){
            user = new UserDTO();
            user.setId("eis");
            user.setUsername("eis");
            user.setNickname("eis");
        }
        DateTime now = DateUtil.date();
        UserDTO finalUser = user;
        putAwayList.forEach(p -> {
            p.setStatus(ReplenishStatus.IN.getStatus());
            p.setModifier(finalUser.getId());
            p.setModifierName(finalUser.getNickname());
            p.setGmtModified(now);
            dMapper.update(p);
        });

        //补货上架 需扣减库存
        doStockCheck(inLineIds);

        doMoveTaskCheck(Arrays.asList(inLineIds), MoveTaskStatus.PUTAWAY.getCode());

//        finishTaskWhenExistsDevice(dto.getWarehouseId(), putAwayList, vo.getMoveFlag());

        //更新outbound movetask的状态
//        RestMessage<Object> restMessage = outBoundClient.updateTasksUnshelveByReplenish(MapUtils.put("ids", Arrays.asList(inLineIds)).put("status", MoveTaskStatus.PUTAWAY.getCode()).getMap());
//        if (!restMessage.isSuccess()){
//            throw new RuntimeException("通知出库上架失败！");
//        }
    }

    /**
     * 校验指定货位是否与推荐货位一致
     */
    private boolean isSameLocator(MoveConfirmParamsVO vo, List<WmsReplenishD> putAwayList) {
        boolean isPc = vo.getDtls().stream().anyMatch(d -> StrUtil.isNotBlank(d.getToLocationId()));
        boolean isSameLocator;
        if (isPc) {
            isSameLocator = putAwayList.stream().allMatch(p -> vo.getDtls().stream().map(ReplenishDetailLocationVO::getToLocationId).collect(Collectors.toList()).contains(p.getToWhLocatorId()));
        } else {
            isSameLocator = putAwayList.stream().allMatch(p -> vo.getDtls().stream().map(ReplenishDetailLocationVO::getToLocationCode).collect(Collectors.toList()).contains(p.getToWhLocatorCode()));
        }
        return isSameLocator;
    }


    /**
     * 封装查询可用库存信息
     */
    private WmsInvStockQtyQueryVO packQueryVO(ReplenishLineIdVO dto, WmsReplenishD replenishD) {
        WmsInvStockQtyQueryVO queryVO = new WmsInvStockQtyQueryVO();
        queryVO.setEnterpriseId(replenishD.getEnterpriseId());
        queryVO.setWarehouseId(dto.getWarehouseId());
        queryVO.setCargoOwnerId(replenishD.getCargoOwnerId());
        queryVO.setBatchId(replenishD.getBatchId());
        queryVO.setStatus(StockStatusEnum.QUALIFIED.getCode());
        queryVO.setItemId(replenishD.getItemId());
        return queryVO;
    }

    /**
     * 下架确认
     */
    private void handle4Unshelve(ReplenishLineIdVO dto, MoveConfirmParamsVO vo) {

        List<WmsReplenishD> list = dto.getUnShelveList();

        UserDTO user = userTemplate.getUserDTO();
        if(user==null){
            user = new UserDTO();
            user.setId("eis");
            user.setUsername("eis");
            user.setNickname("eis");
        }
        DateTime now = DateUtil.date();
        UserDTO finalUser = user;
        list.forEach(d -> {
            d.setStatus(ReplenishStatus.OUT.getStatus());
            d.setModifier(finalUser.getId());
            d.setModifierName(finalUser.getNickname());
            d.setGmtModified(now);
            dMapper.update(d);
        });

        String[] ids = list.stream().map(WmsReplenishD::getDetailId).distinct().toArray(String[]::new);

        outBoundClient.updateTasksUnshelveByReplenish(MapUtils.put("ids", Arrays.asList(ids)).put("status", MoveTaskStatus.UNSHELVE.getCode()).getMap());

        replenishStockCheck(ids, vo.getMoveFlag());

        Set<String> itemIds = list.stream().map(WmsReplenishD::getItemId).filter(StrUtil::isNotEmpty).collect(Collectors.toSet());
        RestMessage<List<BaseMtlItemMst>> itemMsg = itemResource.getListByIds(itemIds);
        if (!itemMsg.isSuccess() || CollUtil.isEmpty(itemMsg.getData())){
            throw new BizException("查询商品信息失败！");
        }
        Map<String, List<WmsReplenishD>> collect = list.stream().collect(Collectors.groupingBy(WmsReplenishD::getToWhAreaCategory));
        List<WmsReplenishD> dList = collect.get("AGVL");
        if (CollUtil.isEmpty(dList)) {
            dList = new ArrayList<>();
        }
        for (WmsReplenishD wmsReplenishD : dList) {
            AsnToEis asnToEis = new AsnToEis();
            List<AsnToEis.AsnDetail> subList = new ArrayList<>();
            asnToEis.setMsgId(cn.hutool.core.util.IdUtil.fastSimpleUUID());
            asnToEis.setTaskId(wmsReplenishD.getHeaderId());
            asnToEis.setContainerType("3");
            asnToEis.setOriginalArea("AGVHJ01-R01");
            asnToEis.setWarehouseId("JXYJ");
            asnToEis.setWmslocationCode("AGVHJ01");
            asnToEis.setWorkType(10);
            asnToEis.setBusinessType(50);
            RestMessage<List<MoveTaskDO>> replenishTasksByIds = outBoundClient.getReplenishListByPickTaskId(wmsReplenishD.getDetailId(), "");
            if (!replenishTasksByIds.isSuccess() || CollUtil.isEmpty(replenishTasksByIds.getData())){
                throw new BizException("推送EIS获取标签失败！" + replenishTasksByIds.getMessage());
            }
            if (StrUtil.isEmpty(replenishTasksByIds.getData().get(0).getBarcode())){
                throw new BizException("推送EIS获取标签失败！OUTBOUND未返回标签号");
            }
            asnToEis.setContainerNo(replenishTasksByIds.getData().get(0).getBarcode());
            AsnToEis.AsnDetail detail = new AsnToEis.AsnDetail();
            detail.setLotId(wmsReplenishD.getBatchId());
            detail.setOwnerId(wmsReplenishD.getCargoOwnerId());
            String packId = "";
            BaseMtlItemMst itemMst = itemMsg.getData().stream().filter(var->var.getItemId().equals(wmsReplenishD.getItemId())).findFirst().orElse(null);
            if (itemMst == null) {
                throw new RuntimeException("未找到商品信息！");
            }
            if (CollUtil.isNotEmpty(itemMst.getBaseMtlItemUomConversionsList())) {
                packId = itemMst.getBaseMtlItemUomConversionsList().get(0).getItemUomId();
            }
            detail.setPackId(packId);
            detail.setSeqNo(wmsReplenishD.getDetailId());
            detail.setQty(wmsReplenishD.getPieceQuantity());
            detail.setItemId(wmsReplenishD.getItemId());
            subList.add(detail);
            asnToEis.setSubList(subList);
            EisLog log = new EisLog();
            log.setRequest(JSONObject.toJSONString(asnToEis));
            log.setGmtCreate(DateUtil.date());
            log.setGmtModified(DateUtil.date());
            log.setInitiator(INITIATOR_WMS);
            log.setStatus(INIT);
            log.setUrl("inboundTaskUrl");
            try {
                RestMessage<Object> restMessage = stockResource.askIntegration(new AskIntegrationVo("inboundTaskUrl", asnToEis));
                log.setResponse(restMessage.getMessage());
                log.setStatus(SUCCESS);
            }catch (Exception e){
                log.setResponse("调用EIS错误！" + e.getMessage());
                log.setStatus(FAIL);
            }
            eisLogMapper.save(log);
        }
        //多余，由拣货任务确认后直接通知EIS
//        generateTaskFinishedConfirmSubListVO(dto.getWarehouseId(), list, vo.getMoveFlag());
    }

    /**
     * 存在立库就封装EIS参数并发送
     */
    private void finishTaskWhenExistsDevice(String warehouseId, List<WmsReplenishD> list, Integer moveFlag) {

        if (CollUtil.isEmpty(list)) {
            return;
        }

        Set<String> set = getLocatorCodesExcludeDeviceByDevice(list, moveFlag);

        if (CollUtil.isEmpty(set)) {
            return;
        }

        String enterpriseId = list.get(0).getEnterpriseId();

        for (String locationCode : set) {

            WmsInvStockLot queryVO = new WmsInvStockLot();
            queryVO.setContainerCode(locationCode);
            queryVO.setWarehouseId(warehouseId);
            List<WmsInvStockLot> stockLots = RestMsgHelper.unpack(stockResource.queryStockList(queryVO));

            if (CollUtil.isEmpty(stockLots) || stockLots.stream().allMatch(s -> s.getQuantity().compareTo(BigDecimal.ZERO) == 0)) {
                ContainerQueryVO containerVO = new ContainerQueryVO();
                containerVO.setContainerCode(locationCode);
                containerVO.setEnterpriseId(enterpriseId);
                containerVO.setWarehouseId(warehouseId);

                RestMsgHelper.unpack(warehouseClient.release(containerVO));
            }

            EisTaskFinishedConfirmVO vo = new EisTaskFinishedConfirmVO();
            vo.setContainerNo(locationCode);
            List<WmsReplenishD> thisDs = list.stream().filter(d -> d.getToWhLocatorCode().equals(locationCode) || d.getWhLocatorCode().equals(locationCode)).collect(Collectors.toList());

            List<TaskFinishedConfirmSubListVO> subList = thisDs.stream().map(d -> generateTaskFinishedConfirmSubListVO(d, warehouseId)).collect(Collectors.toList());

            vo.setSubList(subList);
//            mqProducer.finishTask(vo);
        }
    }

    /**
     * 判断源和目标是否存在立体库,并排除立库货位
     */
    private Set<String> getLocatorCodesExcludeDeviceByDevice(List<WmsReplenishD> list, Integer moveFlag) {

        String[] locatorIdList;
        //移库类型 0：下架、1：WMS上架
        if (moveFlag == 0) {
            locatorIdList = list.stream().map(WmsReplenishD::getWhLocatorId).distinct().toArray(String[]::new);
        } else if (moveFlag == 1) {
            locatorIdList = list.stream().map(WmsReplenishD::getToWhLocatorId).distinct().toArray(String[]::new);
        } else {
            throw new BizException("请求参数错误只允许0和1");
        }

        List<WarehouseLocatorBasicInfo> locators = RestMsgHelper.unpack(warehouseResource.getLocationBasicInfos(locatorIdList));

        boolean isDevice = locators.stream().anyMatch(t -> WhOperataConstant.PICK_MODE_DEVICE.equals(t.getPickMode()));
        if (!isDevice) {
            return null;
        }
        return locators.stream().filter(t -> !WhOperataConstant.PICK_MODE_DEVICE.equals(t.getPickMode())).map(WarehouseLocatorBasicInfo::getWarehouseLocatorCode).collect(Collectors.toSet());
    }

    private TaskFinishedConfirmSubListVO generateTaskFinishedConfirmSubListVO(WmsReplenishD d, String warehouseId) {
        TaskFinishedConfirmSubListVO vo = new TaskFinishedConfirmSubListVO();
        vo.setWarehouseId(warehouseId);
        vo.setOwnerId(d.getCargoOwnerId());
        vo.setBillNo(d.getHeaderId());
        vo.setSeqNo(d.getDetailId());
        vo.setWorkType(EisWorkType.WT_REPLENISH);
        vo.setBusinessType(EisBusinessType.BT_REPLENISH);
        vo.setItemCode(d.getItemCode());
        vo.setItemName(d.getItemName());
        vo.setQty(d.getQuantity());
        vo.setLocCode(d.getWhLocatorCode());
        vo.setLotNo(d.getManufacturerLotNum());
        vo.setYieldDate(d.getBatchProductionDate());
        vo.setExpDate(d.getBatchExpiredDate());
        //vo.setOutboundPlatform("");
        vo.setWmslocationCode(EIS_LOCATION);
        vo.setMsgId(d.getDetailId());

        return vo;
    }

    private ReplenishStatusVO getReplenishStatusVO(Integer moveFlag) {
        Assert.notNull(moveFlag, "库存操作类型不能为空");
        ReplenishStatus status;
        ReplenishStatus preStatus;
        if (UNSHELVE.getCode().equals(moveFlag)) {
            status = ReplenishStatus.OUT;
            preStatus = ReplenishStatus.CREATE;
        } else if (ReplenishMoveFlag.PUTAWAY.getCode().equals(moveFlag)) {
            status = ReplenishStatus.IN;
            preStatus = ReplenishStatus.OUT;
        } else {
            throw new BizException("未知的库存操作类型");
        }
        ReplenishStatusVO result = new ReplenishStatusVO();
        result.setStatus(status);
        result.setPreStatus(preStatus);
        return result;
    }

    @Override
    @TxTransaction
    @Transactional(rollbackFor = Exception.class)
    public void confirmReplenishDevice() {
        IntegrationSearchData sd = new IntegrationSearchData();
        sd.setInterfaceId(instockCallId);
        sd.setData(MapUtils.put("type", 5).getMap());//设备库补货上架，目前约定传5
        logger.info("integrationResource.searchForRestMessageMap入参为：" + JSONObject.toJSONString(sd));
        try {
            List<Map> mapList = integrationApi.searchForRestMessageMap(sd);
            logger.info("integrationResource.searchForRestMessageMap返回为：" + JSONObject.toJSONString(mapList));
            if (mapList == null || mapList.size() == 0) {
                return;
            }
            Object taskId = mapList.get(0).get("TASK_ID");
            if (mapList.size() == 1 && (taskId == null || StringUtils.isBlank(taskId.toString()))) {
                return;
            }
            String[] taskIds = mapList.stream().map(m -> m.get("TASK_ID")).distinct().toArray(String[]::new);
            Criteria criteria = Criteria.forClass(WmsReplenishD.class);
            criteria.restrictionBuilder().eq("status", ReplenishStatus.OUT.getStatus()).in("detailId", taskIds);
            criteria.buildRestriction();
            List<WmsReplenishD> dList = dMapper.findByCriteria(criteria);
            logger.info("WmsReplenishH replenishH dList：" + JSONObject.toJSONString(dList));
            if (CollectionUtil.isEmpty(dList)) {
                return;
            }
            //更新状态
            WmsReplenishD replenishD = new WmsReplenishD();
            replenishD.setStatus(ReplenishStatus.IN.getStatus());
            dMapper.updateFieldsByCriteria(replenishD, FieldSelector.newInstance().include("status"), criteria);
            //补货上架 需扣减库存
            doStockCheck(taskIds);

            doMoveTaskCheck(Arrays.asList(taskIds), ReplenishStatus.IN.getStatus());
            logger.info("WmsReplenishH replenishH doMoveTaskCheck taskIds：" + JSONObject.toJSONString(taskIds));
            for (String string : taskIds) {
                List<Map<String, Object>> paraListMap = new ArrayList<>();
                Map<String, Object> map2 = MapUtils.put("task_id", string).put("status", 1).getMap();
                paraListMap.add(map2);
                Map<String, Object> params = MapUtils.put("param", paraListMap).getMap();
                deviceLocatorHandler.askIntegration(instockCallStatusId, params);
            }
        } catch (Exception e) {
            log.error("调用设备接口异常" + new Random().nextInt(), e);
            throw new BizException("调用设备接口异常");
        }
    }

    public void doStockCheck(InvStockCheckMove move) {
        RestMessage outResult = stockResource.replenishStockCheck(move);
        if (!outResult.isSuccess()) {
            throw new BizException(outResult.getMessage());
        }
    }

    public void doStockCheck(String[] taskIds) {
        InvStockCheckMove vo = new InvStockCheckMove();
        vo.setLineBillIds(taskIds);
        vo.setMoveFlag(2);

        RestMessage msg = stockResource.replenishStockCheck(vo);
        Assert.isTrue(msg.isSuccess(), msg.getMessage());
    }

    public void doMoveTaskCheck(List<String> move, String status) {
        MoveTaskStatusSyncDTO vo = new MoveTaskStatusSyncDTO();
        vo.setDetails(move);
        vo.setStatus(status);
        RestMessage<Object> msg = outBoundClient.updateTasksUnshelveByReplenish(MapUtils.put("ids", move).put("status", status).getMap());
        Assert.isTrue(msg.isSuccess(), msg.getMessage());
    }

    public void replenishStockCheck(String[] lineBillIds, Integer moveFlag) {
        InvStockCheckMove move = new InvStockCheckMove();
        move.setMoveFlag(moveFlag);
        move.setLineBillIds(lineBillIds);

        RestMessage msg = stockResource.replenishStockCheck(move);
        Assert.isTrue(msg.isSuccess(), msg.getMessage());
    }

    public void doPreStock(String warehouseId, List<WmsReplenishD> dList) {
        //构造移库请求参数
        List<InvStockMove> stockMoves = dList.stream().map(x -> {
            InvStockMove stockMove = new InvStockMove();
            stockMove.setBillId(x.getHeaderId());
            stockMove.setWarehouseId(warehouseId);
            stockMove.setLineBillId(x.getDetailId());

            //移入
            InvStockMoveDetail moveDetail = new InvStockMoveDetail();
            moveDetail.setBatchId(x.getBatchId());
            moveDetail.setInLocatorId(x.getToWhLocatorId());
            moveDetail.setQuantity(x.getPieceQuantity());
            moveDetail.setInStockStatus(StockStatusEnum.QUALIFIED.getCode());
            moveDetail.setInContainerCode(x.getToContainerCode());

            //移出
            moveDetail.setOutLocatorId(x.getWhLocatorId());
            moveDetail.setQuantity(x.getPieceQuantity());
            moveDetail.setOutStockStatus(StockStatusEnum.QUALIFIED.getCode());
            moveDetail.setOutContainerCode(x.getContainerCode());
            stockMove.setDetails(Collections.singletonList(moveDetail));
            return stockMove;
        }).collect(Collectors.toList());

        log.info("请求库存服务补货预占--->数量:{}", stockMoves.size());
        log.info("请求库存服务补货预占--->明细:{}", JSONObject.toJSONString(stockMoves));
        RestMessage<Object> restMessage = stockResource.replenishStock(stockMoves);
        Assert.isTrue(restMessage.isSuccess(), "补货预占失败");
    }

    public void doMoveTask(WmsReplenishH h, List<WmsReplenishD> dList) {
        List<MoveTaskDODTO> moveTaskList = dList.stream().map(x -> {
            MoveTaskDODTO moveTask = new MoveTaskDODTO();
            moveTask.setWarehouseId(h.getWarehouseId());
            //状态 0:新生成 1:下架 2:上架
            moveTask.setStatus(x.getStatus());
            moveTask.setCargoOwnerId(x.getCargoOwnerId());
            moveTask.setItemId(x.getItemId());
            moveTask.setItemCode(x.getItemCode());
            moveTask.setItemName(x.getItemName());
            moveTask.setUom(x.getUom());
            moveTask.setBatchId(x.getBatchId());
            moveTask.setBatchNum(x.getBatchNum());
            moveTask.setManufacturerLotNum(x.getManufacturerLotNum());
            moveTask.setQty(x.getPieceQuantity());
            moveTask.setUom(x.getUom());
            moveTask.setSrcWarehouseAreaCategory(x.getWhAreaCategory());
            moveTask.setSrcWarehouseAreaId(x.getWhAreaId());
            moveTask.setSrcWarehouseAreaNum(x.getWhAreaCode());
            moveTask.setSrcWarehouseLocatorId(x.getWhLocatorId());
            moveTask.setSrcWarehouseLocatorNum(x.getWhLocatorCode());
            moveTask.setSrcWarehouseLocatorType(x.getWhWarehouseLocatorType());
            moveTask.setSrcFloor(x.getFloorNum());
            moveTask.setSrcItemPlaceType(x.getItemPlaceType());
            moveTask.setDestWarehouseAreaCategory(x.getToWhAreaCategory());
            moveTask.setDestWarehouseAreaId(x.getToWhAreaId());
            moveTask.setDestWarehouseAreaNum(x.getToWhAreaCode());
            moveTask.setDestWarehouseLocatorId(x.getToWhLocatorId());
            moveTask.setDestWarehouseLocatorNum(x.getToWhLocatorCode());
            moveTask.setDestWarehouseLocatorType(x.getToWhWarehouseLocatorType());
            moveTask.setDestFloor(x.getToFloorNum());
            moveTask.setDestItemPlaceType(x.getToItemPlaceType());
            moveTask.setBldgId(x.getBldgId());
            moveTask.setBldgCode(x.getBldgCode());
            moveTask.setBldgName(x.getBldgName());
            moveTask.setBatchExpiredDateShow(x.getBatchExpiredDateShow());
            moveTask.setBatchProductionDateShow(x.getBatchProductionDateShow());
            moveTask.setBatchNum(x.getBatchNum());
            moveTask.setExpiredDate(x.getBatchExpiredDate());
            moveTask.setProductionDate(x.getBatchProductionDate());

            //补货类型 1主动补货 2紧急补货 3波次补货 4被动补货 5顺带补货
            String moveType;
            ReplenishBusinessType businessType = ReplenishBusinessType.valueOf(h.getBusinessType());
            switch (businessType) {
                case INITIATIVE:
                    moveType = "1";
                    break;
                case URGENT:
                    moveType = "2";
                    break;
                case WAVE:
                    moveType = "3";
                    break;
                case PASSIVE:
                    moveType = "4";
                    break;
                case INCIDENTALLY:
                    moveType = "5";
                    break;
                default:
                    moveType = "1";
                    break;
            }
            moveTask.setMoveType(moveType);
            moveTask.setConversionRate(x.getConversionRate());
            moveTask.setHeaderId(x.getHeaderId());
            moveTask.setDetailId(x.getDetailId());

            moveTask.setLength(x.getLength());
            moveTask.setWidth(x.getWidth());
            moveTask.setHeight(x.getHeight());
            moveTask.setVolume(x.getVolume());
            moveTask.setWeight(x.getWeight());
            moveTask.setIsCaseOrPiece(x.getIsCaseOrPiece());
            moveTask.setContainerCode(x.getContainerCode());

            return moveTask;
        }).collect(Collectors.toList());

        log.info("生成补货任务完成--->明细:{}", JSONObject.toJSONString(moveTaskList));
        RestMessage restMessage = outBoundClient.addMoveTask(moveTaskList);
        Assert.isTrue(restMessage.isSuccess(), restMessage.getMessage());
    }

    @Override
    public Page<ReplenishListVO> getReplenishList(ReplenishQueryVO queryVO) {
        UserDTO user = userTemplate.getUserDTO();

        if (StrUtil.isNotBlank(queryVO.getBusinessType())) {
            if (EnumUtil.notContains(ReplenishBusinessType.class, queryVO.getBusinessType())) {
                throw new BizException("无效的补货类型");
            }
        }

        ReplenishStatus replenishStatus;
        if (StrUtil.isNotBlank(queryVO.getStatus())) {
            replenishStatus = ReplenishStatus.get(queryVO.getStatus());
            if (replenishStatus == null) {
                throw new BizException("无效的补货状态");
            }
        }

        RestMessage<List<BaseWmsWarehouseMst>> whResult = warehouseClient.getWarehouseBasicInfo(new String[]{queryVO.getWarehouseId()});
        if (!whResult.isSuccess() || whResult.getData() == null || whResult.getData().isEmpty()) {
            throw new BizException("未找到有效的仓库信息");
        }
        BaseWmsWarehouseMst warehouseMst = whResult.getData().get(0);
        List<MoveTaskDO> moveTaskDOS;
        if (StrUtil.isNotBlank(queryVO.getDetailId())) {
            if (null != queryVO.getBusinessType() && !queryVO.getBusinessType().equals(INCIDENTALLY.name())) {
                //非顺带补货类型
                moveTaskDOS = RestMsgHelper.unpack(outBoundClient.getReplenishListByPickTaskId(queryVO.getDetailId(), MoveTaskStatus.PUTAWAY.getCode()));
                Assert.notEmpty(moveTaskDOS, "该标签没有待确认的任务");
                List<String> dtlIds = moveTaskDOS.stream().map(MoveTaskDO::getDetailId).distinct().collect(Collectors.toList());
                queryVO.setDetailIds(dtlIds);
                queryVO.setDetailId(null);
            } else {
                moveTaskDOS = new ArrayList<>();
                queryVO.setDetailIds(StrUtil.isBlank(queryVO.getDetailId())?null:Arrays.asList(queryVO.getDetailId().split(",")));
            }
        }else {
            moveTaskDOS = new ArrayList<>();
        }

        Page<ReplenishListVO> replenishList = PageUtils.getPage(dMapper.selectReplenishList(queryVO, user.getEnterpriseId()));
        if (CollectionUtil.isEmpty(replenishList.getList())) {
            return null;
        }
        Set<String> cargoOwnerIds = replenishList.getList().stream().map(ReplenishListVO::getCargoOwnerId).collect(Collectors.toSet());
        RestMessage<Map<String, BaseCargoOwnerMst>> cargoResult = chainResource.getCargoOwerByIds(cargoOwnerIds);

        String[] itemIds = replenishList.getList().stream().map(ReplenishListVO::getItemId).distinct().toArray(String[]::new);
        RestMessage<List<ItemBaseInfoVO>> itemResult = itemResource.getItemBaseList(itemIds);

        String[] batchIds = replenishList.getList().stream().map(ReplenishListVO::getBatchId).distinct().toArray(String[]::new);
        List<BatchNumberListVO> batchNumbers = RestMsgHelper.unpack(stockResource.getBatchNumberBasicInfo(batchIds));

        replenishList.getList().forEach(x -> {
            x.setWarehouseName(warehouseMst.getWarehouseName());
            for (MoveTaskDO moveTaskDO : moveTaskDOS) {
                if (x.getDetailId().equals(moveTaskDO.getDetailId())){
                    x.setBarcode(moveTaskDO.getBarcode());
                }
            }
            if (cargoResult.isSuccess() && cargoResult.getData() != null) {
                BaseCargoOwnerMst cargoOwnerMst = cargoResult.getData().get(x.getCargoOwnerId());
                if (cargoOwnerMst != null) {
                    x.setCargoOwnerName(cargoOwnerMst.getCargoOwnerName());
                }
            }

            if (itemResult.isSuccess() && itemResult.getData() != null) {
                ItemBaseInfoVO itemInfo = itemResult.getData().stream().filter(item -> x.getItemId().equals(item.getItemId())).findFirst().orElse(null);
                if (itemInfo != null) {
                    x.setItemCode(itemInfo.getItemCode());
                    x.setItemName(itemInfo.getItemName());
                    x.setUom(itemInfo.getCaseUom());
                    x.setManufacturerId(itemInfo.getManufacturerId());
                    x.setManufacturerName(itemInfo.getManufacturerName());
                    x.setCountryOriginId(itemInfo.getCountryOriginId());
                    x.setItemHealthCategory(itemInfo.getItemHealthCategory());
                    x.setCaseConversionRate(itemInfo.getCaseConversionRate());
                    x.setSpecification(itemInfo.getSpecification());
                }
            }
            if (CollUtil.isNotEmpty(batchNumbers)) {
                batchNumbers.stream().filter(b -> b.getBatchId().equals(x.getBatchId())).findFirst().ifPresent(t -> x.setProductionPlace(t.getProductionPlace()));
            }
            BigDecimal[] qtyInfo = StockHelper.countStock(x.getPieceQuantity(), x.getCaseConversionRate());
            x.setCaseQuantity(qtyInfo[0].intValue());
        });

        return replenishList;
    }

    @Override
    public ReplenishPdaDetailVO getReplenishDetail(String detailId) {

        WmsReplenishD replenishD = dMapper.findById(detailId, WmsReplenishD.class);
        if (replenishD == null) {
            throw new BizException("补货单不存在或已失效");
        }

        ReplenishPdaDetailVO result = replenishConvert.wmsReplenishD2ReplenishPdaDetailVO(replenishD);

        List<ItemBaseInfoVO> items = RestMsgHelper.unpack(itemResource.getItemBaseList(new String[]{replenishD.getItemId()}));
        if (CollUtil.isNotEmpty(items)) {
            replenishConvert.completeReplenishPdaDetailVOByItem(result, items.get(0));
        }

        List<BatchNumberListVO> batchNumbers = RestMsgHelper.unpack(stockResource.getBatchNumberBasicInfo(new String[]{replenishD.getBatchId()}));
        if (CollUtil.isNotEmpty(batchNumbers)) {
            replenishConvert.completeReplenishPdaDetailVOByBatchNumber(result, batchNumbers.get(0));
        }

        Map<String, Object> map = RestMsgHelper.unpack(chainResource.getCargoOwerById(replenishD.getCargoOwnerId()));
        Optional.ofNullable(map.get("cargoOwnerName")).ifPresent(m -> result.setCargoOwnerName(m.toString()));

        BigDecimal[] qtyInfo = StockHelper.countStock(replenishD.getQuantity(), result.getCaseConversionRate());
        result.setCaseQuantity(qtyInfo[0]);

        return result;
    }

    @Override
    @TxTransaction
    @Transactional
    public void cancelReplenish(String[] detailIds) {
        Criteria criteria = Criteria.forClass(WmsReplenishD.class);
        criteria.restrictionBuilder().in("detailId", detailIds).eq("status", "0");
        criteria.buildRestriction();

        WmsReplenishD replenishD = new WmsReplenishD();
        replenishD.setStatus("-1");

        //取消单据
        long rows = dMapper.updateFieldsByCriteria(replenishD, new FieldSelector().include("status"), criteria);
        if (rows == 0) {
            throw new PrologException("修改补货单状态失败");
        }

        InvStockCancel cancel = new InvStockCancel();
        cancel.setLineBillIds(detailIds);
        RestMessage restMessage = stockResource.replenishCancel(cancel);
        if (!restMessage.isSuccess()) {
            throw new PrologException("补货单取消失败");
        }
    }

    @Override
    public Page<ReplenishUnshelveVO> getReplenishUnshelveList(
            String warehouseId,
            String condition,
            String bldgId,
            String dcId,
            Integer pageNum,
            Integer pageSize,
            String businessType
    ) {

        Assert.notBlank(condition, "请录入托盘号或者拣选单号或者标签条码");

        List<MoveTaskDO> moveTaskDOS = new ArrayList<>();
        List<ReplenishUnshelveVO> list;
        pageNum = pageNum == null ? 1 : pageNum;
        pageSize = pageSize == null ? 10 : pageSize;
        UserDTO user = userTemplate.getUserDTO();
        String[] dtlIds;
        if (StrUtil.isBlank(businessType) || !businessType.equals(INCIDENTALLY.name())) {
            moveTaskDOS = RestMsgHelper.unpack(outBoundClient.getReplenishListByPickTaskId(condition, MoveTaskStatus.UNSHELVE.getCode()));
            Assert.notEmpty(moveTaskDOS, "该托盘没有待确认的任务");
            dtlIds = moveTaskDOS.stream().map(MoveTaskDO::getDetailId).distinct().toArray(String[]::new);
            PageHelper.startPage(pageNum, pageSize, false, true, true);
            list = dMapper.getReplenishUnshelveList(user.getEnterpriseId(), warehouseId, dcId, dtlIds, user.getId(), null);
            Assert.notEmpty(list, "该托盘/拣选单号/标签条码没有待确认的任务");
        }else {
            dtlIds = StrUtil.isNotBlank(condition)?condition.split(","):null;
            list = dMapper.getReplenishUnshelveList(user.getEnterpriseId(), warehouseId, dcId, dtlIds, user.getId(), businessType);
            Assert.notEmpty(list, "没有待确认的任务");
        }
        /*
            "dictCode": "WHOLE_REPLENISH_FIRST",
            "dictName": "整件补货优先",
            "dictCode": "WHOLE_REPLENISH",
            "dictName": "仅整件补货",
         */
        Optional.ofNullable(user.getUserBizModel()).map(UserBizModel::getEmployeeNo).orElseThrow(() -> new PrologException("本账号无权操作"));
        String operatorCode = user.getUserBizModel().getEmployeeNo();
        List<String> areaCodes3 =getAreaCodes(operatorCode, warehouseId,"2011", "WHOLE_REPLENISH");
        List<String> areaCodes4=getAreaCodes(operatorCode, warehouseId,"2011", "WHOLE_REPLENISH_FIRST");
        areaCodes3.addAll(areaCodes4);
        Assert.notEmpty(areaCodes3, "请维护补货员安排表后再操作");


//        Page<RelationWorkgroup> workgroupPage = RestMsgHelper.unpack(controlClient.getPage(warehouseId, null, user.getUsername(), "2010", 15, 1));
//        Assert.notEmpty(workgroupPage.getList(), "请维护补货员安排表后再操作");

        String[] itemIds = list.stream().map(ReplenishUnshelveVO::getItemId).distinct().toArray(String[]::new);
        String[] batchIds = list.stream().map(ReplenishUnshelveVO::getBatchId).distinct().toArray(String[]::new);
        String[] omsBillNos = list.stream().map(ReplenishUnshelveVO::getOmsBillNo).distinct().toArray(String[]::new);

        List<ItemBaseInfoVO> items = RestMsgHelper.unpack(itemResource.getItemBaseList(itemIds));
        List<BatchNumberListVO> batchNumbers = RestMsgHelper.unpack(stockResource.getBatchNumberBasicInfo(batchIds));
        List<OmsDoBillHDO> omsBills = RestMsgHelper.unpack(outBoundClient.getDoBillHByBillNos(omsBillNos, warehouseId));
        List<BaseCommonDict> dicts = RestMsgHelper.unpack(commonResource.getListByDicttype(null, OMS_ASN_BILL_TYPE));

        for (ReplenishUnshelveVO vo : list) {
            if (null != vo.getBusinessType()) {
                if (vo.getBusinessType().equals("INITIATIVE")) {
                    vo.setBusinessType("主动补货");
                } else if (vo.getBusinessType().equals("URGENT")) {
                    vo.setBusinessType("紧急补货");
                } else if (vo.getBusinessType().equals("WAVE")) {
                    vo.setBusinessType("波次补货");
                } else if (vo.getBusinessType().equals("PASSIVE")) {
                    vo.setBusinessType("被动补货");
                } else if (vo.getBusinessType().equals("INCIDENTALLY")) {
                    vo.setBusinessType("顺带补货");
                }
            }

            for (MoveTaskDO moveTaskDO : moveTaskDOS) {
                if (vo.getId().equals(moveTaskDO.getDetailId())){
                    vo.setBarcode(moveTaskDO.getBarcode());
                }
            }
            BigDecimal[] countStock = StockHelper.countStock(vo.getPieceQuantity(), vo.getCaseConversionRate());
            vo.setPcs(countStock[0].intValue());
            setHrdId(condition, vo);
            if (CollUtil.isNotEmpty(items)) {
                items.stream().filter(i -> i.getItemId().equals(vo.getItemId())).findFirst().ifPresent(t -> {
                    vo.setItemCode(t.getItemCode());
                    vo.setItemName(t.getItemName());
                    vo.setSpecification(t.getSpecification());
                    vo.setManufacturerName(t.getManufacturerName());
                });
            }
            if (CollUtil.isNotEmpty(batchNumbers)) {
                batchNumbers.stream().filter(b -> b.getBatchId().equals(vo.getBatchId())).findFirst().ifPresent(t -> {
                    vo.setBatchProductionDateShow(t.getBatchProductionDateShow());
                    vo.setBatchExpiredDateShow(t.getBatchExpiredDateShow());
                });
            }
            if (CollUtil.isNotEmpty(omsBills)) {
                omsBills.stream().filter(o -> o.getBillno().equals(vo.getOmsBillNo())).findFirst().ifPresent(t -> {
                    vo.setCustomerName(t.getShippername());
                    if (CollUtil.isNotEmpty(dicts)) {
                        BaseCommonDict dict = dicts.stream().filter(d -> d.getDictCode().equals(t.getRefBilltype())).findFirst().orElse(null);
                        if (dict != null) {
                            if (null == vo.getBillType()) {
                                vo.setBillType(dict.getDictName());
                            }
                        } else {
                            if (null == vo.getBillType()) {
                                vo.setBillType(t.getRefBilltype());
                            }
                        }
                    }
                });
            }
        }
        return PageUtils.getPage(list);
    }

    public List<String> getAreaCodes(String operatorCode, String warehouseId, String businesstype, String worktype) {
        RestMessage<List<String>> restMessage1 = controlClient.getWorkareaList(operatorCode, warehouseId, businesstype, worktype, null);
        if (!restMessage1.isSuccess())
            return new ArrayList<>();
        List<String> areaCodes = restMessage1.getData();
        if (areaCodes == null || areaCodes.size() == 0)
            return new ArrayList<>();
        return areaCodes;
    }

    private void setHrdId(String condition, ReplenishUnshelveVO vo) {
        if (StrUtil.isNotBlank(vo.getContainerCode())) {
            StringBuffer sb = new StringBuffer();
            sb.append(vo.getContainerCode());
            sb.append("\\");
            sb.append(condition);
            vo.setHrdId(sb.toString());
        } else {
            vo.setHrdId(condition);
        }
    }

    /**
     * 根据托盘余量生成补货下架任务，任务表中的补货下架库位记录立体库大库位，补货下架任务中记录托盘号。
     * 系统自动做补货下架确认（调用作业完成确认接口做立体库库存扣减，记录补货下架任务的确认人，确认时间），
     * 同时生成补货上架任务，使用补货上架推荐库位逻辑（先走货位限定补到平库整件区，分不到货位继续调用上架策略来分配货位）
     */
    @Override
    @TxTransaction(isStart = false)
    @Transactional(rollbackFor = Exception.class)
    public List<ReplenishIncidentallyDTO> replenishIncidentally(ReplenishIncidentallyVO vo) {
        List<ReplenishPassiveSaveDetailVO> details = vo.getDetails();
        String[] itemIds = details.stream().map(ReplenishPassiveSaveDetailVO::getItemId).distinct().toArray(String[]::new);
        Assert.notEmpty(itemIds, "物料ID不能为空");

        List<WarehouseLocatorBasicInfo> locationBasicInfos = RestMsgHelper.unpack(warehouseClient.getLocationBasicInfos(details.stream().map(ReplenishPassiveSaveDetailVO::getToWhLocatorId).distinct().toArray(String[]::new)));

        List<ItemBaseInfoVO> items = RestMsgHelper.unpack(itemResource.getItemBaseList(itemIds));

        WmsReplenishH h = BeanConvertUtils.convertTo(vo, WmsReplenishH::new, (s, t) -> {
            t.setDcId(vo.getDcId());
            t.setBillType(ReplenishBillType.REPLENISH.name());
            t.setSourceType(ReplenishSourceType.WMS.name());
            t.setBusinessType(INCIDENTALLY.name());
            t.setGmtCreate(DateUtil.date());
            t.setCreator(vo.getOpUserId());
            t.setCreatorName(vo.getOpUser());
            t.setGmtModified(DateUtil.date());
            t.setModifier(vo.getOpUserId());
            t.setModifierName(vo.getOpUser());
            t.setBillDate(DateUtil.date());
            t.setAutoBillNum(SequenceApi.getInstance().next(WMS_REPLENISH_AUTO_NUM));
            t.setCustomBillNum(t.getAutoBillNum());
        });
        hMapper.save(h);

        AtomicInteger i = new AtomicInteger();
        List<WmsReplenishD> dList = BeanConvertUtils.convertListTo(details, WmsReplenishD::new, (s, t) -> {
            t.setHeaderId(h.getHeaderId());
            t.setLineNum(String.valueOf(i.getAndIncrement()));
            t.setStatus(ReplenishStatus.OUT.getStatus());
            t.setEnterpriseId(h.getEnterpriseId());
            t.setGmtCreate(DateUtil.date());
            t.setCreator(h.getCreator());
            t.setCreatorName(h.getCreatorName());
            t.setGmtModified(DateUtil.date());
            t.setModifier(h.getModifier());
            t.setModifierName(h.getModifierName());
            t.setPieceQuantity(s.getQuantity());
            t.setErpSrcLineId(s.getErpSrcLineId());
            t.setContainerCode(s.getContainerNo());
            WarehouseLocatorBasicInfo basicInfo = locationBasicInfos.stream().filter(var -> var.getWarehouseLocatorId().equals(s.getToWhLocatorId()) && var.getPickMode().equals(WhOperataConstant.PICK_MODE_DEVICE)).findFirst().orElse(null);
            t.setType(basicInfo == null?REPLENISH_TYPE_WMS:REPLENISH_TYPE_EIS);
            t.setToWhLocatorId(s.getToWhLocatorId());
            t.setToWhAreaId(s.getToWhAreaId());
            t.setToWhAreaCategory(s.getToWhAreaCategory());
            t.setToWhLocatorCode(s.getToWhLocatorCode());
            t.setToWhWarehouseLocatorType(s.getToWhWarehouseLocatorType());
            t.setToWhAreaCode(s.getToWhAreaCode());
        });

        // updateStockLocatorLimit(dList, h);

        dMapper.saveBatch(dList);

        //调用outbound生成补货下架的拣货任务
        doMoveTask(h, dList);

        doPreStock(h.getWarehouseId(), dList);

        String[] ids = dList.stream().map(WmsReplenishD::getDetailId).distinct().toArray(String[]::new);
        replenishStockCheck(ids, UNSHELVE.getCode());

        List<ReplenishIncidentallyDTO> result = Lists.newArrayList();
        for (WmsReplenishD d : dList) {
            ReplenishIncidentallyDTO dto = new ReplenishIncidentallyDTO();
            dto.setDetailId(d.getDetailId());
            if (CollUtil.isNotEmpty(items)) {
                items.stream().filter(t -> t.getItemId().equals(d.getItemId())).findFirst().ifPresent(t -> {
                    dto.setItemName(t.getItemName());
                    dto.setSpecification(t.getSpecification());
                    dto.setManufacturerName(t.getManufacturerName());
                });
            }
            dto.setManufacturerLotNum(d.getManufacturerLotNum());
            dto.setItemCode(d.getItemCode());
            dto.setCaseUom(d.getUom());
            dto.setLocatorCode(d.getToWhLocatorCode());
            BigDecimal[] bigDecimals = d.getQuantity().divideAndRemainder(d.getConversionRate());
            dto.setPcs(bigDecimals[0]);
            dto.setOddPieceQty(bigDecimals[1]);
            dto.setPickTaskDId(details.get(0).getPickTaskDId());
            dto.setPickTaskId(details.get(0).getPickTaskId());
            result.add(dto);
        }
        return result;
    }

    private void updateStockLocatorLimit(List<WmsReplenishD> list, WmsReplenishH header) {

        String warehouseId = header.getWarehouseId();
        String enterpriseId = header.getEnterpriseId();

        //List<ReplenishRuleDTO> rules = RestMsgHelper.unpack(strategyClient.getReplenishRuleList(warehouseId, null, null));
        //Assert.notEmpty(rules, "获取补货规则失败");

        List<AreaInfoByAreaCategoryDTO> areaCategoryList = RestMsgHelper.unpackPageForObject(RestMsgHelper.unpack(warehouseClient.getAreaInfoByAreaCategorys(enterpriseId, warehouseId, new String[]{ZJK.getCode()})), AreaInfoByAreaCategoryDTO.class);
        Assert.notEmpty(areaCategoryList, "获取库区信息失败");

        String[] areaIds = areaCategoryList.stream().map(AreaInfoByAreaCategoryDTO::getWarehouseAreaId).distinct().toArray(String[]::new);

        Map<String, Object> paramsVO = new HashMap<>();
        paramsVO.put("enterpriseId", enterpriseId);
        paramsVO.put("warehouseId", warehouseId);
        paramsVO.put("areaIds", ArrayUtil.join(areaIds, ","));

        findAndUpdateLocator(list, paramsVO);

        List<WmsReplenishD> unDistribution = list.stream().filter(d -> StrUtil.isBlank(d.getToWhLocatorId())).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(unDistribution)) {
            paramsVO.remove("areaIds");
            findAndUpdateLocator(unDistribution, paramsVO);
        }

        WmsReplenishD replenishD = list.stream().filter(d -> StrUtil.isBlank(d.getToWhLocatorId())).findFirst().orElse(null);
        if (replenishD != null) {
            throw new BizException(String.format("请设置商品编码为【%s】的货位限定", replenishD.getItemCode()));
        }
    }

    private void findAndUpdateLocator(List<WmsReplenishD> list, Map<String, Object> paramsVO) {
        for (WmsReplenishD d : list) {
            paramsVO.put("cargoOwnerId", d.getCargoOwnerId());
            paramsVO.put("itemId", d.getItemId());
            log.info("查询货位限定paramsVO:{}", JSONUtil.toJsonStr(paramsVO));
            List<StockLocatorDTO> limitLocators = RestMsgHelper.unpack(stockResource.getLocatorIdLimit(paramsVO));
            log.info("商品:{},货主:{},查询出来的推荐货位信息为:{}",d.getItemId(),d.getCargoOwnerId(),JSONUtil.toJsonStr(limitLocators));
            if (CollUtil.isNotEmpty(limitLocators)) {
                fillToLocatorInfo(d, limitLocators.get(ThreadLocalRandom.current().nextInt(limitLocators.size())));
            }
        }
    }

    private void fillToLocatorInfo(WmsReplenishD d, StockLocatorDTO c) {
        d.setToWhLocatorId(c.getLocatorId());
        d.setToWhAreaId(c.getWarehouseAreaId());
        d.setToWhAreaCategory(c.getWarehouseAreaCategory());
        d.setToWhLocatorCode(c.getLocatorCode());
        d.setToWhWarehouseLocatorType(c.getLocatorType());
        d.setToWhAreaCode(c.getWarehouseAreaCode());
    }


    private List<BaseWmsWarehouseLocator> getLocatorByAreaCode(String enterpriseId,String warehouseAreaCode){
        //查询零货库货位
        WhLocatorQueryVO queryVO = new WhLocatorQueryVO();
        queryVO.setEnterpriseId(enterpriseId);
        queryVO.setWarehouseAreaCode(warehouseAreaCode);
        RestMessage<List<BaseWmsWarehouseLocator>> listRestMessage =
                warehouseClient.locatorList(queryVO);
        if(listRestMessage == null || !listRestMessage.isSuccess() || listRestMessage.getData() == null || listRestMessage.getData().size() <= 0){
            throw new BizException("查询货位失败!");
        }
        return listRestMessage.getData();
    }

    /**
     * A品预补货
     * @param vos
     */
    private List<ReplenishPreVO> aReplenishPre(List<ReplenishPreVO> vos){
        log.info("a品预补货入参：{}",JSONObject.toJSONString(vos));

        List<ReplenishPreVO> initiatives = new ArrayList<>();

        ReplenishUrgentSaveVO replenishUrgentSaveVO = new ReplenishUrgentSaveVO();
        replenishUrgentSaveVO.setWarehouseId(vos.get(0).getWarehouseId());

        for (ReplenishPreVO vo : vos) {
            List<Map> searchForMap = null;
            try {
                Map<String, Object> map = MapUtils.put("warehouseId", vo.getWarehouseId())
                        .put("itemId", vo.getItemId())
                        .put("currentUser.enterpriseId", vo.getEnterpriseId())
                        .put("auto", "1").getMap();
                log.info("A品预补货调用查询服务查询A品容器库存列表入参：{}",JSONObject.toJSONString(map));
                searchForMap = searchApi.searchForMap("querylocator", map);
                log.info("A品预补货调用查询服务查询A品容器库存返回结果：{}",JSONObject.toJSONString(searchForMap));
            }catch (Exception e){
                e.printStackTrace();
                log.info("调用查询服务：querylocator失败!");
            }

            if(searchForMap == null || searchForMap.size() <= 0){
                initiatives.add(vo);
                continue;
            }

            Map map = searchForMap.get(0);

            BigDecimal avlQuantity = new BigDecimal((Double) map.get("avlQuantity"));
            if(vo.getPieceQuantity().compareTo(avlQuantity) > 0){
                initiatives.add(vo);
                continue;
            }

            ReplenishUrgentSaveDetailVO detailVO = new ReplenishUrgentSaveDetailVO();
            List<ReplenishUrgentSaveDetailVO> details = new ArrayList<>();
            detailVO.setCargoOwnerId((String) (map.get("cargoOwnerId")));
            detailVO.setItemId(vo.getItemId());
            detailVO.setPieceQuantity(avlQuantity);
            detailVO.setToWhLocatorId((String) map.get("sjLocatorId"));
            detailVO.setWhLocatorId((String) map.get("locatorId"));
            detailVO.setBatchId((String) map.get("batchId"));
            detailVO.setContainerCode((String) map.get("containerCode"));

            details.add(detailVO);

            replenishUrgentSaveVO.setWaveId(vo.getWaveId());
            replenishUrgentSaveVO.setIsSendEis("N");
            if(StringUtils.isBlank(vo.getWaveId())){
                replenishUrgentSaveVO.setIsSendEis("Y");
            }

            replenishUrgentSaveVO.setDetails(details);
            this.replenishUrgent(replenishUrgentSaveVO);
        }

        return initiatives;
    }

    /**
     * bc品预补货
     * @param vos
     */
    private void bcReplenishPre(List<ReplenishPreVO> vos){
        log.info("bc品预补货入参：{}",JSONObject.toJSONString(vos));
        for (ReplenishPreVO saveVO : vos) {

            //根据品批查询整件库库存
            List<WmsInvStockQtyQueryVO> queryVOS = new ArrayList<>();
            WmsInvStockQtyQueryVO ls = new WmsInvStockQtyQueryVO();
            ls.setEnterpriseId(saveVO.getEnterpriseId());
            ls.setWarehouseId(saveVO.getWarehouseId());
            ls.setCargoOwnerId(saveVO.getCargoOwnerId());
            if(!StringUtils.isBlank(saveVO.getManufacturerLotNum())){
                ls.setManufacturerLotNum(saveVO.getManufacturerLotNum());
            }
            ls.setItemId(saveVO.getItemId());
            queryVOS.add(ls);
            List<WmsInvStockQtyVO> stockQtyList = RestMsgHelper.unpack(stockResource.getAvailableQuantityList(queryVOS));
            if(stockQtyList == null || stockQtyList.size() <= 0 ){
                log.info("根据商品："+saveVO.getItemId()+"，批次："+saveVO.getManufacturerLotNum()+"查询库存失败！");
                continue;
            }
            List<String> zjk = CollUtil.toList("MJK","ZJK","AGVZ","AGVSP","AGVQX","AGVZY");
            List<WmsInvStockQtyVO> zjStockQtyList = stockQtyList.stream().filter(item -> zjk.contains(item.getWarehouseAreaCategory())).collect(Collectors.toList());
            if(zjStockQtyList == null || zjStockQtyList.size() <= 0 ){
                log.info("商品："+saveVO.getItemId()+"，批次："+saveVO.getManufacturerLotNum()+"整件库库存不存在！");
                continue;
            }
            log.info("预补货，查询整件库库存：{}",JSONObject.toJSONString(zjStockQtyList));

            //判断是否指定批次补货
            if(StringUtils.isBlank(saveVO.getManufacturerLotNum())){
                ReplenishInitiativeSaveVO replenishInitiativeSaveVO = new ReplenishInitiativeSaveVO();
                replenishInitiativeSaveVO.setWarehouseId(saveVO.getWarehouseId());
                List<ReplenishInitiativeSaveDetailVO> details = new ArrayList<>();
                ReplenishInitiativeSaveDetailVO detailVO = new ReplenishInitiativeSaveDetailVO();
                detailVO.setCargoOwnerId(saveVO.getCargoOwnerId());
                detailVO.setItemId(saveVO.getItemId());
                //计算补货数量
                BigDecimal zjkAvailableQuantity = zjStockQtyList.stream().map(WmsInvStockQtyVO::getAvailableQuantity).reduce(BigDecimal.ZERO, BigDecimal::add);
                if(zjkAvailableQuantity.compareTo(saveVO.getPieceQuantity()) >= 0){
                    detailVO.setPieceQuantity(saveVO.getPieceQuantity());
                }else {
                    detailVO.setPieceQuantity(zjkAvailableQuantity);
                }
                detailVO.setWarehouseAreaCategory(saveVO.getWarehouseAreaCategory());
                details.add(detailVO);
                replenishInitiativeSaveVO.setDetails(details);
                replenishInitiativeSaveVO.setWaveId(saveVO.getWaveId());
                replenishInitiativeSaveVO.setIsSendEis("N");
                if(StringUtils.isBlank(saveVO.getWaveId())){
                    replenishInitiativeSaveVO.setIsSendEis("Y");
                }
                log.info("预补货-主动补货入参：{}",JSONUtil.toJsonStr(replenishInitiativeSaveVO));
                try {
                    this.replenishInitiative(replenishInitiativeSaveVO);
                }catch (Exception e){
                    e.printStackTrace();
                    log.info("预补货-主动补货失败：{}",e.getMessage());
                }

            }else {
                List<BaseWmsWarehouseLocator> wmsWarehouseLocators = getLocatorByAreaCode(saveVO.getEnterpriseId(),saveVO.getWarehouseAreaCategory());

                //循环生成紧急补货
                BigDecimal pieceQuantity = saveVO.getPieceQuantity();
                for (WmsInvStockQtyVO wmsInvStockQtyVO : zjStockQtyList) {
                    if(pieceQuantity.compareTo(BigDecimal.ZERO) <= 0){
                        return;
                    }
                    ReplenishUrgentSaveVO replenishUrgentSaveVO = new ReplenishUrgentSaveVO();
                    replenishUrgentSaveVO.setWarehouseId(saveVO.getWarehouseId());

                    List<ReplenishUrgentSaveDetailVO> details = new ArrayList<>();
                    ReplenishUrgentSaveDetailVO detailVO = new ReplenishUrgentSaveDetailVO();
                    if(pieceQuantity.compareTo(wmsInvStockQtyVO.getAvailableQuantity()) <= 0){
                        detailVO.setPieceQuantity(pieceQuantity);
                    }
                    detailVO.setWhLocatorId(wmsInvStockQtyVO.getLocatorId());
                    detailVO.setToWhLocatorId(wmsWarehouseLocators.get(0).getWarehouseLocatorId());
                    detailVO.setCargoOwnerId(saveVO.getCargoOwnerId());
                    detailVO.setItemId(saveVO.getItemId());
                    detailVO.setBatchId(wmsInvStockQtyVO.getBatchId());
                    details.add(detailVO);

                    replenishUrgentSaveVO.setDetails(details);

                    replenishUrgentSaveVO.setWaveId(saveVO.getWaveId());
                    replenishUrgentSaveVO.setIsSendEis("N");
                    if(StringUtils.isBlank(saveVO.getWaveId())){
                        replenishUrgentSaveVO.setIsSendEis("Y");
                    }

                    try {
                        log.info("预补货-紧急补货入参：{}",JSONUtil.toJsonStr(replenishUrgentSaveVO));
                        this.replenishUrgent(replenishUrgentSaveVO);
                        //减去已补货数量
                        pieceQuantity = pieceQuantity.subtract(wmsInvStockQtyVO.getAvailableQuantity());
                    }catch (Exception e){
                        e.printStackTrace();
                        log.info("预补货-紧急补货失败：{}",e.getMessage());
                    }

                }

            }
        }
    }

    /**
     * 生成被动补货任务
     * @param saveVO
     * @return
     */
    private List<WmsReplenishD> handleReplenishPassive(ReplenishPassiveSaveVO saveVO){
        RestMessage<List<BaseWmsWarehouseMst>> whResult = warehouseClient.getWarehouseBasicInfo(new String[]{saveVO.getWarehouseId()});
        if (!whResult.isSuccess() || whResult.getData() == null || whResult.getData().isEmpty()) {
            throw new BizException("未找到有效的仓库信息");
        }

        BaseWmsWarehouseMst warehouse = whResult.getData().get(0);

        //转换主信息
        WmsReplenishH wmsReplenishH = BeanConvertUtils.convertTo(saveVO, WmsReplenishH::new, (s, t) -> {
            t.setDcId(warehouse.getDcId());
            t.setBillType(ReplenishBillType.REPLENISH.name());
            t.setSourceType(ReplenishSourceType.WMS.name());
            t.setBusinessType(ReplenishBusinessType.PASSIVE.name());
            t.setGmtCreate(DateUtil.date());
            t.setCreator(saveVO.getOpUserId());
            t.setCreatorName(saveVO.getOpUser());
            t.setGmtModified(DateUtil.date());
            t.setModifier(saveVO.getOpUserId());
            t.setModifierName(saveVO.getOpUser());
        });

        wmsReplenishH.setBillDate(DateUtil.date());
        wmsReplenishH.setAutoBillNum(SequenceApi.getInstance().next(WMS_REPLENISH_AUTO_NUM));
        wmsReplenishH.setCustomBillNum(wmsReplenishH.getAutoBillNum());
        hMapper.save(wmsReplenishH);

        AtomicInteger i = new AtomicInteger();
        List<WmsReplenishD> dList = BeanConvertUtils.convertListTo(saveVO.getDetails(), WmsReplenishD::new, (s, t) -> {
            t.setHeaderId(wmsReplenishH.getHeaderId());
            t.setLineNum(String.valueOf(i.getAndIncrement()));
            t.setStatus(ReplenishStatus.CREATE.getStatus());
            t.setEnterpriseId(wmsReplenishH.getEnterpriseId());
            t.setGmtCreate(DateUtil.date());
            t.setCreator(saveVO.getOpUserId());
            t.setCreatorName(saveVO.getOpUser());
            t.setGmtModified(DateUtil.date());
            t.setModifier(saveVO.getOpUserId());
            t.setModifierName(saveVO.getOpUser());
        });

        dMapper.saveBatch(dList);

        return dList;
    }

}
