package com.prolog.cs.biz.outbound.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.fasterxml.jackson.core.type.TypeReference;
import com.github.pagehelper.PageHelper;
import com.prolog.cs.basic.chain.model.customer.BaseCustomerSitesAll;
import com.prolog.cs.basic.chain.model.customer.vo.CustomerAndSupplierQueryVO;
import com.prolog.cs.basic.common.model.BaseCommonDict;
import com.prolog.cs.basic.item.model.vo.ItemMstQueryVO;
import com.prolog.cs.basic.item.model.vo.ItemMstVO2;
import com.prolog.cs.basic.item.model.vo.WaveItemInfoVO;
import com.prolog.cs.biz.inbound.dto.PageParam;
import com.prolog.cs.biz.outbound.core.Constants;
import com.prolog.cs.biz.outbound.dao.*;
import com.prolog.cs.biz.outbound.funcflow.WaveCreateFunc;
import com.prolog.cs.biz.outbound.funcflow.WaveDistributeFunc;
import com.prolog.cs.biz.outbound.model.*;
import com.prolog.cs.biz.outbound.model.dto.*;
import com.prolog.cs.biz.outbound.model.entity.*;
import com.prolog.cs.biz.outbound.model.vo.*;
import com.prolog.cs.biz.outbound.model.vo.check.WavePendingBillVO;
import com.prolog.cs.biz.outbound.resource.*;
import com.prolog.cs.biz.outbound.service.OrderOutboundStatusBackService;
import com.prolog.cs.biz.outbound.service.RedService;
import com.prolog.cs.biz.outbound.service.WavesService;
import com.prolog.cs.biz.outbound.utils.AlgorithmUtils;
import com.prolog.cs.biz.outbound.utils.BillUtils;
import com.prolog.cs.biz.outbound.utils.ItemUtils;
import com.prolog.cs.biz.outbound.utils.StatusUtil;
import com.prolog.framework.authority.core.dto.UserDTO;
import com.prolog.framework.authority.user.UserTemplate;
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.*;
import com.prolog.framework.dao.util.PageUtils;
import com.prolog.framework.utils.*;
import com.prolog.function.annotation.Flow;
import com.prolog.function.annotation.FuncImpl;
import com.prolog.fx.inf.core.Exception.RetryException;
import com.prolog.fx.inf.core.InterfaceType;
import com.prolog.fx.inf.core.StandardInterface;
import com.prolog.fx.inf.core.annotation.PrologInterface;
import com.prolog.fx.inf.core.dto.SimpleLog;
import com.prolog.upcloud.base.inventory.vo.InvStockCancelOut;
import com.prolog.upcloud.base.inventory.vo.InvStockWave;
import com.prolog.upcloud.base.inventory.vo.WmsInvStockQtyQueryVO;
import com.prolog.upcloud.base.inventory.vo.WmsInvStockQtyVO;
import com.prolog.upcloud.base.strategy.model.BillSortRuleVO;
import com.prolog.upcloud.base.strategy.model.BusinessControl;
import com.prolog.upcloud.base.wh.operate.vo.ReplenishPreVO;
import org.apache.commons.lang.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
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.PostConstruct;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;

import static com.prolog.cs.biz.outbound.model.BILL_STATUS.BATCH_LIMIT;

@Service
@PrologInterface(key="test",name="订单测试接口",type = InterfaceType.Send)
public class WavesServiceImpl implements WavesService, StandardInterface<InterfaceBill,Object> {

    private static final BigDecimal DEFAULT_WEIGHT = BigDecimal.valueOf(33 * 33 * 33);
    private static final BigDecimal DEFAULT_VOLUME = BigDecimal.ONE;
    private Logger logger = LoggerFactory.getLogger(WavesServiceImpl.class);

    @Autowired
    private WaveMapper waveMapper;

    @Autowired
    PickTaskHMapper pickTaskHMapper;

    @Autowired
    private WaveBillGroupMapper groupMapper;

    @Autowired
    private WaveBillMapper billMapper;

    @Autowired
    private WaveBillDMapper billDMapper;

    @Autowired
    private OmsDoBillHMapper omsDoBillHMapper;

    @Autowired
    private OmsDoBillDMapper omsDoBillDMapper;

    @Autowired
    private RedService redService;

    @Autowired
    private UserTemplate userTemplate;

    @Value("${prolog.dao.keyGenerator.snowFlake.dataCenterId:15}")
    private long dataCenterId;

    @Autowired
    WaveDistributeServiceImpl waveDistribute;

    @Value("${prolog.dao.keyGenerator.snowFlake.machineId:2}")
    private long machineId;

    @Autowired
    WaveStorageMapper waveStorageMapper;

    @Autowired
    WorkStageRuntimeMapper workStageMapper;
    private String cargo_owner_pick_rule = "";

    @Autowired
    private StrategyResource strategyResource;

    @Autowired
    private SaleRedRecordMapper saleRedRecordMapper;

    @Autowired
    private ItemClient itemClient;

    @Autowired
    private ChainResource chainResource;

    @Autowired
    private MoveTaskMapper moveTaskMapper;

    @Autowired
    private MoveTaskRefMapper moveTaskRefMapper;

    @Autowired
    private WmsMoveTaskRefMapper wmsMoveTaskRefMapper;

    @Autowired
    private InventoryResource inventoryResource;

    @Autowired
    private AmqpTemplate amqpTemplate;

    @Resource
    private BillDPrePositionMapper prePositionMapper;

    @Autowired
    private CommonClient commonClient;

    @Autowired
    private OperateResource operateResource;

    @Autowired
    private PickTaskDMapper pickTaskDMapper;

    @Resource
    private WaveMonitorMapper  waveMonitorMapper;

    WaveCreateFunc.queryOmsDoBillHList queryOmsDoBillHList;

    WaveCreateFunc.validInputParam validInputParam;

    WaveCreateFunc.queryOmsDoBillDList queryOmsDoBillDList;

    WaveCreateFunc.validModel validModel;

    WaveCreateFunc.saveWaveDO saveWaveDO;

    WaveCreateFunc.saveWaveGroup saveWaveGroup;

    WaveDistributeFunc.validDistributeParam validDistributeParam;

    WaveDistributeFunc.queryWaveMonitor queryWaveMonitor;

    WaveDistributeFunc.calculateMonitorQuantity calculateMonitorQuantity;

    WaveCreateFunc.validBillPickQty validBillPickQty;

    @PostConstruct
    public void init() {
        queryOmsDoBillHList = this::queryOmsDoBillHList;
        validInputParam = this::validInputParam;
        validBillPickQty = this::validBillPickQty;
        queryOmsDoBillDList = this::queryOmsDoBillDList;
        validModel = this::validModel;
        saveWaveDO = this::saveWaveDO;
        saveWaveGroup = this::saveWaveGroup;
        validDistributeParam = this::validDistributeParam;
        queryWaveMonitor = this::queryWaveMonitor;
        calculateMonitorQuantity = this::calculateMonitorQuantity;
    }

    /**
     * 波次下发
     *
     * @param vo vo
     * @return RestMessage RestMessage
     */
    @Transactional
    @Flow(name = "波次下发", category = "出库/波次管理/波次下发", index = 2, description = "波次下发")
    @Override
    public RestMessage<String> waveDistribute(WaveDistributeDTO vo) {
        long startTime = System.currentTimeMillis();
        // 校验波次下发入参
        validDistributeParam.validDistributeParam(vo);
        if (!StringUtils.isBlank(vo.getBillId()) && vo.getWavesIds().size()!=1) {
            throw new BizException("强制下发，只能单个波次单个订单操作！");
        }
        // 波次下发（根据波次ids）提示近效期商品 - by Huanglh 2022-02-21
        List<String> msgList = new ArrayList<>();
        for (String wavesId : vo.getWavesIds()) {
            String warnMsg = waveDistribute.distributeWave(wavesId, vo, "0");
            if (!StringUtils.isBlank(warnMsg)) {
                msgList.add(warnMsg);
            }
        }
        // 提示近效期商品 - by Huanglh 2022-02-21
        String data = msgList.size() > 0 ? String.join(";", msgList) : "操作成功";
        long endTime = System.currentTimeMillis();
        logger.info("波次下发耗时:{}",endTime-startTime);
        return RestMessage.success(data,null);
    }

    /**
     * 任务试算
     *
     * @param vo vo
     * @return RestMessage RestMessage
     */
    @Flow(name = "任务试算", category = "出库/波次管理/任务试算", index = 9, description = "任务试算")
    @Override
    public RestMessage<List<WavePreCalResultVO>> waveTaskTrial(WaveDistributeDTO vo) {
        // 任务试算
        validDistributeParam.validDistributeParam(vo);
        // 任务试算（根据波次ids）
        List<WavePreCalResultVO> rsList = new ArrayList<>();
        List<String> errorMsgList=new ArrayList();
        for (String wavesId : vo.getWavesIds()) {
            try {
                rsList.addAll(waveDistribute.waveTaskTrial(wavesId, "0",errorMsgList));
            } catch (BizException e) {
                logger.error("waveTaskTrial",e);
                errorMsgList.add(e.getMessage());
            } catch (Exception e) {
                logger.error("waveTaskTrial",e);
            }
        }
        String errorMsg = errorMsgList.stream().collect(Collectors.joining());
        if(rsList==null || rsList.size()==0){
            throw new BizException(StringUtils.isBlank(errorMsg)?"试算失败":"试算失败："+errorMsg);
        }
        return RestMessage.success(StringUtils.isBlank(errorMsg)?"操作成功":errorMsg,rsList);
    }

    /**
     * 波次下发入参校验
     */
    @FuncImpl(name = "波次下发入参校验", func = WaveDistributeFunc.validDistributeParam.class, description = "波次ID及波次分组ID不能为空")
    private void validDistributeParam(WaveDistributeDTO vo) {
        List<String> wavesIds = vo.getWavesIds();
        List<String> wavesGroupIds = vo.getWavesGroupIds();
        if ((wavesIds == null || wavesIds.size() == 0) && (wavesGroupIds == null || wavesGroupIds.size() == 0)) {
            throw new ParameterException("请选择波次！");
        }
    }

    @Override
    public Page<WaveVO> getWaves(Integer pageNum, Integer pageSize, String wareHouseId,
                                 String waveId, String billCode, String cargoOwnerId, String status,String type) {
        PageHelper.startPage(pageNum, pageSize);
        List<WaveDO> waveDOS = waveMapper.getWaveDos(wareHouseId, waveId, billCode, cargoOwnerId, status,type);
        Page<WaveDO> page = PageUtils.getPage(waveDOS);
        Page<WaveVO> returnPage = new Page<WaveVO>();
        returnPage.setPageNum(page.getPageNum());
        returnPage.setPageSize(page.getPageSize());
        returnPage.setEndRow(page.getEndRow());
        returnPage.setStartRow(page.getStartRow());
        returnPage.setTotalCount(page.getTotalCount());

        List<WaveVO> waveVos = waveDOS.stream().map(waveDO -> {
            WaveVO waveVO = new WaveVO();
            BeanUtils.copyProperties(waveDO, waveVO);
            fillWaveVo(waveVO);
            getWaveStatisInfo(waveVO);
            return waveVO;
        }).collect(Collectors.toList());
        returnPage.setList(waveVos);
        return returnPage;
    }

    private void getWaveStatisInfo(WaveVO waveVO) {
        WaveStatisticVO statistics = getStatistics(waveVO.getId());
        waveVO.setTotalBills(statistics.getTotalBills());
        waveVO.setPendingBills(statistics.getPendingBills());
        waveVO.setGnkfTasks(statistics.getGnkfTasks());
        waveVO.setZjTasks(statistics.getZjTasks());
        waveVO.setLhTasks(statistics.getLhTasks());
        Integer zdTasks = statistics.getZdTasks();
        if(zdTasks!=null && zdTasks>0){
            waveVO.setZjTasks(0);
            Integer zjTasks = statistics.getZjTasks()==null?0:statistics.getZjTasks();
            waveVO.setZdTasks(zdTasks+zjTasks);
        }
        waveVO.setBhTasks(statistics.getBhTasks());
    }

    @Override
    public WaveVO getBillsByWaveId(String waveId) {
        WaveDO waveDo = waveMapper.findById(waveId, WaveDO.class);
        WaveVO waveVO = new WaveVO();
        BeanUtils.copyProperties(waveDo, waveVO);
        getWaveStatisInfo(waveVO);
        List<StatusRecordDO> status = StatusUtil.findStatus(waveId);
        waveVO.setStatusList(status);
        fillWaveVo(waveVO);
        Criteria cr = Criteria.forClass(WaveBillDO.class);
        cr.restrictionBuilder().eq("waveId", waveId).ne("isMerged", 2);
        cr.buildRestriction();
        List<WaveBillDO> waveBillDOs = billMapper.findByCriteria(cr);

        List<WaveBillVO> waveBillVOs = getBillVo(waveBillDOs);
        waveVO.setBills(waveBillVOs);
        return waveVO;
    }

    private void fillWaveVo(WaveVO waveVO) {
        String waveId = waveVO.getId();
        Map<String, Object> totalInfo = billMapper.getTotalInfo(waveId, null);
        if (totalInfo != null) {
            BigDecimal totalQty = (BigDecimal) totalInfo.get("PIECE_QTY");
            BigDecimal totalVolume = (BigDecimal) totalInfo.get("TOTAL_VOLUME");
            BigDecimal totalWeight = (BigDecimal) totalInfo.get("TOTAL_WEIGHT");
            long numOfBill = billMapper.getNumOfBill(waveId);
            waveVO.setTotalNum(totalQty);
            waveVO.setTotalVolume(totalVolume);
            waveVO.setTotalWeight(totalWeight);
            waveVO.setBillNum(BigDecimal.valueOf(numOfBill));
        }
    }

    @Override
    public List<WaveBillVO> getBillVo(List<WaveBillDO> waveBillDOs) {
        List<WaveBillVO> waveBillVOs = waveBillDOs.stream().map(waveBillDO -> {
            WaveBillVO waveBillVO = new WaveBillVO();
            BeanUtils.copyProperties(waveBillDO, waveBillVO);
            fillWaveBillVo(waveBillVO);
            return waveBillVO;
        }).collect(Collectors.toList());
        return waveBillVOs;
    }

    private void fillWaveBillVo(WaveBillVO waveBillVO) {
        String billId = waveBillVO.getId();
        BigDecimal totalVolume = billMapper.getTotalVolume(null, billId);
        BigDecimal totalQty = billMapper.getTotalQty(null, billId);
        BigDecimal totalWeight = billMapper.getTotalWeight(null, billId);
        long numOfItem = billMapper.getNumOfItem(billId);
        waveBillVO.setTotPcs(BigDecimal.valueOf(numOfItem));
        waveBillVO.setTotQty(totalQty);
        waveBillVO.setTotVolume(totalVolume);
        waveBillVO.setTotWeight(totalWeight);
    }

    @Override
    public WaveBillVO getItemsByBillId(String billId,Integer withStockQty) {
        Criteria cr = Criteria.forClass(WaveBillDDO.class);
        cr.restrictionBuilder().eq("billId", billId);
        cr.buildRestriction();
        List<WaveBillDDO> waveBillDDOs = billDMapper.findByCriteria(cr);
        WaveBillDO bill = billMapper.findById(billId, WaveBillDO.class);
        List<WaveBillDVO> waveBillDVOs = getBillDVOS(bill,waveBillDDOs,withStockQty);

        WaveBillVO billVO = new WaveBillVO();
        BeanUtils.copyProperties(bill, billVO);
        fillWaveBillVo(billVO);
        billVO.setItems(waveBillDVOs);
        return billVO;
    }

    private List<WaveBillDVO> getBillDVOS(WaveBillDO bill,List<WaveBillDDO> waveBillDDOs,Integer withStockQty) {
        Set<String> collect = waveBillDDOs.stream().map(WaveBillDDO::getItemId).distinct().collect(Collectors.toSet());
        ItemUtils itemInfos = new ItemUtils(collect);
        RestMessage<List<WmsInvStockQtyVO>> stockResult=null;
        if(withStockQty!=null && withStockQty==1){
            List<WmsInvStockQtyQueryVO> list=new ArrayList();
            for (int i = 0; i < waveBillDDOs.size(); i++) {
                WaveBillDDO WaveBillDDO=waveBillDDOs.get(i);
                WmsInvStockQtyQueryVO queryVO = new WmsInvStockQtyQueryVO();
                queryVO.setEnterpriseId(bill.getEnterpriseId());
                queryVO.setCargoOwnerId(bill.getConsignorId());
                queryVO.setWarehouseId(bill.getWarehouseId());
                queryVO.setManufacturerLotNum(WaveBillDDO.getManufacturerLotNum());
                queryVO.setItemId(WaveBillDDO.getItemId());
                queryVO.setStatus(WaveBillDDO.getStockStatus());
                list.add(queryVO);
            }
            // 请求库存批次信息
            stockResult = inventoryResource.getAvailableQuantityList(list);
        }
        List<WaveBillDVO> waveBillDVOs=new ArrayList<>();
        for (int i = 0; i < waveBillDDOs.size(); i++) {
            WaveBillDDO billD=waveBillDDOs.get(i);
            WaveBillDVO waveBillDVO = new WaveBillDVO();
            BeanUtils.copyProperties(billD, waveBillDVO);
            waveBillDVO.setSrcId(bill.getSrcId());
            waveBillDVO.setSrcCode(bill.getSrcCode());
            ItemMstVO2 itemInfo = itemInfos.getByItemId(waveBillDVO.getItemId());
            waveBillDVO.setManufacturer(itemInfo.getManufacturerName());
            if(stockResult!=null && stockResult.isSuccess() && stockResult.getData()!=null){
                //当订单明细行，相同商品时有bug，待修复，改为商品级试算，可以解决
                String manufacturerLotNum = billD.getManufacturerLotNum();
                String stockStatus=StringUtils.isBlank(billD.getStockStatus())?"1":billD.getStockStatus();//默认传合格
                if(StringUtils.isBlank(manufacturerLotNum)){
                    BigDecimal qty= stockResult.getData().stream().filter(t->t.getItemId().equals(billD.getItemId())
                                     && t.getStatus().equals(stockStatus))
                            .map(t->t.getAvailableQuantity()).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
                    waveBillDVO.setStockQty(qty);
                }else{
                    BigDecimal qty= stockResult.getData().stream().filter(t->t.getItemId().equals(billD.getItemId())
                            && t.getManufacturerLotNum().equals(manufacturerLotNum) && t.getStatus().equals(stockStatus))
                            .map(t->t.getAvailableQuantity()).reduce(BigDecimal::add).orElse(BigDecimal.ZERO);
                    waveBillDVO.setStockQty(qty);
                }
                waveBillDVO.setUnStockQty(waveBillDVO.getPieceQty().subtract(waveBillDVO.getStockQty())); //库存不足数量
            }

            waveBillDVOs.add(waveBillDVO);
        }
        return waveBillDVOs;
    }

    @Override
    public RestMessage<Boolean> cancelBillById(String billId) {
        WaveBillDO bill = billMapper.findById(billId, WaveBillDO.class);
        if (bill == null) {
            logger.error("找不到指定的订单{}", billId);
            return RestMessage.doSuccess(true);
        }
        BILL_STATUS status = bill.getStatus();
        switch (status) {
            case CREATED:
                break;
            case DISTRIBUTED:
                break;
            case PRE_POSITION:
                break;
            case POSITIONED:
                break;
            case POSITION_ERROR:
                break;
            case WORKSTAGE_FOUND:
                break;
            case WORKSTAGE_ERROR:
                // 集货区异常时会取消预占，以免设备区补货卡流程
                break;
            case PICKED:
                break;
            case PACKAGED:
                break;
            case INNER_CHECKED:
                break;
            case INNER_CHECK_ERROR:
                break;
            case OUT_CHECKED:
                break;
            case OUT_CHECK_ERROR:
                break;
            case COMPLETED:
                break;
        }
        if (status.equals(BATCH_LIMIT)  || status.equals(BILL_STATUS.POSITION_ERROR) || status.equals(BILL_STATUS.WORKSTAGE_FOUND)
                || bill.getStatus().equals("WORKSTAGE_ERROR")) {
            List<WaveBillDDO> waveBillDDOs = billDMapper.findByMap(MapUtils.put("billId", bill.getId()).getMap(), WaveBillDDO.class);
            String[] lineBillIds = waveBillDDOs.stream().map(WaveBillDDO::getId).toArray(String[]::new);
            InvStockCancelOut stockCancel = new InvStockCancelOut();
            stockCancel.setLineBillIds(lineBillIds);
            stockCancel.setOutType("1");
            logger.info("inventoryResource.outCancel，stockCancel={}", JSON.toJSONString(stockCancel));
            RestMessage restMessage = inventoryResource.outCancel(stockCancel);
            if (!restMessage.isSuccess()) {
                logger.error("inventoryResource.outCancel，restMessage={}", JSON.toJSONString(restMessage));
                throw new BizException("扣减库存失败: " + restMessage.getMessage());
            } else {
                logger.info("inventoryResource.outCancel，restMessage={}", JSON.toJSONString(restMessage));
            }
        }
        // 根据billId更新出库单头表状态为初始
        omsDoBillHMapper.updateMapById(bill.getSrcId(),MapUtils.put("status", "10").getMap(),OmsDoBillHDO.class);
        Criteria crt = Criteria.forClass(OmsDoBillDDO.class);
        Restriction r1 = Restrictions.eq("billid", bill.getSrcId());
        crt.setRestriction(Restrictions.and(r1));
        omsDoBillDMapper.updateMapByCriteria(MapUtils.put("status", "10").getMap(),crt);
        billMapper.deleteById(billId, WaveBillDO.class);
        billDMapper.deleteByMap(MapUtils.put("billId", billId).getMap(), WaveBillDDO.class);
        waveStorageMapper.deleteByMap(MapUtils.put("billId", billId).getMap(), WaveStorageDO.class);
        moveTaskRefMapper.deleteByMap(MapUtils.put("billId", billId).getMap(), WmsMoveTaskRef.class);
        return RestMessage.success("更新成功", false);
    }

    @Override
    public Boolean retryBillById(String billId) {
        return Boolean.TRUE;
    }

    @Flow(name = "波次监控", category = "出库/波次管理/波次监控", index = 5, description = "波次监控")
    @Override
    public WaveStatisticVO getStatistics(String waveId) {
        // 根据波次ID，查询波次数据
        List<WaveBillDO> allBills = queryWaveMonitor.queryWaveMonitor(waveId);
        // 计算波次监控各指标
        WaveStatisticVO waveStatisticVO = new WaveStatisticVO();
        calculateMonitorQuantity.calculateMonitorQuantity(waveStatisticVO, waveId, allBills);
        return waveStatisticVO;
    }

    @Override
    public Page<WaveMonitorPageDTO> waveMonitorPage(WaveMonitorPageVO vo) {
        PageUtils.startPage(vo.getPageNum(),vo.getPageSize());
        List<WaveMonitorPageDTO> list = waveMonitorMapper.waveMonitorPage(vo);
        return PageUtils.getPage(list);
    }

    @Transactional
    @Override
    public String autoCreateWave() {
        // 先查询待创建波次数据;
        BillQueryDTO billQueryDTO = new BillQueryDTO();
//        billQueryDTO.setBilltype("1"); // 只创建销售出库单;
        List<OmsDoBillHDO> omsBill = omsDoBillHMapper.findOmsBill(billQueryDTO, null);
        if (CollUtil.isEmpty(omsBill)) {
            logger.info("没有待创建波次的数据");
            return null;
        }
        OmsDoBillHDO omsDoBillHDO = omsBill.get(0);
        // 查询业务控制参数：automatic_wave,bill_num
        Integer automaticWave = 1; // 默认自动创建波次
        Integer billNum = 50 ; // 默认自动创建波次数量为50
        RestMessage<Map<String, BusinessControl>> rest = strategyResource.getControlByCodes(new String[]{"automatic_wave","bill_num"}, omsDoBillHDO.getBranchid(), null, omsDoBillHDO.getEnterpriseId(), null);
        if (rest.isSuccess() && rest.getData() != null) {
            Map<String,BusinessControl> conMap = rest.getData();
            if (conMap.containsKey("automatic_wave")) {
                automaticWave = Integer.valueOf(conMap.get("automatic_wave").getRegValue());
            }
            if (conMap.containsKey("bill_num")) {
                if (Integer.valueOf(conMap.get("bill_num").getRegValue())<=200) {
                    billNum = Integer.valueOf(conMap.get("bill_num").getRegValue());
                }
            }
        }
        if (0 == automaticWave) {
            throw new BizException("参数配置为不自动创建波次!");
        }
        if(omsBill.size() < billNum){
            throw new BizException("未下发订单数量小于自动下发波次订单数量!");
        }
        // 自动创建波次集合
        List<OmsDoBillHDO> autoList = new ArrayList<>();
        if (omsBill.size()>billNum) {
            autoList = omsBill.subList(0,billNum);
        } else {
            autoList = omsBill;
        }
        if (CollUtil.isNotEmpty(autoList)) {
            WavesCreateDTO wavesCreateVO = new WavesCreateDTO();
            List<String> billIdList = autoList.stream().map(OmsDoBillHDO::getBillid).collect(Collectors.toList());
            wavesCreateVO.setBillIdList(billIdList);
            wavesCreateVO.setWarehouseId(omsDoBillHDO.getBranchid());
            wavesCreateVO.setEnterpriseId(omsDoBillHDO.getEnterpriseId());
            // 波次创建
            String waveId = this.wavesCreate(wavesCreateVO)[0];
            try {
                //自动预补货
                this.autoPreReplenish(waveId);
            }catch (Exception e){
                e.printStackTrace();
                logger.info("自动波次，预补货失败："+e.getMessage());
            }
            waveMapper.updateMapById(waveId,MapUtils.put("isAuto","1").getMap(),WaveDO.class);
            return waveId;
        }
        return null;
    }

    @Override
    public Page<PreReplenishDto> getPreReplenishPageList(PreReplenishVo vo) {
        PageUtils.startPage(vo.getPageNum(),vo.getPageSize());
        List<PreReplenishDto> list = omsDoBillDMapper.getPreReplenishPageList(vo);
        if(list == null || list.size() <= 0){
            return null;
        }
        for (PreReplenishDto preReplenishDto : list) {
            preReplenishDto.setLineId(UUIDUtils.generateWithoutSeperator());
            preReplenishDto.setAreaCategory("AGVL");
            preReplenishDto.setShouldQty(preReplenishDto.getShouldPcs().multiply(preReplenishDto.getConversionRate()));
        }
        return PageUtils.getPage(list);
    }

    /**
     * 根据波次ID，查询波次数据
     */
    @FuncImpl(name = "查询波次数据", func = WaveDistributeFunc.queryWaveMonitor.class, description = "根据波次ID，查询波次数据")
    public List<WaveBillDO> queryWaveMonitor(String waveId) {
        Map<String, Object> queryMap = MapUtils.put("waveId", waveId).getMap();
        return billMapper.findByMap(queryMap, WaveBillDO.class);
    }

    /**
     * 计算波次监控各指标
     */
    @FuncImpl(name = "计算波次监控各指标", func = WaveDistributeFunc.calculateMonitorQuantity.class, description = "根据从wms_wave_bill查询了来的数据计算监控指标")
    public void calculateMonitorQuantity(WaveStatisticVO waveStatisticVO,String waveId,List<WaveBillDO> allBills) {
        Map<String, Object> queryMap = MapUtils.put("waveId", waveId).getMap();
        waveStatisticVO.setTotalBills(allBills.size());
        long pendingBills = allBills.stream().filter(bill -> {
            boolean result = Boolean.FALSE;
            BILL_STATUS status = bill.getStatus();
            switch (status) {
                case POSITION_ERROR:
                case DISTRIBUTED:
                case WORKSTAGE_ERROR:
                case CANCELED:
                    result = Boolean.TRUE;
            }
            return result;
        }).count();
        waveStatisticVO.setPendingBills((int) pendingBills);
        List<PickTaskH> pickTasks = pickTaskHMapper.findByMap(queryMap, PickTaskH.class);
        long zj = pickTasks.stream().filter(p -> ObjectUtils.equals(0, p.getPickTaskType())).count();
        long lh = pickTasks.stream().filter(p -> ObjectUtils.equals(1, p.getPickTaskType())).count();
        long gn = pickTasks.stream().filter(p -> ObjectUtils.equals(2, p.getPickTaskType())).count();
        waveStatisticVO.setZjTasks((int) zj);
        waveStatisticVO.setLhTasks((int) lh);
        waveStatisticVO.setGnkfTasks((int) gn);
        int zdTasks=(int) pickTasks.stream().filter(p -> ObjectUtils.equals(3, p.getPickTaskType())).count();
        if(zdTasks>0){
            waveStatisticVO.setZjTasks(0);
            waveStatisticVO.setZdTasks(zdTasks+(int)zj);
        }
        List<WmsMoveTaskRef> byMap = wmsMoveTaskRefMapper.findByMap(queryMap, WmsMoveTaskRef.class);
        waveStatisticVO.setBhTasks(0);
        if(byMap!=null && byMap.size()!=0){
            waveStatisticVO.setBhTasks(byMap.stream().map(WmsMoveTaskRef::getRefLineBillId).collect(Collectors.toSet()).size());
        }
    }

    @Override
    public Page<WaveDO> getWavesByStatus(WAVE_STATUS status, Integer pageNum, Integer pageSize) {
        UserDTO userDTO = userTemplate.getUserDTO();
        String enterpriseId = userDTO.getEnterpriseId();
        Criteria crt = Criteria.forClass(WaveDO.class);
        Restriction r1 = Restrictions.eq("status", status.toString());
        crt.setRestriction(Restrictions.and(r1, Restrictions.eq("enterpriseId", enterpriseId)));
        crt.setOrder(Order.newInstance().desc("gmtCreate"));

        PageUtils.startPage(pageNum, pageSize);
        List<WaveDO> list = waveMapper.findByCriteria(crt);
        return PageUtils.getPage(list);
    }

    @Override
    public Page<WaveStorageVO> getWaveLocatorInfo(String warehouseId,
                                                  String waveId,
                                                  String billId,
                                                  String billDId,
                                                  String itemCode,
                                                  String locatorId,
                                                  String billCode,
                                                  Integer pageNum,
                                                  Integer pageSize) {

        PageUtils.startPage(pageNum, pageSize);
        List<WaveStorageVO> waveStorage = waveStorageMapper.findWaveStorage(warehouseId, waveId, billId, billDId, itemCode, locatorId, billCode);
        return PageUtils.getPage(waveStorage);
    }

    @Override
    public Page<WorkStageVO> getWaveWorkStage(String warehouseId, String waveId, String billGroupId, Integer pageNum, Integer pageSize) {
        List<WorkStageVO> workStages = workStageMapper.findWorkStage(warehouseId, waveId, billGroupId);
        PageUtils.startPage(pageNum, pageSize);
        return PageUtils.getPage(workStages);
    }

    @Flow(name = "波次取消", category = "出库/波次管理/波次取消", index = 4, description = "根据waveId删除wms_wave,wms_wave_bill,wms_wave_bill_d表状态不等于创建或者完成的数据")
    @Override
    public RestMessage<Boolean> deleteByWaveId(String waveId) {
        WaveDO waveDo = waveMapper.findById(waveId, WaveDO.class);
        RestMessage<Boolean> response;
        if (waveDo == null) {
            response = RestMessage.error("找不到对应的波次");
        } else {
            WAVE_STATUS status = waveDo.getStatus();
            if (status != WAVE_STATUS.CREATED || status != WAVE_STATUS.COMPLETED) {
                waveMapper.deleteById(waveId, WaveDO.class);
                billMapper.deleteByWaveId(waveId);
                billDMapper.deleteByWaveId(waveId);
                response = RestMessage.success("删除成功", true);
            } else {
                response = RestMessage.error("订单已经下发，且未完成，不能删除");
            }
        }
        return response;
    }

    @Override
    public Page<WavePendingBillVO> getPendingBills(String type,String warehouseId, String waveId, String billCode, String consignorId,
                                                   Integer pageNum, Integer pageSize) {
        PageHelper.startPage(pageNum, pageSize);
        List<WavePendingBillVO> bills = billMapper.getPendingBill(warehouseId, waveId, billCode, consignorId,null);
        Page<WavePendingBillVO> page = PageUtils.getPage(bills);
        return page;
    }

    @Override
    public RestMessage<List<WavePreCalResultVO>> preCalculate(List<String> wavesIds) {
        InvStockWave invStockWave = new InvStockWave();
        return null;
    }

    @Override
    @Transactional
    public void updateBillType(List<String> billids) {
        AssertUtils.isNotNull(billids,"billids不能为空");
        billids.forEach(x->{
            omsDoBillHMapper.updateMapById(x,MapUtils.put("isCancelled",1).put("billtype",BILL_TYPE.FIFTEEN.getCode()).getMap(),OmsDoBillHDO.class);
            try {
                amqpTemplate.convertAndSend("shipmentStagingQueue", x);
            }catch (Exception e){
                logger.error("发货暂存发送MQ到OMS出错,billid:"+x);
            }
        });
    }

    private String getPickRule(String billType, String shipperid, String cargoOwnerId) {
        if (billType.equals("销售")) {
            //            1.查客户pickrule
            if (cargo_owner_pick_rule.equals("")) {
                //                 2.查货主pickrule*/
                cargo_owner_pick_rule = "aaa";
            }
        } else {
            //           查货主pickrule
            if (cargo_owner_pick_rule.equals("")) {
                //2.查货主pickrule*/
                cargo_owner_pick_rule = "aaa";
            }
        }
        return null;
    }

    /**
     * 波次生成
     *
     * @param wavesCreateVO
     */
    @Flow(name = "波次创建", category = "出库/波次管理/波次创建", index = 1, description = "波次创建")
    @Override
    @Transactional
    public String[] wavesCreate(WavesCreateDTO wavesCreateVO) {
        long startTime = System.currentTimeMillis();
        // 得到userVO
        UserDTO userDTO = userTemplate.getUserDTO();
        if (null == userDTO) {
            userDTO = new UserDTO();
            userDTO.setEnterpriseId(wavesCreateVO.getEnterpriseId());
            userDTO.setId(String.valueOf(SnowFlakeUtils.getId()));
            userDTO.setUsername("定时任务");
        }
        // 校验入参
        validInputParam.validInputParam(wavesCreateVO);
        // 查询得到出库头
        List<OmsDoBillHDO> billHList = queryOmsDoBillHList.queryOmsDoBillHList(wavesCreateVO);
        // 波次创建订单校验
        validBillPickQty.validBillPickQty(billHList);
        // 查询得到出库行
        List<OmsDoBillDDO> billDList = queryOmsDoBillDList.queryOmsDoBillDList(billHList);
        // 校验捡选方式
        validModel.validModel(wavesCreateVO, userDTO, billDList);
        // 校验指定库位
        checkPreLocation(wavesCreateVO, billDList);
        // 保存波次数据
        WaveDO waveDo = saveWaveDO.saveWaveDO(wavesCreateVO, userDTO, billHList);
        // 出库单分组,保存,并返回waveId
        // 订单状态数据推送OMS
        /*billHList.stream().forEach(x->{
            orderOutboundStatusBackService.sendOutboundOrderStatusOms(x.getBillid(), "", "", "", "", "20", BigDecimal.ZERO);
        });*/
        long endTime = System.currentTimeMillis();
        logger.info("波次创建耗时:{}",endTime-startTime);
        return saveWaveGroup.saveWaveGroup(wavesCreateVO, waveDo, userDTO, billHList, billDList);
    }

    /**
     * 波次创建入参校验
     */
    @FuncImpl(name = "波次创建入参校验", func = WaveCreateFunc.validInputParam.class, description = "波次创建入参校验,校验仓库ID和单据ID")
    private void validInputParam(WavesCreateDTO wavesCreateVO) {
        String warehouseId = wavesCreateVO.getWarehouseId();
        List<String> billIdList = wavesCreateVO.getBillIdList();
        if (StringUtils.isBlank(warehouseId)) {
            throw new ParameterException("仓库ID不能为空！");
        }
        if (billIdList == null || billIdList.size() == 0) {
            throw new ParameterException("订单ID不能为空！");
        }
    }

    /**
     * 根据入库单ID查询出库单列表
     */
    @FuncImpl(name = "查询出库单头列表", func = WaveCreateFunc.queryOmsDoBillHList.class, description = "根据billid查询oms_do_bill_h表,然后在根据billid查询wms_wave_bill表是否已在下发波次中")
    private List<OmsDoBillHDO> queryOmsDoBillHList(WavesCreateDTO wavesCreateVO) {
        List<String> billIdList = wavesCreateVO.getBillIdList();
        // 1.根据出库单id集合查询出库单列表
        String[] billIds = billIdList.toArray(new String[billIdList.size()]);
        Criteria cr = Criteria.forClass(OmsDoBillHDO.class);

        Restriction billId = Restrictions.in("billid", billIds);
        Restriction isCancelled = Restrictions.ne("isCancelled", 1);
        Restriction cancelledIsNull = Restrictions.isnull("isCancelled");
        Restriction or = Restrictions.or(isCancelled, cancelledIsNull);
        Restriction and = Restrictions.and(billId, or);
        cr.setRestriction(and);

        List<OmsDoBillHDO> billHList = omsDoBillHMapper.findByCriteria(cr);
        return billHList;
    }

    /**
     * 波次创建订单已波次下发校验
     */
    @FuncImpl(name = "波次创建订单已波次下发校验", func = WaveCreateFunc.validBillPickQty.class, description = "波次创建订单已波次下发校验")
    private void validBillPickQty(List<OmsDoBillHDO> billHList) {
        if (billHList == null || billHList.size() == 0) {
            return;
        }

        List<String> billIds = billHList.stream().map(OmsDoBillHDO::getBillid).distinct().collect(Collectors.toList());
        List<String> listBills = billMapper.getWaveByBillid(billIds);
        if (listBills != null && listBills.size() > 0) {
            StringBuffer str = new StringBuffer();
            for (String billId : listBills) {
                str.append(billId).append(",");
            }
            logger.error("订单号：" + str + "，已组波次，请刷新页面！");
            throw new BizException("订单号：" + str + "，已组波次，请刷新页面！");
        }

        List<String> listBill = omsDoBillHMapper.getBillPickH(billIds);
        if (listBill != null && listBill.size() > 0) {
            StringBuffer str = new StringBuffer();
            for (String billId : listBill) {
                str.append(billId).append(",");
            }
            logger.error("订单：" + str + "，已经下发过，存在拣货任务主表数据，请检查");
            throw new BizException("订单：" + str + "已经下发过，存在拣货任务主表数据，请检查");
        }
    }

    /**
     * 根据入库单头ID查询出库单行列表
     */
    @FuncImpl(name = "查询出库单行列表", func = WaveCreateFunc.queryOmsDoBillDList.class, description = "根据billid查询oms_do_bill_d数据")
    private List<OmsDoBillDDO> queryOmsDoBillDList(List<OmsDoBillHDO> billHList) {
        List<OmsDoBillDDO> billDList = null;
        if (billHList.size() > 0) {
            String[] hIds = new String[billHList.size()];
            for (int i = 0; i < billHList.size(); i++) {
                hIds[i] = billHList.get(i).getBillid();
            }
            Criteria crt = Criteria.forClass(OmsDoBillDDO.class);
            RestrictionBuilder builder = crt.restrictionBuilder();
            builder.in("billid", hIds);
            crt.buildRestriction();
            billDList = omsDoBillDMapper.findByCriteria(crt);

            List<String> itemIds = billDList.stream().map(OmsDoBillDDO::getItemid).distinct().collect(Collectors.toList());
            List<WaveItemInfoVO> itemVOs = itemClient.getWaveItemInfo(itemIds).getData();
            for (int i = 0; i < billDList.size(); i++) {
                OmsDoBillDDO omsDoBillDDO = billDList.get(i);
                String itemId = omsDoBillDDO.getItemid();
                OmsDoBillHDO omsDoBillHDO=billHList.stream().filter(t->t.getBillid().equals(omsDoBillDDO.getBillid())).findFirst().orElse(null);
                if (omsDoBillHDO==null) continue;
                String billType = omsDoBillHDO.getBilltype();
                if ("2".equals(billType) || "16".equals(billType) || "17".equals(billType)) {
                    continue;
                }
                WaveItemInfoVO waveItem=itemVOs.stream().filter(t->t.getItemId().equals(itemId)).findFirst().orElse(null);
                if (waveItem!=null && waveItem.getItemIsUnpack()!=null && waveItem.getItemIsUnpack()==0) {
                    if (omsDoBillDDO.getItemOdd()!=null && omsDoBillDDO.getItemOdd().compareTo(BigDecimal.ZERO)>0) {
                        logger.error("商品"+omsDoBillDDO.getItemname()+"不允许拆中包装");
                        throw new BizException("商品"+omsDoBillDDO.getItemname()+"不允许拆中包装");
                    }
                }
            }
        } else {
            throw new BizException("请检查是否已经在其它波次中");
        }
        return billDList;
    }

    /**
     * 波次创建校验捡选模式
     */
    @FuncImpl(name = "波次创建校验捡选模式", func = WaveCreateFunc.validModel.class, description = "校验捡选模式,是否整散合一或者整散分离")
    private void validModel(WavesCreateDTO wavesCreateVO,UserDTO userDTO,List<OmsDoBillDDO> billDList) {
        RestMessage<BusinessControl> rest = strategyResource.getControlByCode("PICK_TASK_ASK_WAY", wavesCreateVO.getWarehouseId(), null, userDTO.getEnterpriseId(), null);
        if (rest.isSuccess() && rest.getData() != null && "1".equals(rest.getData().getRegValue())) {
            List<OmsDoBillDDO> tempBillDList = new ArrayList<>();

            String[] dIds = new String[billDList.size()];
            for (int i = 0; i < billDList.size(); i++) {
                dIds[i] = billDList.get(i).getLineid();
            }
            Criteria crt = Criteria.forClass(SaleRedRecord.class);
            RestrictionBuilder builder = crt.restrictionBuilder();
            builder.in("srcLineId", dIds);
            crt.buildRestriction();
            List<SaleRedRecord> allRedRecords = saleRedRecordMapper.findByCriteria(crt);

            for (int i = 0; i < billDList.size(); i++) {
                OmsDoBillDDO OmsDoBillDDO = billDList.get(i);
                List<SaleRedRecord> redRecords = allRedRecords.stream().filter(r->r.getSrcLineId().equals(OmsDoBillDDO.getLineid())).collect(Collectors.toList());
                if (CollUtil.isNotEmpty(redRecords)) {
                    BigDecimal lineRedQty = redRecords.stream().map(SaleRedRecord::getQty).reduce(BigDecimal.ZERO, BigDecimal::add);
                    if (OmsDoBillDDO.getItemQty().compareTo(lineRedQty)<=0) {
                        continue;
                    }
                }
                tempBillDList.add(OmsDoBillDDO);
            }
            List<String> itemIds = tempBillDList.stream().map(OmsDoBillDDO::getItemid).distinct().collect(Collectors.toList());
            AssertUtils.isNotEmpty(itemIds,"整单拣选模式，商品必须为全部整散分离或全部整散合一");
            RestMessage<List<WaveItemInfoVO>> waveItemInfos = itemClient.getWaveItemInfo(itemIds);
            List<WaveItemInfoVO> itemVOs = waveItemInfos.getData();
            AssertUtils.isNotEmpty(itemVOs,"整单拣选模式，商品必须为全部整散分离或全部整散合一");
            int caseSum = 0;
            for (int i = 0; i < itemVOs.size(); i++) {
                if (itemVOs.get(i).getIsCaseOrPiece()==null) {
                    throw new BizException("整单拣选模式，商品必须为全部整散分离或全部整散合一");
                }
                caseSum = caseSum + itemVOs.get(i).getIsCaseOrPiece();
            }
            if (caseSum != 0 && itemIds.size() != caseSum) {
                throw new BizException("整单拣选模式，商品必须为全部整散分离或全部整散合一");
            }
        }
    }

    /**
     * 波次创建保存
     */
    @FuncImpl(name = "波次创建保存", func = WaveCreateFunc.saveWaveDO.class, description = "保存信息到波次主表wms_wave,同时根据波次编号更新数据")
    private WaveDO saveWaveDO(WavesCreateDTO wavesCreateVO,UserDTO userDTO,List<OmsDoBillHDO> billHList) {
        WaveDO waveDo = new WaveDO();
        waveDo.setStatus(WAVE_STATUS.CREATED);
        waveDo.setWarehouseId(wavesCreateVO.getWarehouseId());
        // 货主转换单
        String billType = billHList.get(0).getBilltype();

        Set<String> billTypeSet = billHList.stream().map(OmsDoBillHDO::getBilltype).collect(Collectors.toSet());
        if (billTypeSet.size()>1) {
            throw new PrologException("不同类型订单,需要分开创建波次!");
        }
        RestMessage<List<BaseCommonDict>> billTypeList = commonClient.getDictBasicListByCodes("special_bill_type", null);
        List<String> billTypes = billTypeList.getData().stream().map(BaseCommonDict::getDictCode).collect(Collectors.toList());

        if (billTypes.contains(billType)) {
            // 特殊出库订单
            waveDo.setType("8");
        } else {
            // 正常出库订单
            waveDo.setType("1");
        }
        ModelUtils.setBaseInfo(waveDo, ModelUtils.OpType.INSERT, userDTO);
        waveMapper.save(waveDo);
        String wavesId = waveDo.getId();
        waveDo.setCode(wavesId);
        waveMapper.updateFields(waveDo,FieldSelector.newInstance().include("code"));
        return waveDo;
    }

    /**
     * 波次创建-波次分组
     */
    @FuncImpl(name = "波次创建波次分组", func = WaveCreateFunc.saveWaveGroup.class, description = "根据波次分组策略，将出库单进行分组并赋值，并将分组完成数据保存至wms_wave_bill_group表")
    private String[] saveWaveGroup(WavesCreateDTO wavesCreateVO, WaveDO waveDo, UserDTO userDTO,List<OmsDoBillHDO> billHList, List<OmsDoBillDDO> billDList) {
        List<WaveBillGroupDO> waveBillGroupDOList = new ArrayList<>();
        List<WaveBillDO> waveBillList = new ArrayList<>();
        List<WaveBillDDO> waveBillDs = new ArrayList<>();
        List<WaveBillDDO> waveBillDs4near = new ArrayList<>();
        String wavesId = waveDo.getId();
        // 2.根据波次分组策略，将出库单进行分组
        Long timeStart = System.currentTimeMillis();
        Set<String> rshSet = new HashSet<>();
        Set<String> rsdSet = new HashSet<>();
        Map<List<String>, Map<String, Object>> wavesGroups = AlgorithmUtils.waveGroup(billHList, billDList, "branchid");
        for (Map.Entry<List<String>, Map<String, Object>> entry : wavesGroups.entrySet()) {
            Map<String, Object> map = entry.getValue();
            List<OmsDoBillHDO> hList = (List<OmsDoBillHDO>) map.get("lineBeanList");
            List<OmsDoBillDDO> dList = (List<OmsDoBillDDO>) map.get("itemBeanList");
            Set<String> shipperIds = hList.stream().filter(x->x.getShipperid()!=null).map(OmsDoBillHDO::getShipperid).collect(Collectors.toSet());
            rshSet.addAll(shipperIds);
            Set<String> itemIds = dList.stream().map(OmsDoBillDDO::getItemid).collect(Collectors.toSet());
            rsdSet.addAll(itemIds);
        }
        List<String> customerIdList = new ArrayList<>();
        customerIdList.addAll(rshSet);
        // 一次性查询得到全部的数据，减少循环访问的次数
        RestMessage<List<BaseCustomerSitesAll>> restMessage = chainResource.findAllHealTh(customerIdList);
        if(!restMessage.isSuccess()){
            throw new PrologException("批量查询客户失败:"+restMessage.getMessage());
        }
        Long timeEnd = System.currentTimeMillis();
        logger.info("客户入参为：{}", rshSet);
        logger.info("初始化查询客户及商品时间为：{}", timeEnd-timeStart);
        // 初始化itemUtils
        ItemUtils itemUtils = new ItemUtils(rsdSet);
        Long timeEnd1 = System.currentTimeMillis();
        logger.info("商品入参为：{}", rsdSet);
        logger.info("初始化查询客户及商品时间为：{}", timeEnd1-timeEnd);
        for (Map.Entry<List<String>, Map<String, Object>> entry : wavesGroups.entrySet()) {
            Map<String, Object> map = entry.getValue();
            List<OmsDoBillHDO> hList = (List<OmsDoBillHDO>) map.get("lineBeanList");
            List<OmsDoBillDDO> dList = (List<OmsDoBillDDO>) map.get("itemBeanList");
            if (dList == null) {
                throw new PrologException("该波次没有商品可以下发");
            }
            Set<String> shipLineIds = new HashSet<>();
            for (OmsDoBillHDO omsDoBillHDO : hList) {
                // 查询订单排序规则
                BillSortRuleVO billSortRuleVO = new BillSortRuleVO();
                billSortRuleVO.setBillType(omsDoBillHDO.getBilltype());
                billSortRuleVO.setDistributionMethod(omsDoBillHDO.getArrivalMode());
                RestMessage<List<BillSortRuleVO>> restBillSortRule = strategyResource.queryBillSortRuleList(billSortRuleVO);

                BigDecimal billTotalVolume = BigDecimal.ZERO;
                BigDecimal billTotalWeight = BigDecimal.ZERO;
                BigDecimal totalCsQty = BigDecimal.ZERO;
                BigDecimal billTotalQty = BigDecimal.ZERO;
                String consignorId = omsDoBillHDO.getConsignorid();
                shipLineIds.add(omsDoBillHDO.getShipLineId());
                WaveBillDO waveBill = new WaveBillDO();
                //波次分组订单id
                String waveBillId = String.valueOf(SnowFlakeUtils.getId());
                waveBill.setWaveId(wavesId);
                waveBill.setId(waveBillId);
                waveBill.setWarehouseId(wavesCreateVO.getWarehouseId());
                waveBill.setStatus(BILL_STATUS.CREATED);
                waveBill.setSrcId(omsDoBillHDO.getBillid());
                waveBill.setSrcCode(omsDoBillHDO.getBillno());
                waveBill.setCode(omsDoBillHDO.getBillno());
                waveBill.setType(omsDoBillHDO.getBilltype());
                waveBill.setConsignorId(consignorId);
                waveBill.setShipperId(omsDoBillHDO.getShipperid());
                waveBill.setShipperName(omsDoBillHDO.getShippername());
                waveBill.setShipperType(omsDoBillHDO.getShipperType());
                waveBill.setContactName(omsDoBillHDO.getCustPerson());
                waveBill.setContactTel(omsDoBillHDO.getCustPersonTel());
                waveBill.setReceiveWay(omsDoBillHDO.getArrivalMode());
                waveBill.setSalesPersonName(omsDoBillHDO.getSalespersonName());
                waveBill.setConsignorName(omsDoBillHDO.getConsignorname());
                waveBill.setPaymentDayType(omsDoBillHDO.getPaymentDayType());
                waveBill.setCreateBy(omsDoBillHDO.getCreateby());
                if(omsDoBillHDO.getPriorityLevel().matches("^[0-9]*$")){
                    waveBill.setPriority(Integer.parseInt(omsDoBillHDO.getPriorityLevel()));
                }else{
                    waveBill.setPriority(0);
                }

                // 匹配订单排序规则,根据单据类型和配送方式
                if (restBillSortRule.isSuccess()
                        && null != restBillSortRule.getData()
                        && restBillSortRule.getData().size()>0
                        && null != restBillSortRule.getData().get(0)
                        && null != restBillSortRule.getData().get(0).getPriority()) {
                    waveBill.setPriority(Integer.parseInt(restBillSortRule.getData().get(0).getPriority()));
                } else {
                    waveBill.setPriority(0);
                }
                if(omsDoBillHDO.getIsEmergency()!=null && omsDoBillHDO.getIsEmergency()==1) {
                    waveBill.setPriority(5);
                }
                if (!StringUtils.isEmpty(omsDoBillHDO.getShipLineId())) {
                    waveBill.setShipLineId(omsDoBillHDO.getShipLineId());
                } else if(restMessage.getData() != null) {
                    BaseCustomerSitesAll baseCustomerSitesAll = restMessage.getData().stream().filter(t->omsDoBillHDO.getShipperid().equals(t.getCustomerId())).findFirst().orElse(null);
                    if (baseCustomerSitesAll != null) {
                        waveBill.setShipLineId(baseCustomerSitesAll.getSegment1());
                    }
                }
                waveBill.setAttainDate(omsDoBillHDO.getAttainDate());
                waveBill.setReceiveAddress(omsDoBillHDO.getReceiveAddress());
                waveBill.setCarrierName(omsDoBillHDO.getCarrierName());
                waveBill.setBillGroupId(omsDoBillHDO.getBillid());
                waveBill.setRemark(omsDoBillHDO.getRemark());
                waveBill.setIsMerged(0);
                // 订单优先级的过滤条件字段需要值 - by Huanglh 2022-02-22
                waveBill.setSaleBranchName(omsDoBillHDO.getSaleBranchname());
                ModelUtils.setBaseInfo(waveBill, ModelUtils.OpType.INSERT, userDTO);
                waveBillList.add(waveBill);
                // 求挑批次规则
                String pick_rule = getPickRule(omsDoBillHDO.getBilltype(), "", consignorId);
                // Set<String> itemIds = dList.stream().map(OmsDoBillDDO::getItemid).collect(Collectors.toSet());
                List<SaleRedRecord> redRecords = getOmsRedQty(omsDoBillHDO);
                for (Iterator<OmsDoBillDDO> it = dList.iterator(); it.hasNext(); ) {
                    String omsBillId = omsDoBillHDO.getBillid();
                    OmsDoBillDDO item = it.next();
                    if (item.getBillid().equals(omsDoBillHDO.getBillid())) {
                        WaveBillDDO billDetail = new WaveBillDDO();
                        String lineid = item.getLineid();
                        String itemId = item.getItemid();
                        ItemMstVO2 itemMstVO2 = itemUtils.getByItemId(itemId);
                        BigDecimal caseConversionRate = itemMstVO2.getCaseConversionRate();
                        billDetail.setCaseConversionRate(caseConversionRate);
                        billDetail.setIsCaseOrPiece(itemMstVO2.getIsCaseOrPiece());
                        billDetail.setItemSpecs(itemMstVO2.getSpecification());
                        billDetail.setCaseUom(itemMstVO2.getCaseUom());
                        billDetail.setManufacturer(itemMstVO2.getManufacturerName());
                        billDetail.setItemHealthCategoryName(itemMstVO2.getItemHealthCategoryName());

                        BigDecimal omsRedQty = BigDecimal.ZERO;
                        if (CollUtil.isNotEmpty(redRecords)) {
                            List<SaleRedRecord> records = redRecords.stream().filter(r->r.getSrcId().equals(omsDoBillHDO.getBillid())
                                    && r.getSrcLineId().equals(lineid)
                                    && r.getItemId().equals(itemId)
                            ).collect(Collectors.toList());
                            if (CollUtil.isNotEmpty(records)) {
                                omsRedQty = records.stream().map(SaleRedRecord::getQty).filter(x -> x != null).reduce(BigDecimal.ZERO, BigDecimal::add);
                            }
                        }
                        // BigDecimal omsRedQty = redService.getOmsRedQty(omsBillId, lineid, itemId);omsRedQty = Optional.ofNullable(omsRedQty).orElse(BigDecimal.ZERO);
                        BigDecimal pieceQty = item.getItemQty().subtract(omsRedQty);
                        billDetail.setPieceQty(pieceQty); // 商品数量(最小单位数量)
                        // 波次分组商品明细id
                        billDetail.setSrcLineId(lineid);
                        billDetail.setBillId(waveBillId);
                        long billDId = SnowFlakeUtils.getId();
                        billDetail.setId(String.valueOf(billDId));
                        billDetail.setWaveId(wavesId);
                        billDetail.setPickRule(pick_rule);
                        billDetail.setItemId(item.getItemid());
                        billDetail.setItemName(item.getItemname());
                        billDetail.setItemCode(item.getItemcode());
                        billDetail.setUomQty(item.getPackTypeNum()); // 数量
                        billDetail.setOddPieceQty(item.getItemOdd()); // 零散数
                        billDetail.setConversionRate(item.getPackTypeNum()); // 单位因子（包装数量）
                        billDetail.setBatchNum(item.getBatchNo());
                        billDetail.setManufacturerLotNum(item.getLotNo());
                        BigDecimal caseVolume = item.getItemVolume();
                        if (caseVolume == null) {
                            caseVolume = DEFAULT_VOLUME;
                        }
                        BigDecimal volume = caseVolume.divide(caseConversionRate, 4, RoundingMode.UP);
                        billDetail.setVolume(volume);
                        BigDecimal ItemTotalVolume = volume.multiply(pieceQty);
                        billDetail.setTotalVolume(ItemTotalVolume);
                        billTotalVolume = billTotalVolume.add(ItemTotalVolume);

                        BigDecimal caseWeight = item.getItemWeight();
                        if (caseWeight == null) {
                            caseWeight = DEFAULT_WEIGHT;
                        }
                        BigDecimal weight = caseVolume.divide(caseConversionRate, 4, RoundingMode.UP);
                        billDetail.setWeight(weight);
                        BigDecimal itemTotalWeight = caseWeight.multiply(pieceQty);
                        billDetail.setTotalWeight(itemTotalWeight);
                        String lotNo = item.getLotNo();
                        billDetail.setManufacturerLotNum(lotNo);
                        billDetail.setStockStatus(item.getStockStatus());
                        if (!StringUtils.isBlank(itemMstVO2.getItemHealthCategory())) {
                            billDetail.setItemCategoryCode(itemMstVO2.getItemHealthCategory());
                        }
                        if (null != itemMstVO2.getItemHealthIsDouble()) {
                            billDetail.setItemHealthIsDouble(itemMstVO2.getItemHealthIsDouble().toString());
                        }
                        int total_pcs = pieceQty.intValue() / caseConversionRate.intValue();
                        billDetail.setTotalPcs(total_pcs);
                        int oddQty = pieceQty.intValue() % caseConversionRate.intValue();
                        billDetail.setOddPieceQty(BigDecimal.valueOf(oddQty));
                        billDetail.setIsWholeQty(item.getAttrNum1() == null ? 2 : item.getAttrNum1().intValue());// 1 整件行 2 零货行

                        ModelUtils.setBaseInfo(billDetail, ModelUtils.OpType.INSERT, userDTO);
                        if (pieceQty.compareTo(BigDecimal.ZERO) > 0) {
                            waveBillDs.add(billDetail);
                            String[] arr= new String[]{"1","13","14","16","17"};//销售出库、有实物出库、无实物出库
                            if(Arrays.asList(arr).contains(omsDoBillHDO.getBilltype())){
                                waveBillDs4near.add(billDetail);
                            }
                        } else {
                            logger.info("該商品數量<0" + item.getItemname());
                        }
                        if (null != item.getItemPcs()) {
                            totalCsQty = totalCsQty.add(item.getItemPcs());
                        }
                        billTotalQty = billTotalQty.add(pieceQty);
                        billTotalWeight = billTotalWeight.add(itemTotalWeight);
                        // 批号
                        it.remove();
                    }
                }
                waveBill.setTotalWeight(billTotalWeight);
                waveBill.setTotalPcs(totalCsQty);
                waveBill.setTotalVolume(billTotalVolume);
                waveBill.setTotalQty(billTotalQty);
            }
            WaveBillGroupDO waveBillGroupDO = new WaveBillGroupDO();
            waveBillGroupDO.setStatus(Constants.Status.STATUS_NEW);
            waveBillGroupDO.setWaveId(wavesId);
            ModelUtils.setBaseInfo(waveBillGroupDO, ModelUtils.OpType.INSERT, userDTO);
            waveBillGroupDOList.add(waveBillGroupDO);
        }
        StatusUtil.updateWaveStartStatus(wavesId, WAVE_STATUS.CREATED, userDTO);
        if (waveBillDs.size() > 0) {
            int batchSize = 50;
            List<WaveBillDDO> tempWaveBillDs = new ArrayList<>();
            if (waveBillDs.size()>batchSize) {
                int totalPage = waveBillDs.size()%batchSize==0?waveBillDs.size()/batchSize:waveBillDs.size()/batchSize+1;
                for (int i=0;i<totalPage;i++) {
                    tempWaveBillDs = waveBillDs.subList(i*batchSize,((i+1)*batchSize>waveBillDs.size()?waveBillDs.size():(i+1)*batchSize));
                    billDMapper.saveBatch(tempWaveBillDs);
                }
            } else {
                billDMapper.saveBatch(waveBillDs);
            }
            if (waveBillList.size() > 0) {
                List<WaveBillDO> tempWaveBillList = new ArrayList<>();
                if (waveBillList.size()>batchSize) {
                    int totalPage = waveBillList.size()%batchSize==0?waveBillList.size()/batchSize:waveBillList.size()/batchSize+1;
                    for (int i=0;i<totalPage;i++) {
                        tempWaveBillList = waveBillList.subList(i*batchSize,((i+1)*batchSize>waveBillList.size()?waveBillList.size():(i+1)*batchSize));
                        billMapper.saveBatch(tempWaveBillList);
                    }
                } else {
                    billMapper.saveBatch(waveBillList);
                }
            }
        }
        if (waveBillGroupDOList.size() > 0) {
            int batchSize = 50;
            List<WaveBillGroupDO> tempWaveBillGroupDOList = new ArrayList<>();
            if (waveBillGroupDOList.size()>batchSize) {
                int totalPage = waveBillGroupDOList.size()%batchSize==0?waveBillGroupDOList.size()/batchSize:waveBillGroupDOList.size()/batchSize+1;
                for (int i=0;i<totalPage;i++) {
                    tempWaveBillGroupDOList = waveBillGroupDOList.subList(i*batchSize,((i+1)*batchSize>waveBillGroupDOList.size()?waveBillGroupDOList.size():(i+1)*batchSize));
                    groupMapper.saveBatch(tempWaveBillGroupDOList);
                }
            } else {
                groupMapper.saveBatch(waveBillGroupDOList);
            }
        }
        for (OmsDoBillHDO omsDoBillHDO : billHList) {
            omsDoBillHMapper.updateMapById(omsDoBillHDO.getBillid(), MapUtils.put("status", "20").getMap(), OmsDoBillHDO.class);
            Criteria crt = Criteria.forClass(OmsDoBillDDO.class);
            Restriction r1 = Restrictions.eq("billid", omsDoBillHDO.getBillid());
            crt.setRestriction(Restrictions.and(r1));
            omsDoBillDMapper.updateMapByCriteria(MapUtils.put("status", "20").getMap(),crt);
        }
        // return new String[]{wavesId,waveDistribute.alertNear(billHList.get(0).getBranchid(),billHList.get(0).getConsignorid(), waveBillDs4near)};
        return new String[]{wavesId}; // 不校验近效期
    }

    public List<SaleRedRecord> getOmsRedQty(OmsDoBillHDO omsDoBillHDO) {
        String billId = omsDoBillHDO.getBillid();
        Criteria criteria = Criteria.forClass(SaleRedRecord.class);
        Restriction r11 = Restrictions.eq("srcId", billId);
        criteria.setRestriction(r11);
        List<SaleRedRecord> saleRedRecordList = saleRedRecordMapper.findByCriteria(criteria);
        return saleRedRecordList;
    }

    /**
     * 查询订单列表
     *
     * @param billQueryDTO
     * @return
     */
    @Override
    public Page<OmsDoBillHDO> getBillHList(BillQueryDTO billQueryDTO) {
        UserDTO userDTO = userTemplate.getUserDTO();
        String enterpriseId = userDTO.getEnterpriseId();
/*        if(!StringUtils.isEmpty(billQueryDTO.getItemCondition())){
            //编码，名称，助记码查询商品信息
            List<String> itemIds = queryItemInfoByCondition(billQueryDTO.getConsignorid(), billQueryDTO.getItemCondition());
            if(itemIds.size()>0){
                billQueryDTO.setItemIds(itemIds);
            }else{
                return new Page<>();
            }
        }
        if(!StringUtils.isEmpty(billQueryDTO.getShipperCondition())){
            //编码，名称，助记码查询单位信息
            List<String> shipperIds = queryShipperInfoByCondition(billQueryDTO.getConsignorid(),billQueryDTO.getShipperCondition());
            if(shipperIds.size()>0){
                billQueryDTO.setShipperIds(shipperIds);
            }else{
                return new Page<>();
            }
        }*/
        PageHelper.startPage(billQueryDTO.getPageNum(), billQueryDTO.getPageSize());
        List<OmsDoBillHDO> omsBill = omsDoBillHMapper.findOmsBill(billQueryDTO, enterpriseId);
        // 处理展示字段
        processList(omsBill);
        return PageUtils.getPage(omsBill);
    }

    public List<String> queryShipperInfoByCondition(String cargoOwnerId,String shipperCondition){
        List<String> shipperIds = new ArrayList<>();
        CustomerAndSupplierQueryVO queryVO = new CustomerAndSupplierQueryVO();
        queryVO.setCargoOwnerId(cargoOwnerId);
        queryVO.setCondition(shipperCondition);
        queryVO.setPageNum(1);
        queryVO.setPageSize(999);
        RestMessage<Page<CustomerAndSupplierQueryVO>> queryVORestMessage = chainResource.customerAndSupplierPage(queryVO);
        if(queryVORestMessage.isSuccess()&&queryVORestMessage.getData()!=null){
            List<CustomerAndSupplierQueryVO> list = queryVORestMessage.getData().getList();
            if(list.size()>0){
                shipperIds = list.stream().map(x -> x.getCustomerAndSupplierId()).distinct().collect(Collectors.toList());
            }
        }
        return shipperIds;
    }

    public List<String> queryItemInfoByCondition(String cargoOwnerId,String itemCondition){
        ItemMstQueryVO queryVO = new ItemMstQueryVO();
        queryVO.setCargoOwnerId(cargoOwnerId);
        queryVO.setCondition(itemCondition);
        queryVO.setPageNum(1);
        queryVO.setPageSize(999);
        List<String> itemIds = new ArrayList<>();
        RestMessage<Page<ItemMstVO2>> pageRestMessage = itemClient.pagebyRelated(queryVO);
        if(pageRestMessage.isSuccess()&&pageRestMessage.getData()!=null){
            List<ItemMstVO2> list = pageRestMessage.getData().getList();
            if(list.size()>0){
                itemIds = list.stream().map(x -> x.getItemId()).distinct().collect(Collectors.toList());
            }
        }
        return itemIds;
    }

    /**
     * 处理展示值
     * @param list list
     */
    private void processList(List<OmsDoBillHDO> list) {
        for (OmsDoBillHDO omsDoBillHDO:list) {
            if (null != omsDoBillHDO.getBilltype()) {
                if (omsDoBillHDO.getBilltype().equals("1")) {
                    omsDoBillHDO.setBillTypeName("销售出库");
                } else if (omsDoBillHDO.getBilltype().equals("2")) {
                    omsDoBillHDO.setBillTypeName("购进退出单");
                } else if (omsDoBillHDO.getBilltype().equals("3")) {
                    omsDoBillHDO.setBillTypeName("返工出库");
                } else if (omsDoBillHDO.getBilltype().equals("4")) {
                    omsDoBillHDO.setBillTypeName("购进退出");
                } else if (omsDoBillHDO.getBilltype().equals("5")) {
                    omsDoBillHDO.setBillTypeName("质检出库");
                }
            }
            if (null != omsDoBillHDO.getStatus()) {
                if (omsDoBillHDO.getStatus().equals("10")) {
                    omsDoBillHDO.setStatusName("初始");
                } else if (omsDoBillHDO.getStatus().equals("15")) {
                    omsDoBillHDO.setStatusName("计划波次");
                } else if (omsDoBillHDO.getStatus().equals("20")) {
                    omsDoBillHDO.setStatusName("已下发");
                } else if (omsDoBillHDO.getStatus().equals("30")) {
                    omsDoBillHDO.setStatusName("已确认");
                } else if (omsDoBillHDO.getStatus().equals("40")) {
                    omsDoBillHDO.setStatusName("波次下发完成");
                } else if (omsDoBillHDO.getStatus().equals("50")) {
                    omsDoBillHDO.setStatusName("正在作业");
                } else if (omsDoBillHDO.getStatus().equals("60")) {
                    omsDoBillHDO.setStatusName("拣货完成");
                } else if (omsDoBillHDO.getStatus().equals("80")) {
                    omsDoBillHDO.setStatusName("集货完成");
                } else if (omsDoBillHDO.getStatus().equals("100")) {
                    omsDoBillHDO.setStatusName("配送完成/复核完成");
                } else if (omsDoBillHDO.getStatus().equals("110")) {
                    omsDoBillHDO.setStatusName("发运确认");
                }
            }

            if (null != omsDoBillHDO.getDepartment()) {
                if (omsDoBillHDO.getDepartment().equals("01")) {
                    omsDoBillHDO.setDepartmentName("部门1");
                }
            }

            if (null != omsDoBillHDO.getArrivalMode()) {
                if (omsDoBillHDO.getArrivalMode().equals("1")) {
                    omsDoBillHDO.setArrivalModeName("集团连锁");
                } else if (omsDoBillHDO.getArrivalMode().equals("2")) {
                    omsDoBillHDO.setArrivalModeName("市外配送");
                } else if (omsDoBillHDO.getArrivalMode().equals("3")) {
                    omsDoBillHDO.setArrivalModeName("市内配送");
                } else if (omsDoBillHDO.getArrivalMode().equals("4")) {
                    omsDoBillHDO.setArrivalModeName("托运");
                } else if (omsDoBillHDO.getArrivalMode().equals("5")) {
                    omsDoBillHDO.setArrivalModeName("自提");
                }
            }

            if (null != omsDoBillHDO.getShipTerm()) {
                if (omsDoBillHDO.getShipTerm().equals("1")) {
                    omsDoBillHDO.setShipTermName("常温");
                } else if (omsDoBillHDO.getShipTerm().equals("2")) {
                    omsDoBillHDO.setShipTermName("冷藏");
                } else if (omsDoBillHDO.getShipTerm().equals("3")) {
                    omsDoBillHDO.setShipTermName("冷冻");
                }
            }

            if (null != omsDoBillHDO.getPriorityLevel()) {
                if (omsDoBillHDO.getPriorityLevel().equals("1")) {
                    omsDoBillHDO.setPriorityLevelName("紧急");
                } else if (omsDoBillHDO.getPriorityLevel().equals("2")) {
                    omsDoBillHDO.setPriorityLevelName("普通");
                }
            }

        }
    }

    /**
     * 查询订单商品明细列表
     *
     * @param itemQueryDTO
     * @return
     */
    @Override
    public Page<OmsDoBillDDO> getBillDByBillId(ItemQueryDTO itemQueryDTO) {
        UserDTO userDTO = userTemplate.getUserDTO();
        String enterpriseId = userDTO.getEnterpriseId();
        PageHelper.startPage(itemQueryDTO.getPageNum(), itemQueryDTO.getPageSize());
        Map<String, Object> map = new HashMap<>();
        map.put("EnterpriseId", enterpriseId);
        map.put("billid", itemQueryDTO.getBillid());
        if (!StringUtils.isBlank(itemQueryDTO.getItemname())) {
            map.put("itemname", itemQueryDTO.getItemname());
        }
        if (!StringUtils.isBlank(itemQueryDTO.getItemcode())) {
            map.put("itemcode", itemQueryDTO.getItemcode());
        }
        List<OmsDoBillDDO> list = omsDoBillDMapper.findByMap(map, OmsDoBillDDO.class);
        Set<String> itemIds = list.stream().map(OmsDoBillDDO::getItemid).collect(Collectors.toSet());
        ItemUtils itemUtils = new ItemUtils(itemIds);
        for (OmsDoBillDDO x : list) {
            String itemid = x.getItemid();
            String lineid = x.getLineid();
            String billid = itemQueryDTO.getBillid();
            BigDecimal omsRedQty = redService.getOmsRedQty(billid, lineid, itemid);
            omsRedQty = omsRedQty == null?BigDecimal.ZERO:omsRedQty;
            x.setItemQty(x.getItemQty().subtract(omsRedQty));
            x.setPieceQty(x.getItemQty());
            ItemMstVO2 mstVO2 = itemUtils.getByItemId(itemid);
            x.setIsCaseOrPiece(mstVO2.getIsCaseOrPiece());
            x.setItemHealthListedLicensor(mstVO2.getItemHealthListedLicensor());
            x.setItemHealthRegisterNbr(mstVO2.getItemHealthRegisterNbr());
            x.setItemHealthSuperviseflag(mstVO2.getItemHealthSuperviseflag());
            x.setItemIsValuable(mstVO2.getItemIsValuable());
            x.setItemIsFragile(mstVO2.getItemIsFragile());
            x.setIsBacteria(mstVO2.getIsBacteria());
            x.setIsDelegate(mstVO2.getIsDelegate());
            x.setIsHosted(mstVO2.getIsHosted());
            x.setItemAssistantCode(mstVO2.getItemAssistantCode());
            x.setCargoOwnerCode(mstVO2.getCargoOwnerCode());
            x.setItemHealthIsDouble(mstVO2.getItemHealthIsDouble());
            x.setBillDId(x.getLineid());
			if (null != x.getStockStatus()) {
                if (x.getStockStatus().equals("1")) {
                    x.setStockStatusName("合格");
                } else if (x.getStockStatus().equals("2")) {
                    x.setStockStatusName("不合格");
                } else if (x.getStockStatus().equals("3")) {
                    x.setStockStatusName("待检");
                } else if (x.getStockStatus().equals("4")) {
                    x.setStockStatusName("退货");
                }
            }
        }
        return PageUtils.getPage(list);
    }


    /**
     * 查询波次分组订单列表
     *
     * @param vo
     * @return
     */
    @Override
    public Page<WaveBillDO> getWaveBills(WavesGroupBillQueryDTO vo) {
        String wavesGroupId = vo.getWavesGroupId();
        UserDTO userDTO = userTemplate.getUserDTO();
        String enterpriseId = userDTO.getEnterpriseId();
        Criteria crt = Criteria.forClass(WaveBillDO.class);
        Restriction r2 = vo.getStatus() == null ? null : Restrictions.eq("status", vo.getStatus().toString());
        Restriction r1 = StringUtils.isEmpty(wavesGroupId) ? null : Restrictions.eq("wavesGroupId", wavesGroupId);
        crt.setRestriction(Restrictions.and(r1, r2, Restrictions.eq("enterpriseId", enterpriseId)));
        crt.setOrder(Order.newInstance().desc("gmtCreate"));
        crt.buildRestriction();

        PageHelper.startPage(vo.getPageNum(), vo.getPageSize());
        List<WaveBillDO> list = billMapper.findByCriteria(crt);
        return PageUtils.getPage(list);
    }

    /**
     * 校验特殊订单指定库位数量
     * @param wavesCreateVO
     * @param billDList
     * @return boolean
     */
    public boolean checkPreLocation(WavesCreateDTO wavesCreateVO, List<OmsDoBillDDO> billDList) {
        if (!"Y".equals(wavesCreateVO.getIsSpecial())) {
            List<String> msg = new ArrayList<>();
            if (billDList.size()>0) {
                List<String> billDIds = billDList.stream().map(OmsDoBillDDO::getLineid).collect(Collectors.toList());
                Criteria criteria = Criteria.forClass(BillDPrePositionDO.class);
                Restriction r11 = Restrictions.in("billDId", billDIds.toArray(new String[billDIds.size()]));
                criteria.setRestriction(r11);
                List<BillDPrePositionDO> prePositionDOS = prePositionMapper.findByCriteria(criteria);
                for (OmsDoBillDDO omsDoBillDDO : billDList) {
                    List<BillDPrePositionDO> positionDOS = prePositionDOS.stream().filter(p->p.getBillDId().equals(omsDoBillDDO.getLineid())).collect(Collectors.toList());
                    // 是否指定了库位
                    if (positionDOS.size()>0) {
                        Double qty = positionDOS.stream().mapToDouble(e -> e.getQty().doubleValue()).sum();
                        // 校验订单行的未处理数量是否等于订单行的已指定数量
                        if (omsDoBillDDO.getItemQty().doubleValue() != qty) {
                            msg.add(String.format("订单【%s】订单行【%s】",omsDoBillDDO.getBillid(),omsDoBillDDO.getLineid()));
                        }
                    }
                }
            }
            if (!msg.isEmpty()) {
                throw new ParameterException(msg+"指定数量错误！");
            }
            return true;
        }

        List<OmsDoBillDDO> notLoc = new ArrayList<>(); // 未指定库位
        List<OmsDoBillDDO> missQty = new ArrayList<>(); // 指定数量不一致

        List<String> billIds = billDList.stream().map(OmsDoBillDDO::getBillid).distinct().collect(Collectors.toList());
        Restriction r1 = Restrictions.in("billId", billIds.toArray());

        // 订单冲红数量
        Criteria redC = Criteria.forClass(SaleRedRecord.class);
        redC.setRestriction(r1);
        List<SaleRedRecord> redRecords = saleRedRecordMapper.findByCriteria(redC);
        Map<String, BigDecimal> redMap = redRecords.stream().collect(Collectors.toMap(SaleRedRecord::getBillDId, SaleRedRecord::getQty, BigDecimal::add));

        // 订单指定库位数量
        Criteria preC = Criteria.forClass(BillDPrePositionDO.class);
        preC.setRestriction(r1);
        List<BillDPrePositionDO> prePositionDOS = prePositionMapper.findByCriteria(preC);

        if (CollUtil.isEmpty(prePositionDOS)) {
            // 所有单据均没有指定库位数量
            notLoc.addAll(billDList);
        } else {
            // 订单行指定库位数量
            Map<String, BigDecimal> preQtyMap = prePositionDOS.stream().collect(Collectors.toMap(BillDPrePositionDO::getBillDId, BillDPrePositionDO::getQty, BigDecimal::add));

            //
            notLoc.addAll(billDList.stream().filter(row -> {
                BigDecimal preQty = preQtyMap.getOrDefault(row.getLineid(), BigDecimal.ZERO);
                return preQty.compareTo(BigDecimal.ZERO) == 0;
            }).collect(Collectors.toList()));

            missQty.addAll(billDList.stream().filter(row -> !notLoc.contains(row)).filter(row -> {
                BigDecimal redQty = redMap.getOrDefault(row.getLineid(), BigDecimal.ZERO);
                BigDecimal preQty = preQtyMap.getOrDefault(row.getLineid(), BigDecimal.ZERO);
                return row.getItemQty().subtract(redQty).compareTo(preQty) != 0;
            }).collect(Collectors.toList()));
        }

        StringBuilder builder = new StringBuilder();
        if (CollUtil.isNotEmpty(notLoc)) {
            Map<String, String> billMap = notLoc.stream().collect(Collectors.toMap(OmsDoBillDDO::getBillno, OmsDoBillDDO::getItemcode, (o1, o2) -> String.format("%s,%s", o1, o2)));
            String message = billMap.entrySet().stream()
                    .map(row -> String.format("单据【%s】商品【%s】", row.getKey(), row.getValue()))
                    .collect(Collectors.joining(","));
            builder.append(message).append("未指定库位！");
        }
        if (CollUtil.isNotEmpty(missQty)){
            Map<String, String> billMap = missQty.stream().collect(Collectors.toMap(OmsDoBillDDO::getBillno, OmsDoBillDDO::getItemcode, (o1, o2) -> String.format("%s,%s", o1, o2)));
            String message = billMap.entrySet().stream()
                    .map(row -> String.format("单据【%s】商品【%s】", row.getKey(), row.getValue()))
                    .collect(Collectors.joining(","));
            builder.append(message).append("指定数量错误！");
        }
        logger.info("校验特殊订单指定库位数量：" + builder.toString());
        if (builder.length() > 0) {
            throw new BizException(builder.toString());
        }
        return true;
    }


    @Override
    public Object execute(InterfaceBill dataObject) throws Exception{
        if(dataObject.getNo().equals("retry")){
            throw new RetryException("retry test");
        }
        System.out.println("there is your interface proces");
        return null;
    }

    /**
     * @param dataObject
     * @return com.prolog.fx.inf.core.dto.LogInfo
     * @Description 转换日志信息
     * @param: dataObject
     * @Author chenyc
     * @Date 2021/8/9 18:25
     */
    @Override
    public SimpleLog toLog(InterfaceBill dataObject) {
        SimpleLog log = new SimpleLog();
        try {
            log.setData(JsonUtils.toString(dataObject));
        } catch (Exception e) {
            logger.error(e.getMessage());
        }
        log.setBussinessKey1(dataObject.getId());
        log.setBussinessKey2(dataObject.getNo());
        log.setBussinessKey3(dataObject.getCustomer());
        return log;
    }

    /**
     * @param log
     * @return D
     * @Description 转换数据对象
     * @param: logInfo
     * @Author chenyc
     * @Date 2021/8/9 18:25
     */
    @Override
    public InterfaceBill toDataObject(SimpleLog log) {
        if(log!=null){
            try {
                InterfaceBill bill = JsonUtils.parseJsonString(log.getData(), new TypeReference<InterfaceBill>(){});
                return bill;
            } catch (Exception e) {
                logger.error(e.getMessage());
            }
        }
        return null;
    }

    @Override
    public Page<WaveDO> listWaveRepush(Map<String, Object> param) {
        Integer pageNum = (Integer) param.get("pageNum");
        Integer pageSize = (Integer) param.get("pageSize");
        if (pageNum == null || pageNum == 0) {
            pageNum = PageParam.DEFAULT_PAGE_NUM;
        }
        if (pageSize == null || pageSize == 0) {
            pageSize = PageParam.DEFAULT_PAGE_SIZE;
        }
        PageHelper.startPage(pageNum, pageSize);
        String id = (String) param.get("id");
        Criteria criteria = Criteria.forClass(WaveDO.class);
        Restriction r1 = null;
        Restriction r2 = Restrictions.eq("seedNoticeFlag", "1");
        if (!StringUtils.isEmpty(id)) {
            r1 = Restrictions.eq("id", id);
        }
        criteria.setRestriction(Restrictions.and(r1, r2));
        criteria.orderAscBy("gmtCreate");
        return PageUtils.getPage(waveMapper.findByCriteria(criteria));
    }

    @Override
    public void waveRepush(List<String> ids) {
        AssertUtils.isNotEmpty(ids, "请勾选重推数据！");
        WaveDO waveDO = new WaveDO();
        waveDO.setSeedNoticeFlag("0");
        Criteria criteria = Criteria.forClass(waveDO.getClass());
        criteria.restrictionBuilder().in("id", ids.toArray(new String[ids.size()]));
        criteria.buildRestriction();
        long seedNoticeFlag = waveMapper.updateFieldsByCriteria(waveDO, FieldSelector.newInstance().include("seedNoticeFlag"), criteria);
        if (seedNoticeFlag <= 0 ) {
            throw new BizException("发票重推失败！");
        }
    }

    @Override
    public void hangupBill(List<String> taskLineIds) {
        List<PickTaskD> pickTaskDS = pickTaskDMapper.findByIds(PickTaskD.class, taskLineIds.toArray(), FieldSelector.newInstance().include("billId"));
        if(pickTaskDS == null || pickTaskDS.size() <= 0){
            return;
        }
        List<String> billIds = pickTaskDS.stream().filter(item -> !StringUtils.isBlank(item.getBillId())).map(PickTaskD::getBillId).distinct().collect(Collectors.toList());
        for (String billId : billIds) {
            BillUtils.updateStatus(billId, BILL_STATUS.POSITION_ERROR, "EIS缺货!");
        }

    }

    /**
     * 自动预补货
     */
    private void autoPreReplenish(String waveId){
        PreReplenishVo vo = new PreReplenishVo();
        List<PreReplenishDto> list = omsDoBillDMapper.getPreReplenishPageList(vo);
        if(list == null || list.size() <= 0){
            return ;
        }

        //预补货列表
        List<ReplenishPreVO> replenishPreVOS = new ArrayList<>();

        //根据品批合并
        Map<String, List<PreReplenishDto>> map = list.stream().collect(Collectors.groupingBy(item -> item.getItemId() + "," + item.getBatchNo() + "," + item.getBranchId()));
        for (Map.Entry<String, List<PreReplenishDto>> entry : map.entrySet()) {
            List<PreReplenishDto> value = entry.getValue();

            PreReplenishDto preReplenishDto = value.get(0);

            ReplenishPreVO replenishPreVO = new ReplenishPreVO();
            replenishPreVO.setEnterpriseId(preReplenishDto.getEnterpriseId());
            replenishPreVO.setWarehouseId(preReplenishDto.getBranchId());
            replenishPreVO.setCargoOwnerId(preReplenishDto.getConsignorId());

            replenishPreVO.setWaveId(waveId);

            replenishPreVO.setItemId(preReplenishDto.getItemId());
            replenishPreVO.setManufacturerLotNum(preReplenishDto.getBatchNo());
            replenishPreVO.setWarehouseAreaCategory("AGVL");
            BigDecimal sumPcs = value.stream().map(PreReplenishDto::getShouldPcs).reduce(BigDecimal.ZERO, BigDecimal::add);
            replenishPreVO.setPieceQuantity(sumPcs.multiply(preReplenishDto.getConversionRate()));

            replenishPreVOS.add(replenishPreVO);
        }

        logger.error("自动波次，预补货列表：{}",JSON.toJSONString(replenishPreVOS));
        operateResource.replenishPre(replenishPreVOS);
//        RestMessage<Object> restMessage = operateResource.replenishPre(replenishPreVOS);
//        if(restMessage == null || !restMessage.isSuccess()){
//            throw new BizException("自动波次，自动预补货失败!");
//        }

    }
}
