package com.hw.wms.service.impl;

import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import com.hw.common.core.constant.SecurityConstants;
import com.hw.common.core.constant.WmsConstants;
import com.hw.common.core.domain.R;
import com.hw.common.core.exception.ServiceException;
import com.hw.common.core.utils.StringUtils;
import com.hw.common.security.utils.SecurityUtils;
import com.hw.mes.api.RemoteMesService;
import com.hw.mes.api.domain.MesBaseMaterialInfo;
import com.hw.mes.api.domain.vo.MesBaseMaterialInfoVo;
import com.hw.wms.domain.*;
import com.hw.wms.domain.vo.*;
import com.hw.wms.mapper.*;
import com.hw.wms.service.IWmsRawOutstockService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 原材料出库记录Service业务层处理
 *
 * @author xs
 * @date 2023-12-20
 */
@Service
public class WmsRawOutstockServiceImpl implements IWmsRawOutstockService {
    @Autowired
    private WmsRawOutstockMapper wmsRawOutstockMapper;

    @Autowired
    private WmsRawOutstockDetailMapper wmsRawOutstockDetailMapper;

    @Autowired
    private WmsRawInstockMapper wmsRawInstockMapper;

    @Autowired
    private WmsRawInstockDetailMapper wmsRawInstockDetailMapper;

    @Autowired
    private WmsRawStockMapper wmsRawStockMapper;

    @Autowired
    private WmsBaseWarehouseMapper wmsBaseWarehouseMapper;

    @Autowired
    private WmsBaseLocationMapper wmsBaseLocationMapper;

    @Autowired
    private RemoteMesService remoteMesService;

    /**
     * 查询原材料出库记录
     *
     * @param rawOutstockId 原材料出库记录主键
     * @return 原材料出库记录
     */
    @Override
    public WmsRawOutstock selectWmsRawOutstockByRawOutstockId(Long rawOutstockId) {
        WmsRawOutstock wmsRawOutstock = wmsRawOutstockMapper.selectWmsRawOutstockByRawOutstockId(rawOutstockId);
        List<WmsRawOutstockDetail> wmsRawOutstockDetailList = wmsRawOutstock.getWmsRawOutstockDetailList();
        Long[] materialIds = wmsRawOutstockDetailList.stream().map(o -> o.getMaterialId()).toArray(Long[]::new);
        MesBaseMaterialInfoVo mesBaseMaterialInfoVo = new MesBaseMaterialInfoVo();
        mesBaseMaterialInfoVo.setMaterialIds(materialIds);
        R<List<MesBaseMaterialInfo>> materialInfosR = remoteMesService.getMaterialsByMaterialIds(mesBaseMaterialInfoVo, SecurityConstants.INNER);
        if (materialInfosR != null) {
            List<MesBaseMaterialInfo> materialInfos = materialInfosR.getData();

            Map<Long, MesBaseMaterialInfo> MesBaseMaterialInfoMap = materialInfos.stream()
                    .collect(Collectors.toMap(MesBaseMaterialInfo::getMaterialId, mesBaseMaterialInfo -> mesBaseMaterialInfo));

            wmsRawOutstockDetailList.forEach(wmsRawOutstockDetail -> {
                MesBaseMaterialInfo materialInfo = MesBaseMaterialInfoMap.get(wmsRawOutstockDetail.getMaterialId());
                if (materialInfo != null) {
                    wmsRawOutstockDetail.setMaterialCode(materialInfo.getMaterialCode());
                    wmsRawOutstockDetail.setMaterialName(materialInfo.getMaterialName());
                }
            });
        }

        return wmsRawOutstock;
    }

    /**
     * 查询原材料出库记录列表
     *
     * @param wmsRawOutstock 原材料出库记录
     * @return 原材料出库记录
     */
    @Override
    public List<WmsRawOutstock> selectWmsRawOutstockList(WmsRawOutstock wmsRawOutstock) {
        return wmsRawOutstockMapper.selectWmsRawOutstockList(wmsRawOutstock);
    }

    /**
     * 新增原材料出库记录
     *
     * @param wmsRawOutstock 原材料出库记录
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int insertWmsRawOutstock(WmsRawOutstock wmsRawOutstock) {
        int rows = wmsRawOutstockMapper.insertWmsRawOutstock(wmsRawOutstock);
        insertWmsRawOutstockDetail(wmsRawOutstock);
        return rows;
    }

    /**
     * 修改原材料出库记录
     *
     * @param wmsRawOutstock 原材料出库记录
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int updateWmsRawOutstock(WmsRawOutstock wmsRawOutstock) {
        wmsRawOutstockMapper.deleteWmsRawOutstockDetailByRawOutstockId(wmsRawOutstock.getRawOutstockId());
        insertWmsRawOutstockDetail(wmsRawOutstock);
        return wmsRawOutstockMapper.updateWmsRawOutstock(wmsRawOutstock);
    }

    /**
     * 批量删除原材料出库记录
     *
     * @param rawOutstockIds 需要删除的原材料出库记录主键
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int deleteWmsRawOutstockByRawOutstockIds(Long[] rawOutstockIds) {
        wmsRawOutstockMapper.deleteWmsRawOutstockDetailByRawOutstockIds(rawOutstockIds);
        return wmsRawOutstockMapper.deleteWmsRawOutstockByRawOutstockIds(rawOutstockIds);
    }

    /**
     * 删除原材料出库记录信息
     *
     * @param rawOutstockId 原材料出库记录主键
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int deleteWmsRawOutstockByRawOutstockId(Long rawOutstockId) {
        wmsRawOutstockMapper.deleteWmsRawOutstockDetailByRawOutstockId(rawOutstockId);
        return wmsRawOutstockMapper.deleteWmsRawOutstockByRawOutstockId(rawOutstockId);
    }

    /**
     * 新增原材料出库记录明细信息
     *
     * @param wmsRawOutstock 原材料出库记录对象
     */
    public void insertWmsRawOutstockDetail(WmsRawOutstock wmsRawOutstock) {
        List<WmsRawOutstockDetail> wmsRawOutstockDetailList = wmsRawOutstock.getWmsRawOutstockDetailList();
        Long rawOutstockId = wmsRawOutstock.getRawOutstockId();
        if (StringUtils.isNotNull(wmsRawOutstockDetailList)) {
            List<WmsRawOutstockDetail> list = new ArrayList<WmsRawOutstockDetail>();
            for (WmsRawOutstockDetail wmsRawOutstockDetail : wmsRawOutstockDetailList) {
                wmsRawOutstockDetail.setRawOutstockId(rawOutstockId);
                list.add(wmsRawOutstockDetail);
            }
            if (list.size() > 0) {
                wmsRawOutstockMapper.batchWmsRawOutstockDetail(list);
            }
        }
    }


    /**
     * 审核原材料出库记录
     *
     * @param wmsRawOutstockAuditVo
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public JSONObject auditWmsRawOutstock(WmsRawOutstockAuditVo wmsRawOutstockAuditVo) {
        String auditStatus = wmsRawOutstockAuditVo.getAuditStatus();
        String auditReason = wmsRawOutstockAuditVo.getAuditReason();
        Date currentDate = new Date();
        String userName = SecurityUtils.getUsername();
        WmsRawOutstock wmsRawOutstock = wmsRawOutstockMapper.selectWmsRawOutstockByRawOutstockId(wmsRawOutstockAuditVo.getRawOutstockId());
        wmsRawOutstock.setAuditStatus(auditStatus);
        wmsRawOutstock.setAuditReason(auditReason);
        wmsRawOutstock.setAuditBy(SecurityUtils.getUsername());
        wmsRawOutstock.setAuditDate(new Date());

        JSONObject returnJson = new JSONObject();
        returnJson.put("auditStatus", auditStatus);
        //审核不通过，需要有审核意见，并且需要更新库位占用数量。
        if (auditStatus.equalsIgnoreCase(WmsConstants.RAW_OUTSTOCK_AUDIT_STATUS_FAIL)) {
            if (StringUtils.isEmpty(auditReason)) {
                throw new ServiceException("请输入审核意见");
            }
            List<WmsRawOutstockDetail> wmsRawOutstockDetails = wmsRawOutstock.getWmsRawOutstockDetailList();
            wmsRawOutstockDetails.forEach(wmsRawOutstockDetail -> {
                String locationCode = wmsRawOutstockDetail.getLocationCode();
                String instockBatch = wmsRawOutstockDetail.getInstockBatch();
                BigDecimal planAmount = wmsRawOutstockDetail.getPlanAmount();
                WmsRawStock wmsRawStock = wmsRawStockMapper.selectRawStockByLocationAndBatch(locationCode, instockBatch);
                if (wmsRawStock != null) {
                    BigDecimal occupyAmount = wmsRawStock.getOccupyAmount() == null ?
                            BigDecimal.ZERO : wmsRawStock.getOccupyAmount();
                    if (occupyAmount.compareTo(planAmount) >= 0) {
                        wmsRawStock.setOccupyAmount(occupyAmount.subtract(planAmount));
                        wmsRawStock.setUpdateBy(userName);
                        wmsRawStock.setUpdateDate(currentDate);
                    } else {
                        wmsRawStock.setOccupyAmount(BigDecimal.ZERO);
                        wmsRawStock.setUpdateBy(userName);
                        wmsRawStock.setUpdateDate(currentDate);
                    }
                    wmsRawStockMapper.updateWmsRawStock(wmsRawStock);
                }
            });

        } else {
            //如果操作类型是agv，需要返回具体任务信息给上位机，由上位机通知WCS调度agv
            if (wmsRawOutstock.getOperationType().equals(WmsConstants.WMS_OPERATION_TYPE_AUTO)) {
                JSONArray agvTasks = new JSONArray();
                List<WmsRawOutstockDetail> wmsRawOutstockDetails = wmsRawOutstock.getWmsRawOutstockDetailList();
                wmsRawOutstockDetails.forEach(wmsRawOutstockDetail -> {
                    //todo:双排库，先出前排，再出后排，如果前排有挡住的需要移库锁库等，按顺序下发任务
                    JSONObject agvTask = new JSONObject();
                    agvTask.put("stationId", wmsRawOutstock.getStationId());
                    agvTask.put("locationCode", wmsRawOutstockDetail.getLocationCode());
                    agvTask.put("planAmount", wmsRawOutstockDetail.getPlanAmount());
                    agvTasks.add(agvTask);
                });
                returnJson.put("agvTasks", agvTasks.toString());
            }
        }

        wmsRawOutstockMapper.updateWmsRawOutstock(wmsRawOutstock);

        return returnJson;

    }

    /**
     * 原材料出库记录明细确认
     *
     * @param wmsRawOutstockConfirmVo
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int confirmWmsRawOutstock(WmsRawOutstockConfirmVo wmsRawOutstockConfirmVo) {
        Long rawOutstockDetailId = wmsRawOutstockConfirmVo.getRawOutstockDetailId();
        String locationCode = wmsRawOutstockConfirmVo.getLocationCode();
        String instockBatch = wmsRawOutstockConfirmVo.getInstockBatch();
        BigDecimal outstockAmount = wmsRawOutstockConfirmVo.getOutstockAmount();
        //判断是否有此出库任务,人工出库的同一个出库记录，同一个库位、同一个物料对应一个明细（agv的有可能是多个）
        WmsRawOutstockDetail queryRawOutstockDetail = new WmsRawOutstockDetail();
        queryRawOutstockDetail.setLocationCode(locationCode);
        queryRawOutstockDetail.setInstockBatch(instockBatch);
        List<WmsRawOutstockDetail> wmsRawOutstockDetails = wmsRawOutstockDetailMapper.selectWmsRawOutstockDetailList(queryRawOutstockDetail);
        WmsRawOutstockDetail wmsRawOutstockDetail = new WmsRawOutstockDetail();
        if (wmsRawOutstockDetails == null || wmsRawOutstockDetails.isEmpty()) {
            throw new ServiceException("没有此出库任务");
        } else if (wmsRawOutstockDetails.size() > 1) {
            if (rawOutstockDetailId == null) {
                throw new ServiceException("请选择具体出库任务后点击确认");
            } else {
                wmsRawOutstockDetail = wmsRawOutstockDetails.stream().filter(wro -> wro.getRawOutstockDetailId().equals(rawOutstockDetailId)).findFirst().get();
            }
        } else {
            wmsRawOutstockDetail = wmsRawOutstockDetails.get(0);
        }

        //判断质检状态
        if (wmsRawOutstockDetail.getQualityStatus() != null
                && !wmsRawOutstockDetail.getQualityStatus().equals(WmsConstants.WMS_QUALITY_STATUS_PASS)) {
            throw new ServiceException("质检通过才能出库");
        }

        // 判断本次出库数量+已出库数量是否大于计划出库数量
        BigDecimal hadOutstockAmount = wmsRawOutstockDetail.getOutstockAmount() == null ? BigDecimal.ZERO : wmsRawOutstockDetail.getOutstockAmount();
        BigDecimal planOutstockAmount = wmsRawOutstockDetail.getPlanAmount();

        BigDecimal updatedOutstockAmount = hadOutstockAmount.add(outstockAmount);
        if (updatedOutstockAmount.compareTo(planOutstockAmount) > 0) {
            throw new ServiceException("出库数量不能大于" + planOutstockAmount);
        }

        wmsRawOutstockDetail.setOutstockAmount(updatedOutstockAmount);
        String executeStatus = "";
        if (updatedOutstockAmount.compareTo(planOutstockAmount) == 0) {
            executeStatus = WmsConstants.WMS_EXECUTE_STATUS_FINISH;
            //更新出库记录执行状态
            this.updateRawOutstockDetailExecuteStatus(wmsRawOutstockDetail.getRawOutstockId(),
                    wmsRawOutstockDetail.getRawOutstockDetailId());
        } else {
            executeStatus = WmsConstants.WMS_EXECUTE_STATUS_EXECUTING;
        }

        Date currentDate = new Date();
        String userName = SecurityUtils.getUsername();
        wmsRawOutstockDetail.setExecuteStatus(executeStatus);
        if (wmsRawOutstockDetail.getOutstockTime() == null) {
            wmsRawOutstockDetail.setOutstockTime(currentDate);
        } else {
            wmsRawOutstockDetail.setUpdateDate(currentDate);
        }
        if (wmsRawOutstockDetail.getOutstockPerson() == null) {
            wmsRawOutstockDetail.setOutstockPerson(userName);
        } else {
            wmsRawOutstockDetail.setUpdateBy(userName);
        }

        //更新原材料库存占用数量和总数量
        WmsRawStock wmsRawStock = wmsRawStockMapper.selectRawStockByLocationAndBatch(locationCode, instockBatch);
        if (wmsRawStock == null) {
            throw new ServiceException("数据异常");
        } else {
            BigDecimal totalAmount = wmsRawStock.getTotalAmount() == null ? BigDecimal.ZERO : wmsRawStock.getTotalAmount();
            BigDecimal occupyAmount = wmsRawStock.getOccupyAmount() == null ? BigDecimal.ZERO : wmsRawStock.getOccupyAmount();
            BigDecimal updatedOccupyAmount = occupyAmount.subtract(outstockAmount);
            BigDecimal updatedTotalAmount = totalAmount.subtract(outstockAmount);
            updatedOccupyAmount = updatedOccupyAmount.compareTo(BigDecimal.ZERO) >= 0 ? updatedOccupyAmount : BigDecimal.ZERO;
            updatedTotalAmount = updatedTotalAmount.compareTo(BigDecimal.ZERO) >= 0 ? updatedTotalAmount : BigDecimal.ZERO;
            wmsRawStock.setOccupyAmount(updatedOccupyAmount);
            wmsRawStock.setTotalAmount(updatedTotalAmount);
            wmsRawStock.setUpdateDate(currentDate);
            wmsRawStock.setUpdateBy(userName);
            wmsRawStockMapper.updateWmsRawStock(wmsRawStock);
        }

        wmsRawOutstockDetailMapper.updateWmsRawOutstockDetail(wmsRawOutstockDetail);

        //todo 同步erp
        return 1;
    }

    /**
     * 查询原材料出库记录明细列表，供手持出库使用
     *
     * @param wmsRawOutstockDetail 原材料出库记录明细
     * @return 原材料出库记录明细
     */
    @Override
    public List<WmsRawOutstockDetail> selectAuditPassRawOutstockDetails(WmsRawOutstockDetail wmsRawOutstockDetail) {
        return wmsRawOutstockDetailMapper.selectAuditPassRawOutstockDetails(wmsRawOutstockDetail);
    }


    /**
     * 更新原材料出库记录的执行状态
     *
     * @param rawOutstockId
     * @param rawOutStockDetailId
     */
    private void updateRawOutstockDetailExecuteStatus(Long rawOutstockId, Long rawOutStockDetailId) {
        WmsRawOutstockDetail queryRawOutstockDetail = new WmsRawOutstockDetail();
        queryRawOutstockDetail.setRawOutstockId(rawOutstockId);
        List<WmsRawOutstockDetail> wmsRawOutstockDetails = wmsRawOutstockDetailMapper.selectWmsRawOutstockDetailList(queryRawOutstockDetail);

        boolean isExecuteFinish = true;
        for (WmsRawOutstockDetail wmsRawOutstockDetail : wmsRawOutstockDetails) {
            if (!wmsRawOutstockDetail.getRawOutstockDetailId().equals(rawOutStockDetailId)
                    && !wmsRawOutstockDetail.getExecuteStatus().equals(WmsConstants.WMS_EXECUTE_STATUS_FINISH)) {
                isExecuteFinish = false;
            }
        }

        if (isExecuteFinish) {
            WmsRawOutstock wmsRawOutstock = wmsRawOutstockMapper.selectWmsRawOutstockByRawOutstockId(rawOutstockId);
            wmsRawOutstock.setExecuteStatus(WmsConstants.WMS_EXECUTE_STATUS_FINISH);
            wmsRawOutstockMapper.updateWmsRawOutstock(wmsRawOutstock);
        }
    }


    /**
     * 申请领料
     *
     * @param wmsRawOutstock 原材料出库记录
     * @return 结果
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public int applyRawOutstock(WmsRawOutstock wmsRawOutstock) {
        Long warehouseId = wmsRawOutstock.getWarehouseId();
        List<WmsRawOutstockDetail> rawOutstockDetails = wmsRawOutstock.getWmsRawOutstockDetailList();
        //根据物料ID分组获取总申请数量
        Map<Long, BigDecimal> rawOutstockDetailMap = rawOutstockDetails.stream()
                .collect(Collectors.groupingBy(WmsRawOutstockDetail::getMaterialId, HashMap::new, Collectors.reducing(BigDecimal.ZERO, WmsRawOutstockDetail::getPlanAmount, BigDecimal::add)));

        //判断仓库类型，出库记录保存对应操作类型，出库记录明细保存对应出库方式
        WmsBaseWarehouse warehouse = wmsBaseWarehouseMapper.selectWmsBaseWarehouseByWarehouseId(warehouseId);
        String warehouseType = warehouse.getWarehouseType();
        String operationType = WmsConstants.OPERATION_TYPE_MAP.get(warehouseType);
        String outstockWay = WmsConstants.OUTSTOCK_WAY_MAP.get(warehouseType);

        String queryQualityStatus = WmsConstants.WMS_QUALITY_STATUS_PASS;
        List<WmsRawOutstockDetail> toUpdatedRawOutstockDetails = new ArrayList<>();
        Date currentDate = new Date();
        String userName = SecurityUtils.getUsername();
        for (Long materialId : rawOutstockDetailMap.keySet()) {
            BigDecimal planAmount = rawOutstockDetailMap.get(materialId);
            BigDecimal realRawStock = BigDecimal.ZERO;//实际库存总量
            WmsRawStock queryRawStock = new WmsRawStock();
            queryRawStock.setWarehouseId(warehouseId);
            queryRawStock.setMaterialId(materialId);
//            queryRawStock.setQualityStatus(queryQualityStatus);//todo：先不过滤质检状态
            //判断库位和可用数量（总数量减冻结数量和占用数量），按先进先出（按入库时间排序获取）
            List<WmsRawStock> wmsRawStocks = wmsRawStockMapper.selectWmsRawStocks4Apply(queryRawStock);

            if (wmsRawStocks != null && !wmsRawStocks.isEmpty()) {
                for (WmsRawStock wmsRawStock : wmsRawStocks) {
                    BigDecimal totalAmount = wmsRawStock.getTotalAmount();
                    BigDecimal frozenAmount = wmsRawStock.getFrozenAmount() == null ? BigDecimal.ZERO : wmsRawStock.getFrozenAmount();
                    BigDecimal occupyAmount = wmsRawStock.getOccupyAmount() == null ? BigDecimal.ZERO : wmsRawStock.getOccupyAmount();
                    BigDecimal availableAmount = totalAmount.subtract(frozenAmount).subtract(occupyAmount);
                    realRawStock = realRawStock.add(availableAmount);
                    //可出库数量大于0
                    if (availableAmount.compareTo(BigDecimal.ZERO) > 0) {
                        //如果申请数量大于此库位可出库的数量则继续进行下一库位判断
                        if (planAmount.compareTo(availableAmount) > 0) {
                            //占用数量更改
                            wmsRawStock.setOccupyAmount(occupyAmount.add(availableAmount));
                            wmsRawStock.setUpdateDate(currentDate);
                            wmsRawStock.setUpdateBy(userName);
                            //如果同一个物料有多条记录的处理（最好避免前端可以传多条同一个物料的申请）,提前更新（主要是更新数量等信息，避免在下一次判断中还使用上次的数量），放在事务中
                            wmsRawStockMapper.updateWmsRawStock(wmsRawStock);

                            //原材料出库记录明细
                            WmsRawOutstockDetail wmsRawOutstockDetail = getWmsRawOutstockDetail(wmsRawStock, materialId,
                                    availableAmount, currentDate, userName, outstockWay);
                            toUpdatedRawOutstockDetails.add(wmsRawOutstockDetail);

                            planAmount = planAmount.subtract(availableAmount);
                        } else if (planAmount.compareTo(availableAmount) == 0) {//计划数量等于可出库数量
                            //占用数量更改
                            wmsRawStock.setOccupyAmount(occupyAmount.add(availableAmount));
                            wmsRawStock.setUpdateDate(currentDate);
                            wmsRawStock.setUpdateBy(userName);
                            //如果同一个物料有多条记录的处理,提前更新（主要是更新数量等信息，避免在下一次判断中还使用上次的数量），放在事务中
                            wmsRawStockMapper.updateWmsRawStock(wmsRawStock);

                            //原材料出库记录明细
                            WmsRawOutstockDetail wmsRawOutstockDetail = getWmsRawOutstockDetail(wmsRawStock, materialId,
                                    availableAmount, currentDate, userName, outstockWay);
                            toUpdatedRawOutstockDetails.add(wmsRawOutstockDetail);
                            planAmount = BigDecimal.ZERO;
                            break;
                        } else if (planAmount.compareTo(availableAmount) < 0) {
                            //占用数量更改
                            wmsRawStock.setOccupyAmount(occupyAmount.add(planAmount));
                            wmsRawStock.setUpdateDate(currentDate);
                            wmsRawStock.setUpdateBy(userName);
                            //如果同一个物料有多条记录的处理,提前更新（主要是更新数量等信息，避免在下一次判断中还使用上次的数量），放在事务中
                            wmsRawStockMapper.updateWmsRawStock(wmsRawStock);

                            //原材料出库记录明细
                            WmsRawOutstockDetail wmsRawOutstockDetail = getWmsRawOutstockDetail(wmsRawStock, materialId,
                                    planAmount, currentDate, userName, outstockWay);
                            toUpdatedRawOutstockDetails.add(wmsRawOutstockDetail);
                            planAmount = BigDecimal.ZERO;
                            break;
                        }
                    }
                }
            }

            if (planAmount.compareTo(BigDecimal.ZERO) > 0) {
                throw new ServiceException("物料编号为[" + materialId + "]库存不足，只有" + realRawStock);
            }

        }


        //todo 审核状态默认为待审核，后续可根据系统参数判断保存是待审核还是直接审核通过
        wmsRawOutstock.setOperationType(operationType);
        wmsRawOutstock.setApplyBy(userName);
        wmsRawOutstock.setApplyDate(currentDate);
        wmsRawOutstock.setAuditStatus(WmsConstants.RAW_OUTSTOCK_AUDIT_STATUS_TOAUDIT);
        wmsRawOutstock.setExecuteStatus(WmsConstants.WMS_EXECUTE_STATUS_TOEXECUTE);

        int rows = wmsRawOutstockMapper.insertWmsRawOutstock(wmsRawOutstock);
        wmsRawOutstock.setWmsRawOutstockDetailList(toUpdatedRawOutstockDetails);
        insertWmsRawOutstockDetail(wmsRawOutstock);

        return rows;
    }


    /**
     * 获取待保存的原材料出库记录明细
     *
     * @param wmsRawStock
     * @param materialId
     * @param availableAmount
     * @param currentDate
     * @param userName
     * @return
     */
    private WmsRawOutstockDetail getWmsRawOutstockDetail(WmsRawStock wmsRawStock, Long materialId,
                                                         BigDecimal availableAmount, Date currentDate, String userName, String outstockWay) {
        WmsRawOutstockDetail wmsRawOutstockDetail = new WmsRawOutstockDetail();
        wmsRawOutstockDetail.setLocationCode(wmsRawStock.getLocationCode());
        wmsRawOutstockDetail.setMaterialId(materialId);
        wmsRawOutstockDetail.setInstockBatch(wmsRawStock.getInstockBatch());
        wmsRawOutstockDetail.setPlanAmount(availableAmount);
        wmsRawOutstockDetail.setExecuteStatus(WmsConstants.WMS_EXECUTE_STATUS_TOEXECUTE);
        wmsRawOutstockDetail.setOutstockWay(outstockWay);
        wmsRawOutstockDetail.setQualityStatus(wmsRawStock.getQualityStatus());
        wmsRawOutstockDetail.setCreateDate(currentDate);
        wmsRawOutstockDetail.setCreateBy(userName);

        return wmsRawOutstockDetail;
    }


    /**
     * AGV原材料出库
     *
     * @param wmsAgvRawOutstockVo
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public List<WmsWcsVo> applyAgvRawOutstock(WmsAgvRawOutstockVo wmsAgvRawOutstockVo) {
        List<WmsWcsVo> wmsWcsVos = new ArrayList<>();

        Long warehouseId = wmsAgvRawOutstockVo.getWarehouseId();
        Long materialId = wmsAgvRawOutstockVo.getMaterialId();

        WmsBaseWarehouse warehouse = wmsBaseWarehouseMapper.selectWmsBaseWarehouseByWarehouseId(warehouseId);
        List<WmsBaseLocation> baseLocations = warehouse.getWmsBaseLocationList();
        Map<String, WmsBaseLocation> baseLocationsMap = baseLocations.stream().collect(Collectors.toMap(WmsBaseLocation::getLocationCode, baseLocation -> baseLocation));

        WmsRawStock queryRawStock = new WmsRawStock();
        queryRawStock.setWarehouseId(warehouseId);
        queryRawStock.setMaterialId(materialId);
        queryRawStock.setCompleteFlag(WmsConstants.WMS_RAW_STOCK_COMPLETE_FLAG_YES);

        List<WmsRawStock> rawStockList = wmsRawStockMapper.selectWmsRawStockInList(queryRawStock);

        Map<String, List<WmsRawStock>> rawStockListMap = rawStockList.stream()
                .collect(Collectors.groupingBy(WmsRawStock::getLocationCode));

        String userName = SecurityUtils.getUsername();
        Date currentDate = new Date();

        //选择出库库位
        WmsBaseLocation outstockLocation = null;//出库库位
        String moveMaterialBarcode = "";
        Long moveMaterialId = null;
        for (WmsRawStock wmsRawStock : rawStockList) {
            BigDecimal occupyAmount = wmsRawStock.getOccupyAmount() == null ? BigDecimal.ZERO : wmsRawStock.getOccupyAmount();
            BigDecimal frozenAmount = wmsRawStock.getFrozenAmount() == null ? BigDecimal.ZERO : wmsRawStock.getFrozenAmount();
            WmsBaseLocation wmsBaseLocation = baseLocationsMap.get(wmsRawStock.getLocationCode());
            //先确认出库库位，可用数量>=1，库位状态是正常的

            if (occupyAmount.compareTo(BigDecimal.ZERO) <= 0 && frozenAmount.compareTo(BigDecimal.ZERO) <= 0
                    && wmsBaseLocation.getLocationStatus().equals(WmsConstants.WMS_BASE_LOCATION_STATUS_NORMAL)
            ) {
                outstockLocation = wmsBaseLocation;
                moveMaterialBarcode = wmsRawStock.getInstockBatch();
                moveMaterialId = wmsRawStock.getMaterialId();
                break;
            }
        }

        if (outstockLocation == null) {
            throw new ServiceException("没有可出库库位");
        }

        Long outstockLocRow = outstockLocation.getLocRow();//出库库位排
        if (outstockLocRow > 1) { //如果是后排
            //先判断对应的前排库位有没有库存，如果没有库存，则锁定库位;如果有库存则需要移库
            Long outstockColumn = outstockLocation.getLocColumn();
            WmsBaseLocation frontLocation = new WmsBaseLocation();//前排库位
            WmsBaseLocation outExcessLocation = null;//出库过度库位
            for (WmsBaseLocation location : baseLocations) {
                String instockFlag = location.getInstockFlag() == null ? "0" : location.getInstockFlag();
                Long instockLocRow = location.getLocRow() == null ? 1L : location.getLocRow();
                if (location.getLocColumn().equals(outstockColumn) && location.getLocRow().equals(1L)) {
                    frontLocation = location;
                }
                //如果是入库过度库位，并且是第一排
                if (instockFlag.equals(WmsConstants.WMS_BASE_LOCATION_INSTOCK_FLAG_YES) && instockLocRow.equals(1L)) {
                    outExcessLocation = location;
                }
            }

            List<WmsRawStock> frontLocationRawStocks = rawStockListMap.get(frontLocation.getLocationCode());
            //如果是后排，则判断前排是否有库存，如果有库存则需要移库，移库还要选择没锁定的，然后锁定
            if (frontLocationRawStocks != null && !frontLocationRawStocks.isEmpty()) {//移库位
                if (outExcessLocation == null) {
                    throw new ServiceException("需设置入库过度库位");
                }

                if (outExcessLocation.getLocationId().equals(frontLocation.getLocationId())) {
                    throw new ServiceException("前排库位为入库过度库位，但是过度库位有库存");
                }

                WmsRawStock forntWmsRawStock = frontLocationRawStocks.get(0);

                //锁入库过度库位
                outExcessLocation.setLocationStatus(WmsConstants.WMS_BASE_LOCATION_STATUS_AUTO_LOCK);
                outExcessLocation.setInstockFlag(WmsConstants.WMS_BASE_LOCATION_INSTOCK_FLAG_NO);//需设置为非入库过度库位
                outExcessLocation.setUpdateBy(userName);
                outExcessLocation.setUpdateTime(currentDate);
                wmsBaseLocationMapper.updateWmsBaseLocation(outExcessLocation);

                //移库出库任务
                agvRawOutstock(wmsAgvRawOutstockVo, frontLocation, moveMaterialBarcode, moveMaterialId, WmsConstants.WMS_RAW_OUTSTOCK_TASK_TYPE_MOVE,
                        userName, currentDate);
                WmsWcsVo moveOutWcsVo = new WmsWcsVo();
                moveOutWcsVo.setOrderNumer(1);
                wmsWcsVos.add(moveOutWcsVo);

                //移库入库任务
                agvRawInstock(wmsAgvRawOutstockVo, outExcessLocation, moveMaterialBarcode, moveMaterialId,
                        userName, currentDate);
                WmsWcsVo moveInWcsVo = new WmsWcsVo();
                moveInWcsVo.setOrderNumer(2);
                wmsWcsVos.add(moveInWcsVo);

                frontLocation.setInstockFlag(WmsConstants.WMS_BASE_LOCATION_INSTOCK_FLAG_YES);//需设置此库位已入库过度库位
            }

            //锁前排库位（自动锁定的库位可以不用更新库存中的占用数量）
            frontLocation.setLocationStatus(WmsConstants.WMS_BASE_LOCATION_STATUS_AUTO_LOCK);
            frontLocation.setUpdateBy(userName);
            frontLocation.setUpdateTime(currentDate);
            wmsBaseLocationMapper.updateWmsBaseLocation(frontLocation);

            //锁出库库位
            outstockLocation.setLocationStatus(WmsConstants.WMS_BASE_LOCATION_STATUS_AUTO_LOCK);
            outstockLocation.setUpdateBy(userName);
            outstockLocation.setUpdateTime(currentDate);

            wmsBaseLocationMapper.updateWmsBaseLocation(outstockLocation);
        }

        WmsRawOutstock agvRawOutstock = agvRawOutstock(wmsAgvRawOutstockVo, outstockLocation, moveMaterialBarcode, moveMaterialId,
                WmsConstants.WMS_RAW_OUTSTOCK_TASK_TYPE_PRODUCTION_PICKING, userName, currentDate);
        WmsWcsVo outWcsVo = new WmsWcsVo();
        outWcsVo.setOrderNumer(3);
        outWcsVo.setTaskId(agvRawOutstock.getRawOutstockId());
        wmsWcsVos.add(outWcsVo);
        return wmsWcsVos;
    }


    private WmsRawOutstock agvRawOutstock(WmsAgvRawOutstockVo wmsAgvRawOutstockVo, WmsBaseLocation oriBaseLocation, String moveMaterialBarcode, Long moveMaterialId,
                                          String taskType, String userName, Date currentDate) {
        WmsRawOutstock rawOutstock = new WmsRawOutstock();
        rawOutstock.setWarehouseId(oriBaseLocation.getWarehouseId());
        rawOutstock.setPlanCode(wmsAgvRawOutstockVo.getPlanCode());
        rawOutstock.setOperationType(WmsConstants.WMS_OPERATION_TYPE_AUTO);
        rawOutstock.setTaskType(taskType);
        rawOutstock.setAuditStatus(WmsConstants.WMS_AUDIT_STATUS_PASS);
        rawOutstock.setExecuteStatus(WmsConstants.WMS_EXECUTE_STATUS_TOEXECUTE);
        rawOutstock.setApplyDate(currentDate);
        rawOutstock.setApplyBy(userName);
        rawOutstock.setCreateBy(userName);
        rawOutstock.setCreateTime(currentDate);

        wmsRawOutstockMapper.insertWmsRawOutstock(rawOutstock);

        WmsRawOutstockDetail rawOutstockDetail = new WmsRawOutstockDetail();
        rawOutstockDetail.setRawOutstockId(rawOutstock.getRawOutstockId());
        rawOutstockDetail.setLocationCode(oriBaseLocation.getLocationCode());
        rawOutstockDetail.setMaterialBarcode(moveMaterialBarcode);
        rawOutstockDetail.setMaterialId(moveMaterialId);
        rawOutstockDetail.setPlanAmount(new BigDecimal(1));//todo： agv数量默认1
        rawOutstockDetail.setExecuteStatus(WmsConstants.WMS_EXECUTE_STATUS_TOEXECUTE);
        rawOutstockDetail.setOutstockWay(WmsConstants.WMS_STOCK_WAY_AGV);
        wmsRawOutstockDetailMapper.insertWmsRawOutstockDetail(rawOutstockDetail);

        return rawOutstock;
    }


    private WmsRawInstock agvRawInstock(WmsAgvRawOutstockVo wmsAgvRawOutstockVo, WmsBaseLocation targetBaseLocation, String moveMaterialBarcode, Long moveMaterialId,
                                        String userName, Date currentDate) {
        WmsRawInstock rawInstock = new WmsRawInstock();
        rawInstock.setWarehouseId(wmsAgvRawOutstockVo.getWarehouseId());
        rawInstock.setOperationType(WmsConstants.WMS_OPERATION_TYPE_AUTO);
//        rawInstock.setInstockType(WmsConstants.RAW_INSTOCK_INSTOCK_TYPE_MOVE);
        rawInstock.setAuditStatus(WmsConstants.WMS_AUDIT_STATUS_PASS);
        rawInstock.setExecuteStatus(WmsConstants.WMS_EXECUTE_STATUS_TOEXECUTE);
        rawInstock.setApplyDate(currentDate);
        rawInstock.setApplyBy(userName);
        rawInstock.setCreateBy(userName);
        rawInstock.setCreateTime(currentDate);

        wmsRawInstockMapper.insertWmsRawInstock(rawInstock);

        WmsRawInstockDetail rawInstockDetail = new WmsRawInstockDetail();
        rawInstockDetail.setRawInstockId(rawInstock.getRawInstockId());
        rawInstockDetail.setLocationCode(targetBaseLocation.getLocationCode());
        rawInstockDetail.setMaterialBarcode(moveMaterialBarcode);
        rawInstockDetail.setMaterialId(moveMaterialId);
        rawInstockDetail.setPlanAmount(new BigDecimal(1));//todo： agv数量默认1
        rawInstockDetail.setExecuteStatus(WmsConstants.WMS_EXECUTE_STATUS_TOEXECUTE);
        rawInstockDetail.setInstockWay(WmsConstants.WMS_STOCK_WAY_AGV);
        wmsRawInstockDetailMapper.insertWmsRawInstockDetail(rawInstockDetail);

        return rawInstock;
    }


    /**
     * 获取空的库位
     * @param warehouseId
     * @return
     */
    public List<WmsBaseLocation> agvReturnAssignLocations(Long warehouseId) {
        WmsBaseLocation queryLocation = new WmsBaseLocation();
        queryLocation.setWarehouseId(warehouseId);
        List<WmsBaseLocation> baseLocations = wmsBaseLocationMapper.selectEmptyWmsBaseLocationList(queryLocation);
        return baseLocations;
    }


    /**
     * agv拆分后的柜体返库
     * @param wmsAgvRawReturnVo
     */
    public void agvReturn(WmsAgvRawReturnVo wmsAgvRawReturnVo) {
        String materialBarcode = wmsAgvRawReturnVo.getMaterialBarcode();
        String locationCode = wmsAgvRawReturnVo.getLocationCode();

//        WmsConstants.WMS_RAW_STOCK_COMPLETE_FLAG_NO;
    }

}
