package com.sw.mes.itemclean.clean.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.google.gson.Gson;
import com.sw.mes.core.annotation.Jinko;
import com.sw.mes.core.api.system.StoreFeignClients;
import com.sw.mes.core.api.system.YbFeignClients;
import com.sw.mes.core.context.BaseContextHandler;
import com.sw.mes.core.dto.*;
import com.sw.mes.core.dto.sap.RecycledReportSapRecordDto;
import com.sw.mes.core.dto.sap.ReportSapChargItemDto;
import com.sw.mes.core.dto.sap.ReportSapGoodsmvtDto;
import com.sw.mes.core.dto.store.WarehouseAllotBillDetailDto;
import com.sw.mes.core.dto.store.WarehouseAllotBillDto;
import com.sw.mes.core.dto.store.WarehouseInputBillBo;
import com.sw.mes.core.dto.store.WarehouseInputBillDetailBo;
import com.sw.mes.core.enums.*;
import com.sw.mes.core.exception.BusinessException;
import com.sw.mes.core.response.Result;
import com.sw.mes.core.response.ResultCode;
import com.sw.mes.core.utils.BeanUtil;
import com.sw.mes.core.utils.SpringUtils;
import com.sw.mes.core.utils.ThreadManager;
import com.sw.mes.itemclean.clean.domain.CleaningProcessFlow;
import com.sw.mes.itemclean.clean.domain.MaterialStoreSignBox;
import com.sw.mes.itemclean.clean.domain.MaterialUnpacking;
import com.sw.mes.itemclean.clean.dto.BoxPropertyDto;
import com.sw.mes.itemclean.clean.dto.GeneralScanBoxNoParam;
import com.sw.mes.itemclean.clean.dto.GroupStoreDetailDto;
import com.sw.mes.itemclean.clean.dto.MaterialStoreSignBoxDto;
import com.sw.mes.itemclean.clean.mapper.MaterialStoreSignBoxMapper;
import com.sw.mes.itemclean.clean.mapping.ScanBoxNoMapping;
import com.sw.mes.itemclean.clean.service.CleaningProcessFlowService;
import com.sw.mes.itemclean.clean.service.MaterialStoreSignBoxService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sw.mes.itemclean.clean.service.ScanBoxNoService;
import com.sw.mes.redis.server.RedissonUtils;
import org.apache.commons.lang3.StringUtils;
import org.modelmapper.ModelMapper;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

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

/**
 * <p>
 * 打签箱待存（待扫码领料） 服务实现类
 * </p>
 *
 * @author liuzemin
 * @since 2022-11-10
 */
@Service
public class MaterialStoreSignBoxServiceImpl extends ServiceImpl<MaterialStoreSignBoxMapper, MaterialStoreSignBox> implements MaterialStoreSignBoxService {

    @Autowired
    private MaterialStoreSignBoxMapper materialStoreSignBoxMapper;

    @Autowired
    private RedissonClient redisClient;

    @Autowired
    private CleaningProcessFlowService cleaningProcessFlowService;

    @Autowired
    private StoreFeignClients storeFeignClients;

    @Autowired
    YbFeignClients ybFeignClients;

    @Autowired
    ModelMapper modelMapper;

    final String lockKey = "clean_discount_box_";

    /**
     * @param boxNos 箱号
     * @return
     * @throws
     * @title 扣减箱子统一用这个方法
     * @description 原子的，避免重扣
     * @author liuzemin
     * @updateTime 2022/11/10 16:48
     */
    @Override
    @Transactional(rollbackFor = Throwable.class)
    public void discountBox(List<String> boxNos) {
        for (String boxNo : boxNos) {
            RLock lock = RedissonUtils.getRLock(redisClient, lockKey + boxNo);
            boolean isLock = RedissonUtils.getRLockExpireTime(lock, 5L);
            if (isLock) {
                try {
                    LambdaQueryWrapper<MaterialStoreSignBox> queryWrapper = new LambdaQueryWrapper<>();
                    queryWrapper.eq(MaterialStoreSignBox::getMaterialBoxNo, boxNo);
//                    wrapper.eq(MaterialStoreSignBox::getPickingStatus, 1);
                    queryWrapper.last("limit 1");
                    MaterialStoreSignBox box = getOne(queryWrapper);
                    if (box != null) {
                        materialStoreSignBoxMapper.deleteById(box.getId());
                    } else {
                        throw new RuntimeException("箱号：" + boxNo + "，被占用！");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                    throw new RuntimeException(e);
                } finally {
                    RedissonUtils.unRLock(lock);
                }
            } else {
                throw new RuntimeException("有其他人操作箱号：" + boxNo + "，请稍后重试！");
            }
        }
    }

    /**
     * @param param 箱号  与  扫描名称 {com.sw.mes.itemclean.clean.mapping.ScanBoxNoMapping.types.key}
     * @return 箱子属性
     * @throws
     * @title 通用扫箱号方法
     * @description
     * @author liuzemin
     * @updateTime 2022/11/11 10:04
     */
    @Override
    public BoxPropertyDto generalScanBoxNo(GeneralScanBoxNoParam param) {
        Class<ScanBoxNoService> klass = ScanBoxNoMapping.get(param.getScanName());
        if (klass == null) {
            throw new IllegalArgumentException(param.getScanName() + "--扫描方法不存在");
        }
        ScanBoxNoService scanBoxNoService = SpringUtils.getBean(klass);
        return scanBoxNoService.scanBoxNo(param);
    }

    @Override
    public MaterialStoreSignBox getByBoxNo(String materialBoxNo) {
        LambdaQueryWrapper<MaterialStoreSignBox> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MaterialStoreSignBox::getMaterialBoxNo, materialBoxNo);
        queryWrapper.last("limit 1");
        return getOne(queryWrapper);
    }

    @Override
    public MaterialStoreSignBox getByBoxNo(MaterialStoreSignBox materialStoreSignBox) {
        LambdaQueryWrapper<MaterialStoreSignBox> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MaterialStoreSignBox::getMaterialBoxNo, materialStoreSignBox.getMaterialBoxNo());
        queryWrapper.eq(MaterialStoreSignBox::getPickingStatus, 1);
        queryWrapper.in(MaterialStoreSignBox::getMaterialType, 1, 2, 4);
        queryWrapper.last("limit 1");

        return getOne(queryWrapper);
    }

    @Override
    public void updateByBoxNo(String materialBoxNo) {
        materialStoreSignBoxMapper.updateByBoxNo(materialBoxNo);
    }

    @Override
    public MaterialStoreSignBox getByKbxBoxNo(String boxNo) {
        LambdaQueryWrapper<MaterialStoreSignBox> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MaterialStoreSignBox::getBoxNo, boxNo);
        queryWrapper.eq(MaterialStoreSignBox::getDelFlag, 0);
        queryWrapper.eq(MaterialStoreSignBox::getPickingStatus, ItemCleanEnums.MaterialPickStatus.已领料.getKey());
        queryWrapper.eq(MaterialStoreSignBox::getUseStatus, ItemCleanEnums.MaterialUseStatus.未使用.getKey());
        queryWrapper.eq(MaterialStoreSignBox::getStatus, ItemCleanEnums.MaterialStatus.已接收.getKey());
        queryWrapper.orderByDesc(MaterialStoreSignBox::getCreateTime);
        queryWrapper.last("limit 1");
        return getOne(queryWrapper);
    }

    @Override
    public MaterialStoreSignBox getByProductBatchNo(String productBatchNo) {
        LambdaQueryWrapper<MaterialStoreSignBox> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MaterialStoreSignBox::getProductBatchNo, productBatchNo);
        queryWrapper.eq(MaterialStoreSignBox::getDelFlag, 0);
        queryWrapper.eq(MaterialStoreSignBox::getPickingStatus, ItemCleanEnums.MaterialPickStatus.已领料.getKey());
        queryWrapper.eq(MaterialStoreSignBox::getUseStatus, ItemCleanEnums.MaterialUseStatus.未使用.getKey());
        queryWrapper.eq(MaterialStoreSignBox::getStatus, ItemCleanEnums.MaterialStatus.已接收.getKey());
        queryWrapper.last("limit 1");
        return getOne(queryWrapper);
    }

    /**
     * 获取 已领料,已接收,已使用过的物料信息
     *
     * @param productBatchNo
     * @return
     */
    @Override
    public MaterialStoreSignBox getUsedInfoByProductBatchNo(String productBatchNo) {
        LambdaQueryWrapper<MaterialStoreSignBox> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MaterialStoreSignBox::getProductBatchNo, productBatchNo);
        queryWrapper.eq(MaterialStoreSignBox::getDelFlag, 0);
        queryWrapper.eq(MaterialStoreSignBox::getPickingStatus, ItemCleanEnums.MaterialPickStatus.已领料.getKey());
        queryWrapper.in(MaterialStoreSignBox::getUseStatus, ItemCleanEnums.MaterialUseStatus.已使用.getKey(), ItemCleanEnums.MaterialUseStatus.已分选.getKey(), ItemCleanEnums.MaterialUseStatus.已清空.getKey());
        queryWrapper.eq(MaterialStoreSignBox::getStatus, ItemCleanEnums.MaterialStatus.已接收.getKey());
        queryWrapper.last("limit 1");
        return getOne(queryWrapper);
    }

    /**
     * 审核待领料数据,发送到线边仓
     *
     * @param entity
     * @param status 审核状态 0 待审核 1 已接收 2已拒收
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void examineStatus(MaterialStoreSignBox entity, Integer status) {
        if (ItemCleanEnums.MaterialStatus.已接收.getKey() == status) {

            //1 调用线边仓调拨单接口和SAP调拨接口
            handleWarehouseCodeMoveNextWarehouseCode(entity);
            //2 直接更新
            entity.setStatus(status);
            entity.setPickingStatus(ItemCleanEnums.MaterialPickStatus.已领料.getKey());
            entity.setUpdateTime(LocalDateTime.now());
            entity.setUpdateUser(BaseContextHandler.getUsername());
            entity.setExamineTime(LocalDateTime.now());
            entity.setExamineUser(BaseContextHandler.getUsername());
            materialStoreSignBoxMapper.updateById(entity);

            //3 记录批次站点
            cleaningProcessFlowService.inStation(entity.getProductBatchNo(), entity.getProductBatchNo(), entity.getWeight(), entity.getUnusedWeight(), entity.getUsedWeight(), null,
                    EnumValuesFactory.CleaningWipStorageCode.分选.getValue(), null, entity.getBoxNo(), EnumValuesFactory.CleaningWipStorageCode.领料.getValue(), EnumValuesFactory.CleaningWipStorageStatus.工序待进站.getKey());
        } else if (ItemCleanEnums.MaterialStatus.已拒收.getKey() == status) {
            //1 拒收,调用线边仓退库
            //1.1 查询余料对应的工单,查询仓位
            Result<RecycledMaterialManageDto> manageDtoResult = ybFeignClients.getByProduceBatchNo(entity.getProductBatchNo());
            if (manageDtoResult.getCode() != ResultCode.SUCCESS.getCode() || null == manageDtoResult.getData()) {
                log.error(entity.getProductBatchNo() + "回收料查询异常");
                throw new RuntimeException("回收料查询异常：" + manageDtoResult.getMessage());
            }
            //查询余料对应的工单
            RecycledMaterialManageDto recycledMaterialManage = manageDtoResult.getData();
            if (manageDtoResult.getCode() != ResultCode.SUCCESS.getCode() || null == manageDtoResult.getData()) {
                log.error(entity.getProductBatchNo() + "回收料查询异常");
                throw new RuntimeException("回收料查询异常：" + manageDtoResult.getMessage());
            }
            String workOrderNo = recycledMaterialManage.getWorkOrderNo();

            //1.2获取应该退回的仓位
            HashMap<String, String> wareHouseCodeMapByWorkOrderNo = getWareHouseCodeMapByWorkOrderNo(workOrderNo);
            String warehouseCode = wareHouseCodeMapByWorkOrderNo.get("warehouseCode");
            Result<String> result = storeFeignClients.automaticInputOut(newInputBill(entity, ItemCleanEnums.MaterialInputWarehouse.清洗硅料退库.getValue(), warehouseCode));
            Integer code = result.getCode();
            String data = result.getData();
            if (code != 200 || StringUtils.isBlank(data)) {
                throw new BusinessException("清洗硅料退库失败，状态码：" + code + "，反馈信息：" + result.getMessage());
            }
            //2 直接更新
            entity.setStatus(status);
            entity.setUpdateTime(LocalDateTime.now());
            entity.setUpdateUser(BaseContextHandler.getUsername());
            entity.setExamineTime(LocalDateTime.now());
            entity.setExamineUser(BaseContextHandler.getUsername());
            materialStoreSignBoxMapper.updateById(entity);
        }
    }


    /**
     * 获取余料调拨库位
     * 成品仓->下个工序的副产品材料仓
     *
     * @param workOrderNo 计划工单
     * @return
     */
    @Jinko(author = "yyl", value = "获取余料调拨库位")
    private HashMap<String, String> getWareHouseCodeMapByWorkOrderNo(String workOrderNo) {
        //2.1 报工副产品产出到SAP
        //通过计划工单中的片区field4找到字典中的成品仓
        Result<ProductionOrderDto> productionOrder = ybFeignClients.getListByAufnr(workOrderNo);
        String field4 = "";
        String warehouseCode = "";
        String nextWarehouseCode = "";
        if (productionOrder.getCode() != ResultCode.SUCCESS.getCode() || null == productionOrder.getData()) {
            throw new RuntimeException("工单查询异常：" + productionOrder.getMessage());
        }
        ProductionOrderDto orderData = productionOrder.getData();

        if (null != orderData) {
            field4 = orderData.getField4();
        }
        if (null != field4) {
            Result<DictionaryItemDto> dictionaryItemDtoResult = ybFeignClients.findNameByThemeValueAndItemValue("ByProductWarehouseLocation", field4);
            if (null != dictionaryItemDtoResult && null != dictionaryItemDtoResult.getData() && ResultCode.SUCCESS.getCode() == dictionaryItemDtoResult.getCode()) {
                //成品仓
                warehouseCode = dictionaryItemDtoResult.getData().getExtendValue1();
                //下个工序的副产品材料仓
                nextWarehouseCode = dictionaryItemDtoResult.getData().getName();
            } else {
                throw new BusinessException(dictionaryItemDtoResult.getMessage() + "详情: 关键字[ByProductWarehouseLocation]中的[工作中心编码]: " + field4 + " 未配置");
            }
        }
        HashMap<String, String> stringStringHashMap = new HashMap<>();
        stringStringHashMap.put("warehouseCode", warehouseCode);
        stringStringHashMap.put("nextWarehouseCode", nextWarehouseCode);
        return stringStringHashMap;
    }

    @Override
    public MaterialStoreSignBox checkBox(String boxNo) {
        LambdaQueryWrapper<MaterialStoreSignBox> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MaterialStoreSignBox::getBoxNo, boxNo);
        queryWrapper.in(MaterialStoreSignBox::getUseStatus, ItemCleanEnums.MaterialUseStatus.未使用.getKey(), ItemCleanEnums.MaterialUseStatus.已使用.getKey(), ItemCleanEnums.MaterialUseStatus.已分选.getKey());
        queryWrapper.in(MaterialStoreSignBox::getStatus, ItemCleanEnums.MaterialStatus.待审核.getKey(), ItemCleanEnums.MaterialStatus.已接收.getKey());
        queryWrapper.orderByDesc(MaterialStoreSignBox::getCreateTime);
        queryWrapper.last("limit 1");
        return getOne(queryWrapper);
    }

    @Override
    public MaterialStoreSignBox useInfoByMaterialProductBatchNo(String productBatchNo) {
        LambdaQueryWrapper<MaterialStoreSignBox> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MaterialStoreSignBox::getProductBatchNo, productBatchNo);
        queryWrapper.eq(MaterialStoreSignBox::getDelFlag, 0);
        queryWrapper.eq(MaterialStoreSignBox::getPickingStatus, ItemCleanEnums.MaterialPickStatus.已领料.getKey());
        queryWrapper.in(MaterialStoreSignBox::getUseStatus, ItemCleanEnums.MaterialUseStatus.未使用.getKey(),
                ItemCleanEnums.MaterialUseStatus.已使用.getKey(), ItemCleanEnums.MaterialUseStatus.已分选.getKey());
        queryWrapper.eq(MaterialStoreSignBox::getStatus, ItemCleanEnums.MaterialStatus.已接收.getKey());
        queryWrapper.last("limit 1");
        return getOne(queryWrapper);
    }

    @Override
    public List<MaterialStoreSignBox> getByParentId(Integer parentId) {
        LambdaQueryWrapper<MaterialStoreSignBox> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(MaterialStoreSignBox::getParentId, parentId);
        queryWrapper.eq(MaterialStoreSignBox::getDelFlag, 0);
        queryWrapper.eq(MaterialStoreSignBox::getPickingStatus, ItemCleanEnums.MaterialPickStatus.已领料.getKey());
        queryWrapper.eq(MaterialStoreSignBox::getStatus, ItemCleanEnums.MaterialStatus.已接收.getKey());
        queryWrapper.eq(MaterialStoreSignBox::getUseStatus, ItemCleanEnums.MaterialUseStatus.已合批.getKey());
        return this.list(queryWrapper);
    }


    /**
     * 清洗硅料退库
     *
     * @param entity
     * @param reason
     * @param warehouseCode
     * @return
     */
    private WarehouseInputBillBo newInputBill(MaterialStoreSignBox entity, String reason, String warehouseCode) {
        WarehouseInputBillBo inputBill = new WarehouseInputBillBo();
        inputBill.setType(ItemCleanEnums.MaterialInputWarehouse.清洗硅料退库.getValue());
        inputBill.setRemark(reason);
//        inputBill.setWarehouseCode(warehouseCode);
        inputBill.setAutoAudit(true);
        //入库物料明细
        List<WarehouseInputBillDetailBo> inputBillDetails = new ArrayList<>();
        WarehouseInputBillDetailBo dto = new WarehouseInputBillDetailBo();

        //组装物料明细数据
        dto.setBatchNo(entity.getProductBatchNo());
        dto.setDateProduceBatchNo(entity.getDateProduceBatchNo());
        dto.setCrystalNo(entity.getDateProduceBatchNo());
        dto.setCount(entity.getWeight());
        dto.setSupplier(entity.getSupplier());
        dto.setSupplierCode(entity.getSupplierCode());
        //字段需要与物料主数据对齐
        Result<MaterialModelDto> result = ybFeignClients.getMaterialByCode(entity.getMaterialCode());
        MaterialModelDto materialModel = result.getData();
        if (materialModel == null) {
            throw new BusinessException("【" + entity.getMaterialCode() + "】物料主数据不存在，请检查数据！");
        }

        dto.setMaterialModelId(Long.valueOf(materialModel.getId()));
        dto.setMaterialModelCode(materialModel.getCode());
        dto.setMaterialModelName(materialModel.getName());
        dto.setMaterialTypeName(materialModel.getMaterialTypeDto().getName());
        dto.setMaterialTh(materialModel.getNumberOfDrawings());
        dto.setUnitName(materialModel.getUnitDto().getName());
        dto.setSpecification(materialModel.getSpecification());
        dto.setWarehouseCode(warehouseCode);
        inputBillDetails.add(dto);
        inputBill.setInputBillDetails(inputBillDetails);
        return inputBill;
    }


    /**
     * 余料收集,审核通过后副产品调拨,供上报sap使用
     *
     * @param workOrderNo         工单
     * @param materialCode        物料编码
     * @param produceBatchNo      产品批次
     * @param weight              数量(重量)
     * @param unitName            单位(KG,PC)
     * @param warehouseCode       库存地点编码
     * @param nextWarehouseCode   去往库存地点编码
     * @param factoryWorkTypeName 工序名称
     */
    @Jinko(author = "yyl", value = "余料收集,审核通过后副产品调拨,供上报sap使用")
    private void handleReportSapGoodsmvtDto(String workOrderNo, String materialCode, String produceBatchNo, BigDecimal weight, String unitName, String warehouseCode, String nextWarehouseCode, String factoryWorkTypeName) {
        ReportSapGoodsmvtDto reportSapGoodsmvtDto = new ReportSapGoodsmvtDto();
        List<ReportSapChargItemDto> reportSapChargItemDtos = new ArrayList<>();
        ReportSapChargItemDto reportSapChargItemDto = new ReportSapChargItemDto();
//        reportSapChargItemDto.setAtnam("P01");
//        reportSapChargItemDto.setAtwrt("JK1");
        reportSapChargItemDtos.add(reportSapChargItemDto);
        reportSapGoodsmvtDto.setChargItems(reportSapChargItemDtos);
        reportSapGoodsmvtDto.setAufnr(null);
        reportSapGoodsmvtDto.setCharg(produceBatchNo);
        reportSapGoodsmvtDto.setBwart("311");
        reportSapGoodsmvtDto.setWerks("5041");
        reportSapGoodsmvtDto.setUmcha(produceBatchNo);
        reportSapGoodsmvtDto.setUmwrk("5041");
        reportSapGoodsmvtDto.setMatnr(materialCode);
        reportSapGoodsmvtDto.setMenge(weight == null ? "0" : weight.toString());
        reportSapGoodsmvtDto.setMeins(unitName);
        reportSapGoodsmvtDto.setZfcpFlag("X");
        reportSapGoodsmvtDto.setLgort(warehouseCode);
        reportSapGoodsmvtDto.setUmlgo(nextWarehouseCode);
        reportSapGoodsmvtDto.setProcName(factoryWorkTypeName);
        ybFeignClients.sendSapGoodsmvt(reportSapGoodsmvtDto);
        doMesToSapRecord(reportSapGoodsmvtDto, MesToSapTypeEnum.余料调拨.getCode());
    }

    @Jinko(author = "yyl", value = "硅料审核通过后副产品调拨线边仓和sap,供上报sap使用")
    private void handleWarehouseCodeMoveNextWarehouseCode(MaterialStoreSignBox entity) {
        Result<MaterialModelDto> result = ybFeignClients.getMaterialByCode(entity.getMaterialCode());
        MaterialModelDto materialModel = result.getData();
        if (materialModel == null) {
            throw new BusinessException("【" + entity.getMaterialCode() + "】物料主数据不存在，请检查数据！");
        }
        String dateProduceBatchNo = entity.getDateProduceBatchNo();
        String workOrderNo = entity.getAufnr();

        //获取调拨的起始仓和终止仓库位编码
        HashMap<String, String> wareHouseCodeMapByWorkOrderNo = getWareHouseCodeMapByWorkOrderNo(workOrderNo);
        String warehouseCode = wareHouseCodeMapByWorkOrderNo.get("warehouseCode");
        String nextWarehouseCode = wareHouseCodeMapByWorkOrderNo.get("nextWarehouseCode");

        WarehouseAllotBillDto warehouseAllotBillDto = new WarehouseAllotBillDto();
        List<WarehouseAllotBillDetailDto> allotBillDetailDtos = new ArrayList<>();
        WarehouseAllotBillDetailDto warehouseAllotBillDetailDto = new WarehouseAllotBillDetailDto();
        warehouseAllotBillDetailDto.setWarehouseId(null);
        warehouseAllotBillDetailDto.setBatchNo(entity.getProductBatchNo());
        warehouseAllotBillDetailDto.setCrystalNo(entity.getDateProduceBatchNo());
        warehouseAllotBillDetailDto.setCount(entity.getWeight());
        // warehouseAllotBillDetailDto.setMaterialModelCode(entity.getMaterialCode());
        // warehouseAllotBillDetailDto.setMaterialModelName(entity.getMaterialName());
        warehouseAllotBillDetailDto.setMaterialModelId(Long.valueOf(materialModel.getId()));
        warehouseAllotBillDetailDto.setMaterialModelCode(materialModel.getCode());

        warehouseAllotBillDetailDto.setMaterialModelName(materialModel.getName());
        if (null != materialModel.getMaterialTypeDto()) {
            warehouseAllotBillDetailDto.setMaterialTypeName(materialModel.getMaterialTypeDto().getName());
        }
        if (null != materialModel.getUnitDto()) {
            warehouseAllotBillDetailDto.setUnitName(materialModel.getUnitDto().getName());
        }
        warehouseAllotBillDetailDto.setSupplier(materialModel.getSupplier());
        warehouseAllotBillDetailDto.setSpecification(materialModel.getSpecification());
        warehouseAllotBillDetailDto.setMaterialTh(materialModel.getNumberOfDrawings());
        Result<WarehouseModelDto> codeResult = storeFeignClients.getCode(warehouseCode);
        warehouseAllotBillDetailDto.setWarehouseCode(warehouseCode);
        if (codeResult.getCode() == ResultCode.SUCCESS.getCode()) {
            warehouseAllotBillDetailDto.setWarehouseId(codeResult.getData().getId());
        }
        warehouseAllotBillDetailDto.setAllotWarehouseCode(nextWarehouseCode);
        allotBillDetailDtos.add(warehouseAllotBillDetailDto);
        warehouseAllotBillDto.setAllotBillDetailDtos(allotBillDetailDtos);
        warehouseAllotBillDto.setCreateUserName(BaseContextHandler.getUsername());
        warehouseAllotBillDto.setCreateUserId(BaseContextHandler.getUserID());
        warehouseAllotBillDto.setAutoAudit(true);
        warehouseAllotBillDto.setAllotType(RecycleAutoAllotWarehouseEnum.硅料清洗审核调拨.getDesc());
        Result allotBill = storeFeignClients.createAllotBill(warehouseAllotBillDto);
        if (allotBill.getCode() != ResultCode.SUCCESS.getCode()) {
            log.error("硅料审核通过,入库线边仓出库失败:{}" + allotBill.getMessage());
            throw new BusinessException("硅料审核通过,入库线边仓出库失败，" + allotBill.getMessage());
        }
        //1.2调拨sap
        String factoryWorkTypeName = getFactoryWorkTypeNameByWorkOrderNo(workOrderNo);
        factoryWorkTypeName = factoryWorkTypeName + RecycleCleanSAPEnum.余料调拨.getDesc();
        //因为调拨这里不使用 工单workOrderNo,所以无需像余料发起入库一样,判断是否是拉晶,包含详情,具体属于哪个工单上报给SAP
        try {
            handleReportSapGoodsmvtDto(workOrderNo, entity.getMaterialCode(), dateProduceBatchNo, entity.getWeight(), warehouseAllotBillDetailDto.getUnitName(), warehouseCode, nextWarehouseCode, factoryWorkTypeName);
        } catch (Exception e) {
            log.error("废料上报异常:" + e.getMessage());
        }
    }

    /**
     * 通过工单号获取对应成本中心名称
     * 硅料1
     * 拉晶2
     * 截断3
     * 刀剖4
     * 籽晶5
     *
     * @param workOrderNo 工单号
     * @return
     */
    @Jinko(author = "yyl", value = "通过工单号获取对应成本中心名称")
    private String getFactoryWorkTypeNameByWorkOrderNo(String workOrderNo) {
        Result<ProductionOrderDto> productionOrder = ybFeignClients.getListByAufnr(workOrderNo);
        if (productionOrder.getCode() != ResultCode.SUCCESS.getCode() || null == productionOrder.getData()) {
            throw new RuntimeException("工单查询异常：" + productionOrder.getMessage());
        }
        ProductionOrderDto orderData = productionOrder.getData();
        if (null == orderData) {
            throw new BusinessException(workOrderNo + "工单不存在!");
        }
        String workType = String.valueOf(orderData.getWorkType());
        //查询成本中心名称
        String typeName = "";
        Result<DictionaryItemDto> dictionaryItemDtoResult = ybFeignClients.findNameByThemeValueAndItemValueAndEnable("factoryWorkType", workType, true);
        if (null != dictionaryItemDtoResult && null != dictionaryItemDtoResult.getData() && ResultCode.SUCCESS.getCode() == dictionaryItemDtoResult.getCode()) {
            typeName = dictionaryItemDtoResult.getData().getName();
        } else {
            log.error("详情: 关键字[factoryWorkType]中的[词条值]: " + workType + " 未配置");
        }
        return typeName;
    }

    /**
     * 记录MES发送到SAP的过程数据
     *
     * @param reportSapGoodsmvtDto
     * @param recordType
     */
    public void doMesToSapRecord(ReportSapGoodsmvtDto reportSapGoodsmvtDto, Integer recordType) {
        ThreadManager.getThreadPollProxy().execute(() -> {
            try {
                //异步记录日志
                RecycledReportSapRecordDto recycledReportSapRecord = fromRecordDto(reportSapGoodsmvtDto);
                recycledReportSapRecord.setRecordType(recordType);
                ybFeignClients.sendRecycledSapRecord(recycledReportSapRecord);
            } catch (Exception e) {
                log.error("记录mes发送SAP的余料副产品收货记录失败:" + e.getMessage());
            }
        });
    }

    /**
     * 合并料箱功能实现。
     * 将多个料箱中的物料合并到一个料箱中，并更新相关数据。
     *
     * @param dtos 合并操作涉及的料箱数据传输对象列表，每个对象包含料箱的相关信息。
     * @throws RuntimeException 当满足以下条件之一时抛出异常：
     *                          - 批次号重复；
     *                          - 批次号对应的料箱不存在；
     *                          - 存在不同物料编码的物料；
     *                          - 存在不同日生产批次号的物料；
     *                          - 绑定数据小于2条，不允许合批操作。
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public void blending(List<MaterialStoreSignBoxDto> dtos) {
        if (dtos != null && dtos.size() > 0) {
            List<String> productBatchNos = new ArrayList<>();
            productBatchNos = dtos.stream().map(temp -> temp.getProductBatchNo()).collect(Collectors.toList());
            HashSet<String> hashSet = new HashSet<>(productBatchNos);
            if (productBatchNos.size() != hashSet.size()) {
                throw new RuntimeException("批次号重复!");
            }
            //物料编码
            String materialCode;
            String productBatchNo = dtos.get(0).getProductBatchNo();
            MaterialStoreSignBox storeSignBox = this.getByProductBatchNo(productBatchNo);
            if (null == storeSignBox) {
                throw new RuntimeException("不存在批次号为【" + productBatchNo + "】的领料未使用数据");
            }
            //通过批次号去material_store_sign_box里面查询aufnr
            List<String> collect = dtos.stream().map(MaterialStoreSignBoxDto::getProductBatchNo).collect(Collectors.toList());
            List<MaterialStoreSignBox> storeSignBoxList = this.getMaterialStoreSignBoxByProductBatchNo(collect);
            Set<String> aufnrSet = storeSignBoxList.stream().map(MaterialStoreSignBox::getAufnr).collect(Collectors.toSet());
            if (aufnrSet.size() != 1) {
                //多工单的时候,校验工单对应定向标识是否相同
                checkOrderInfo(aufnrSet);
            }
            for (MaterialStoreSignBoxDto dto : dtos) {
                MaterialStoreSignBox materialStoreSignBox = null;
                materialStoreSignBox = this.getByProductBatchNo(dto.getProductBatchNo());
                if (null == materialStoreSignBox) {
                    throw new RuntimeException("不存在批次号为【" + dto.getProductBatchNo() + "】的领料未使用数据");
                }
                materialCode = storeSignBox.getMaterialCode();
                if (!materialStoreSignBox.getMaterialCode().equals(materialCode)) {
                    throw new RuntimeException("存在" + materialCode + "和 " + materialStoreSignBox.getMaterialCode() + " 不同物料编码的物料,无法合批");
                }
                String dateProduceBatchNo = storeSignBox.getDateProduceBatchNo();
                if (!materialStoreSignBox.getDateProduceBatchNo().equals(dateProduceBatchNo)) {
                    throw new RuntimeException("存在" + dateProduceBatchNo + "和 " + materialStoreSignBox.getDateProduceBatchNo() + " 不同日生产批次号的物料,无法合批");
                }
            }
            //总重量
            BigDecimal quantityClaimed = dtos.stream().map(dto -> dto.getWeight()).reduce(BigDecimal.ZERO, BigDecimal::add);
            //用重量最大的当做合并物料的新箱号
            MaterialStoreSignBoxDto materialStoreSignBoxDto = dtos.stream().max(Comparator.comparing(MaterialStoreSignBoxDto::getWeight)).get();
            MaterialStoreSignBox blendingSignBox = this.getByProductBatchNo(materialStoreSignBoxDto.getProductBatchNo());
            blendingSignBox.setWeight(quantityClaimed);
            blendingSignBox.setUnusedWeight(quantityClaimed);
            blendingSignBox.setUsedWeight(BigDecimal.ZERO);
            blendingSignBox.setUpdateTime(LocalDateTime.now());
            blendingSignBox.setUpdateUser(BaseContextHandler.getUsername());
            this.updateById(blendingSignBox);

            //其他被合批箱子要置空,变更状态为使用
            for (MaterialStoreSignBoxDto signBoxDto : dtos) {
                if (!signBoxDto.getBoxNo().equals(blendingSignBox.getBoxNo())) {
                    String useProductBatchNo = signBoxDto.getProductBatchNo();
                    MaterialStoreSignBox useSignBox = this.getByProductBatchNo(useProductBatchNo);
                    if (null != useSignBox) {
                        //查找被合批的id是否存在关联合批数据
                        List<MaterialStoreSignBox> list = this.getByParentId(useSignBox.getId());
                        if (CollectionUtil.isNotEmpty(list)) {
                            for (MaterialStoreSignBox signBox : list) {
                                signBox.setParentId(blendingSignBox.getId());
                                signBox.setUpdateTime(LocalDateTime.now());
                                signBox.setUpdateUser(BaseContextHandler.getUsername());
                                signBox.setBlendingUser(BaseContextHandler.getUsername());
                                signBox.setBlendingTime(LocalDateTime.now());
                                signBox.setBlendingProductBatchNo(blendingSignBox.getProductBatchNo());
                                this.updateById(signBox);
                            }
                        }
                        useSignBox.setUsedWeight(useSignBox.getWeight());
                        useSignBox.setUnusedWeight(BigDecimal.ZERO);
                        useSignBox.setUseStatus(ItemCleanEnums.MaterialUseStatus.已合批.getKey());
                        useSignBox.setParentId(blendingSignBox.getId());
                        useSignBox.setUpdateTime(LocalDateTime.now());
                        useSignBox.setUpdateUser(BaseContextHandler.getUsername());
                        useSignBox.setBlendingUser(BaseContextHandler.getUsername());
                        useSignBox.setBlendingTime(LocalDateTime.now());
                        useSignBox.setBlendingProductBatchNo(blendingSignBox.getProductBatchNo());
                        this.updateById(useSignBox);
                    }
                }
            }
        } else {
            throw new RuntimeException("绑定数据小于2条,不允许合批操作!");
        }
    }

    @Override
    public List<MaterialStoreSignBox> getMaterialStoreSignBoxByProductBatchNo(List<String> productBatchNos) {
        LambdaQueryWrapper<MaterialStoreSignBox> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.in(MaterialStoreSignBox::getProductBatchNo, productBatchNos);
        return this.list(queryWrapper);
    }

    public RecycledReportSapRecordDto fromRecordDto(ReportSapGoodsmvtDto dto) {
        RecycledReportSapRecordDto reportSapGoodsmvtDto = new RecycledReportSapRecordDto();
        reportSapGoodsmvtDto.setChargItems(dto.getChargItems());
        reportSapGoodsmvtDto.setAufnr(dto.getAufnr());
        reportSapGoodsmvtDto.setCharg(dto.getCharg());
        reportSapGoodsmvtDto.setBwart(dto.getBwart());
        reportSapGoodsmvtDto.setWerks(dto.getWerks());
        reportSapGoodsmvtDto.setUmcha(dto.getUmcha());
        reportSapGoodsmvtDto.setUmwrk(dto.getUmwrk());
        reportSapGoodsmvtDto.setMatnr(dto.getMatnr());
        reportSapGoodsmvtDto.setMenge(dto.getMenge());
        reportSapGoodsmvtDto.setMeins(dto.getMeins());
        reportSapGoodsmvtDto.setZfcpFlag(dto.getZfcpFlag());
        reportSapGoodsmvtDto.setLgort(dto.getLgort());
        reportSapGoodsmvtDto.setUmlgo(dto.getUmlgo());
        reportSapGoodsmvtDto.setProcName(dto.getProcName());
        reportSapGoodsmvtDto.setCreateDate(new Date());
        reportSapGoodsmvtDto.setCreateUser(BaseContextHandler.getUsername());
        return reportSapGoodsmvtDto;
    }

    /**
     * 检查订单信息，确保所有订单满足特定条件（有相同的定向标识）。
     *
     * @param aufnrs 一个包含订单号的集合。
     */
    public void checkOrderInfo(Set<String> aufnrs) {
        List<String> list = new ArrayList<String>(aufnrs);
        Result<List<ProductionOrderDto>> listResult = ybFeignClients.getListByAufnrs(list);
        if (listResult.getCode() != ResultCode.SUCCESS.getCode() || null == listResult.getData()) {
            throw new RuntimeException("工单查询异常：" + listResult.getMessage());
        }
        List<ProductionOrderDto> data = listResult.getData();
        if (CollectionUtil.isEmpty(data)) {
            throw new RuntimeException("工单查询异常!");
        } else {
            // 遍历订单数据，如果定向标识为空，则设置为"非定向"。
            data.forEach(entity -> {
                if (com.sw.mes.core.utils.StringUtils.isBlank(entity.getZorientationind())) {
                    entity.setZorientationind("非定向");
                }
            });
            Set<String> zorientationindSet = data.stream().map(ProductionOrderDto::getZorientationind).collect(Collectors.toSet());
            if (zorientationindSet.size() != 1) {
                throw new RuntimeException("不是相同定向标识不能合批");
            }
        }
    }
}
