package cn.estsh.i3plus.ext.mes.pcn.apiservice.serviceimpl.busi;

import cn.estsh.i3plus.ext.mes.pcn.api.base.IMesPartService;
import cn.estsh.i3plus.ext.mes.pcn.api.base.IMesProdOrgExtService;
import cn.estsh.i3plus.ext.mes.pcn.api.base.IMesShiftService;
import cn.estsh.i3plus.ext.mes.pcn.api.busi.*;
import cn.estsh.i3plus.ext.mes.pcn.apiservice.dao.IMesProdRuleSortCfgDao;
import cn.estsh.i3plus.ext.mes.pcn.apiservice.dao.IMesProductionRecordDao;
import cn.estsh.i3plus.ext.mes.pcn.apiservice.util.MesPcnException;
import cn.estsh.i3plus.ext.mes.pcn.pojo.constant.MesCommonConstant;
import cn.estsh.i3plus.ext.mes.pcn.pojo.model.MesWorkOrderExtModel;
import cn.estsh.i3plus.ext.mes.pcn.pojo.model.MesWorkOrderSortReportModel;
import cn.estsh.i3plus.ext.mes.pcn.pojo.util.MesPcnExtConstWords;
import cn.estsh.i3plus.mes.pcn.api.iservice.busi.ISyncFuncService;
import cn.estsh.i3plus.platform.common.convert.ConvertBean;
import cn.estsh.i3plus.platform.common.tool.MathOperation;
import cn.estsh.i3plus.platform.common.tool.TimeTool;
import cn.estsh.i3plus.pojo.base.bean.DdlPackBean;
import cn.estsh.i3plus.pojo.base.bean.ListPager;
import cn.estsh.i3plus.pojo.base.codemaker.SnowflakeIdMaker;
import cn.estsh.i3plus.pojo.base.common.Pager;
import cn.estsh.i3plus.pojo.base.common.PagerHelper;
import cn.estsh.i3plus.pojo.base.enumutil.CommonEnumUtil;
import cn.estsh.i3plus.pojo.base.tool.DdlPreparedPack;
import cn.estsh.i3plus.pojo.base.util.StringUtil;
import cn.estsh.i3plus.pojo.mes.bean.*;
import cn.estsh.i3plus.pojo.mes.model.GenSerialNoModel;
import cn.estsh.i3plus.pojo.mes.repository.*;
import cn.estsh.i3plus.pojo.mes.util.MesExtEnumUtil;
import cn.estsh.impp.framework.boot.exception.ImppBusiException;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.format.DateParser;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @Description :
 * @Reference :
 * @Author :
 * @CreateDate : 2024-05-09 10:54
 * @Modify:
 **/

@Service
@Slf4j
public class MesWorkOrderService implements IMesWorkOrderService {

    public static final Logger LOGGER = LoggerFactory.getLogger(MesWorkOrderService.class);
    @Autowired
    private MesWorkOrderRepository workOrderRepository;

    @Autowired
    private IMesWorkOrderExtService workOrderExtService;

    @Autowired
    private MesProductVersionRepository mesProductVersionRDao;
    @Autowired
    private MesBomRepository mesBomRDao;
    @Autowired
    private MesWorkCenterRepository mesWorkCenterRDao;
    @Autowired
    private MesPartRepository mesPartRDao;
    @Autowired
    private MesProductPlanRepository mesProductPlanRDao;
    @Autowired
    private MesProductOffLineRepository mesProductOffLineRDao;

    @Autowired
    private MesProductionRecordRepository productionRecordRao;
    @Autowired
    private MesWorkOrderRepository mesWorkOrderRDao;
    @Autowired
    private MesProduceSnRepository mesProduceSnRao;
    @Autowired
    private ISyncFuncService syncFuncService;

    @Autowired
    private MesWorkOrderLogRepository mesWorkOrderLogRepository;
    @Autowired
    private MesShiftRepository mesShiftRepository;
    @Autowired
    private IMesConfigService configService;

    @Autowired
    private IMesPartService iMesPartService;

    @Autowired
    private IMesShiftService mesShiftService;

    @Autowired
    private MesPartSapRepository partSapRepository;

    @Autowired
    private IMesProductionRecordService productionRecordService;

    @Autowired
    private SnowflakeIdMaker snowflakeIdMaker;

    @Autowired
    private IMesProdOrgExtService mesProdOrgExtService;

    @Autowired
    private IMesProductVersionService mesProductVersionService;

    @Autowired
    private IMesProductionRecordDao mesProductionRecordDao;

    @Autowired
    private IMesMoveRuleService mesMoveRuleService;

    @Autowired
    private MesWorkOrderPartRepository mesWorkOrderPartRepository;

    @Autowired
    private MesMoveRepository mesMoveRepository;

    @Autowired
    private MesJisShippingRepository mesJisShippingRepository;

    @Autowired
    private IMesProdRuleSortCfgDao mesProdRuleSortCfgDao;

    @Autowired
    private MesPartProdGroupRepository mesPartProdGroupRao;

    @Override
    public MesWorkOrder queryMesWorkOrder(MesWorkOrder workOrder) {

        DdlPackBean packBean = getDdlPackBean(workOrder);
        MesWorkOrder result = workOrderRepository.getByProperty(packBean);

        if (StringUtils.isEmpty(result)) {
            throw new ImppBusiException("查询工单不存在，请检查数据");
        }

        return result;
    }

    @Override
    public MesWorkOrder findMesWorkOrderList(String organizeCode,String workOrderNo) {
        DdlPackBean packBean = DdlPackBean.getDdlPackBean(organizeCode);
        DdlPreparedPack.getStringEqualPack(workOrderNo, "workOrderNo", packBean);
        MesWorkOrder mesWorkOrder = workOrderRepository.getByProperty(packBean);
        if(Objects.isNull(mesWorkOrder)){
            MesPcnException.throwMesBusiException("工单【%s】信息不存在",workOrderNo);
        }
        return mesWorkOrder;
    }

    @Override
    public List<MesWorkOrder> queryMesWorkOrderList(MesWorkOrder workOrder) {

        DdlPackBean packBean = getDdlPackBean(workOrder);

        return workOrderRepository.findByHqlWhere(packBean);
    }

    @Override
    public ListPager<MesWorkOrder> queryMesWorkOrderListByPager(MesWorkOrder workOrder, Pager pager) {


        DdlPackBean packBean = getDdlPackBean(workOrder);
        DdlPreparedPack.getOrderByPack(new Object[]{CommonEnumUtil.ASC_OR_DESC.ASC.getValue()}, new String[]{MesPcnExtConstWords.WORK_ORDER_NO}, packBean);
        pager = PagerHelper.getPager(pager, workOrderRepository.findByHqlWhereCount(packBean));

        List<MesWorkOrder> resultList = workOrderRepository.findByHqlWherePage(packBean, pager);

        return new ListPager<>(resultList, pager);
    }

    private DdlPackBean getDdlPackBean(MesWorkOrder workOrder) {

        DdlPackBean packBean = DdlPackBean.getDdlPackBean(workOrder.getOrganizeCode());
        DdlPreparedPack.getStringLikerPack(workOrder.getWorkCenterCode(), "workCenterCode", packBean);
        DdlPreparedPack.getStringLikerPack(workOrder.getWorkCellCode(), "workCellCode", packBean);
        DdlPreparedPack.getStringLikerPack(workOrder.getWorkOrderNo(), "workOrderNo", packBean);
        DdlPreparedPack.getStringLikerPack(workOrder.getPartNo(), "partNo", packBean);

        if (!StringUtils.isEmpty(workOrder.getWorkOrderStatus()))
            DdlPreparedPack.getNumEqualPack(workOrder.getWorkOrderStatus(), "workOrderStatus", packBean);
        else
            DdlPreparedPack.getInPackList(Stream.of(MesExtEnumUtil.ORDER_STATUS.RELEASE.getValue(), MesExtEnumUtil.ORDER_STATUS.PROCESS.getValue()).collect(Collectors.toList()), "workOrderStatus", packBean);

        DdlPreparedPack.getStringBiggerPack(workOrder.getStartTimeStart(), "planStartTime", packBean);
        DdlPreparedPack.getStringSmallerPack(workOrder.getStartTimeEnd(), "planStartTime", packBean);
        DdlPreparedPack.getStringBiggerPack(workOrder.getEndTimeStart(), "planEndTime", packBean);
        DdlPreparedPack.getStringSmallerPack(workOrder.getEndTimeEnd(), "planEndTime", packBean);

        return packBean;
    }

    @Override
    public void doPcnJobProductReport(List<MesProductionRecord> mesProductionRecordList, List<MesShift> mesShiftList, String organizeCode, String userName, MesWorkOrder mesWorkOrderDb) {

        //查询物料信息
        DdlPackBean ddlPackBeanPart = DdlPackBean.getDdlPackBean(mesWorkOrderDb.getOrganizeCode());
        DdlPreparedPack.getStringEqualPack(mesWorkOrderDb.getPartNo(), "partNo", ddlPackBeanPart);
        MesPart mesPart = mesPartRDao.getByProperty(ddlPackBeanPart);
        if (null == mesPart) {
            MesPcnException.throwMesBusiException("物料【%s】信息不存在", mesWorkOrderDb.getPartNo());
        }

        //查询产线信息
        DdlPackBean ddlPackBean = DdlPackBean.getDdlPackBean(mesWorkOrderDb.getOrganizeCode());
        DdlPreparedPack.getStringEqualPack(mesWorkOrderDb.getWorkCenterCode(), "workCenterCode", ddlPackBean);
        MesWorkCenter mesWorkCenter = mesWorkCenterRDao.getByProperty(ddlPackBean);
        if (Objects.isNull(mesWorkCenter)) {
            MesPcnException.throwMesBusiException("产线【%s】不存在", mesWorkOrderDb.getWorkCenterCode());
        }
        //获取生产版本
        MesProductVersion mesProductVersion = getProductVersion(mesWorkOrderDb.getOrganizeCode(), mesWorkOrderDb.getPartNo(), mesWorkOrderDb.getProductVersion());
        String bomVersion =  mesProductVersion.getAlternativePartList();
        String partProdGroupCode = mesWorkOrderDb.getPartProdGroupCode();
        // partProdGroupCode 不为空，查询的数据不为空，查询数据中的获取bom方式不为空且枚举是 基于默认BOM ，才bomversion = null 否则都是通过生产版本找bom
        if (!StringUtil.isEmpty(partProdGroupCode)) {
            DdlPackBean partProdBean = DdlPackBean.getDdlPackBean(mesWorkOrderDb.getOrganizeCode());
            DdlPreparedPack.getStringEqualPack(partProdGroupCode, "partProdGroupCode", partProdBean);
            List<MesPartProdGroup> mesPartProdGroupList = mesPartProdGroupRao.findByHqlTopWhere(partProdBean, 1);

            // 没有零件生产组用默认找生产版本、有的话找获取bom方式枚举，枚举是基于默认BOM 则直接找
            if (!mesPartProdGroupList.isEmpty()) {
                MesPartProdGroup mesPartProdGroup = mesPartProdGroupList.get(0);
                if (mesPartProdGroup.getEnableBomVersion() != null && mesPartProdGroup.getEnableBomVersion() == MesExtEnumUtil.ENABLE_BOM_VERSION.ENABLE_BOM_VERSION_2.getValue()) {
                    log.info("基于默认BOM获取BOM");
                    bomVersion = "";
                }
            }
        }
        //物料+生产版本获取bom信息
        List<MesBom> mesBoms = findBomList(mesPart.getOrganizeCode(), mesPart, bomVersion);

        LOGGER.info("产线:{} 零件:{}更新工单", mesWorkOrderDb.getWorkCenterCode(), mesWorkOrderDb.getPartNo());
        //更新工单
        // updateMesWorkOrder(mesWorkOrderDb, userName );

        //保存数据
//        List<MesProductOffLine> mesProductOffLineList = new ArrayList<>();
//        for (MesBom mesBom : mesBoms) {
//            mesProductOffLineList.add(creatMesProductOffLine(mesWorkOrder, mesProductVersion, sn, mesBom, false));
//        }
//        mesProductOffLineService.insertBatch(mesProductOffLineList);

        LOGGER.info("产线:{} 零件:{}更新工单mesProduceSns:{}", mesWorkOrderDb.getWorkCenterCode(), mesWorkOrderDb.getPartNo(), mesProductionRecordList.size());
        //保存数据
        List<MesProductOffLine> mesProductOffLineList = new ArrayList<>();
        MesProductOffLine newMesProductOffLine;
        for (MesProductionRecord mesProductionRecord : mesProductionRecordList) {
            for (MesBom mesBom : mesBoms) {

                DdlPackBean productPackBean = DdlPackBean.getDdlPackBean(mesProductionRecord.getOrganizeCode());
                DdlPreparedPack.getStringEqualPack(mesBom.getItemPartNo(),"itemPartNo", productPackBean);
                DdlPreparedPack.getStringEqualPack(mesWorkOrderDb.getPartNo(),"reportPartNo", productPackBean);
                DdlPreparedPack.getStringEqualPack(mesProductionRecord.getProductSn(),"reportSn", productPackBean);
                int count = mesProductOffLineRDao.findByHqlWhereCount(productPackBean);
                if (count > 0) {
                    String msg = String.format("子零件【%s】产成零件【%s】条码【%s】对应报工信息已存在",
                            mesBom.getItemPartNo(),mesWorkOrderDb.getPartNo(), mesProductionRecord.getProductSn());
                    LOGGER.info(msg);
                    continue;
                }

                newMesProductOffLine = new MesProductOffLine();
                newMesProductOffLine.setReportPartNo(mesWorkOrderDb.getPartNo());
                newMesProductOffLine.setReportPartNameRdd(mesWorkOrderDb.getPartName());
                newMesProductOffLine.setItemPartNo(mesBom.getItemPartNo());
                newMesProductOffLine.setItemPartName(mesBom.getItemPartName());
                newMesProductOffLine.setItemQty(mesBom.getItemQty());
                newMesProductOffLine.setAlort(mesProductVersion.getReceiveInventoryPoint());
                newMesProductOffLine.setStgeLoc(mesProductVersion.getShipInventoryPoint());
                newMesProductOffLine.setQty(1d);
                newMesProductOffLine.setReportSn(mesProductionRecord.getProductSn());
                newMesProductOffLine.setBomVersion(mesWorkOrderDb.getProductVersion());
                newMesProductOffLine.setSerialNumber(mesProductionRecord.getSerialNumber());
                newMesProductOffLine.setUnit(mesBom.getUnit());
                newMesProductOffLine.setItemUnit(mesBom.getItemUnit());
                newMesProductOffLine.setWorkOrderNo(mesWorkOrderDb.getWorkOrderNo());
                newMesProductOffLine.setWorkOrderType(mesWorkOrderDb.getWorkOrderType());
                newMesProductOffLine.setWorkCenterCode(mesWorkOrderDb.getWorkCenterCode());
                newMesProductOffLine.setWorkCellCode(mesWorkOrderDb.getWorkCellCode());
                newMesProductOffLine.setReportType(mesProductionRecord.getReportType());
                newMesProductOffLine.setSapWorkCenter(mesWorkOrderDb.getErpWorkCenter());
                newMesProductOffLine.setOrganizeCode(organizeCode);
                newMesProductOffLine.setDescription(mesProductionRecord.getCompleteDateTime());
                newMesProductOffLine.setBomCode(mesBom.getBomCode());
                newMesProductOffLine.setBomTotalSize(mesBoms.size());
                ConvertBean.serviceModelInitialize(newMesProductOffLine, userName);
                mesProductOffLineList.add(newMesProductOffLine);
            }
        }
        mesProductOffLineRDao.saveAll(mesProductOffLineList);
    }

    private void updateMesWorkOrder(MesWorkOrder mesWorkOrder, String userName,MesExtEnumUtil.WORK_ORDER_LOG_TYPE type,double qty) {
        //修改工单，需要重新同步
        mesWorkOrder.setSystemSyncStatus(CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue());
        ConvertBean.serviceModelUpdate(mesWorkOrder, userName);
        mesWorkOrder.setModifyDatetime((new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")).format(new Date()));
        workOrderExtService.update(mesWorkOrder);
        //保存记录
        saveMesWorkOrderLog(mesWorkOrder,type.getValue(),qty);
    }

    public void saveMesWorkOrderLog(MesWorkOrder mesWorkOrder,int type,double qty) {
        MesWorkOrderLog workOrderLog = new MesWorkOrderLog();
        BeanUtils.copyProperties(mesWorkOrder, workOrderLog, "id");
        workOrderLog.setType(type);
        workOrderLog.setModifyQty(qty);
        mesWorkOrderLogRepository.insert(workOrderLog);
    }

    public List<MesBom> findBomList(String organizeCode, MesPart mesPart, String bomVersion) {
        List<MesBom> bomData = new ArrayList<>();
        // 递归展开 BOM
        expendBomChild(organizeCode, mesPart.getPartNo(), bomVersion, bomData);

        if (bomData.isEmpty()) {
            throw new ImppBusiException(String.format("零件号 %s 没找找到 ERP 同步的原始 BOM 数据，请检查接口是否正常！",mesPart.getPartNo()));
        }

        Iterator<MesBom> iterator = bomData.iterator();
        while (iterator.hasNext()) {
            MesBom item = iterator.next();
            // 删除虚零件
            if (Objects.equals("X", item.getPartType())) {
                iterator.remove();
            } else {
                item.setPartNo(mesPart.getPartNo());
                item.setPartName(mesPart.getPartName());
            }
        }

        Map<String, MesBom> summaryBomMap = new HashMap<>();

        for (MesBom item : bomData) {
            String key = String.format("%s:%s", item.getPartNo().toUpperCase().trim(),
                    item.getItemPartNo().toUpperCase().trim());
            if (!summaryBomMap.containsKey(key)) {
                summaryBomMap.put(key, item);
            } else {
                MesBom tempItem = summaryBomMap.get(key);
                // 子零件重复的累加数量
                tempItem.setItemQty(MathOperation.add(tempItem.getItemQty(), item.getItemQty()));
            }
        }
        // 返回汇总数据
        return new ArrayList<>(summaryBomMap.values());
    }

    /**
     * 递归展开 BOM
     *
     * @param organizeCode 工厂代码
     * @param partNo       父零件号
     * @param bomData      存放 BOM 数据的集合
     */
    private void expendBomChild(String organizeCode, String partNo, String bomVersion, List<MesBom> bomData) {
        List<MesBom> bomDetails = findMesBomByPartNoAndBomVersion(partNo, organizeCode, bomVersion);
        if (org.springframework.util.CollectionUtils.isEmpty(bomDetails)) {
            throw new ImppBusiException(String.format("物料【%s】生产版本【%s】对应bom信息不存在", partNo, bomVersion));
        }
        for (MesBom item : bomDetails) {
            MesBom newItem = new MesBom();
            BeanUtils.copyProperties(item, newItem);
            // 处理父零件用量不为 1 的情况
            if (newItem.getQty() != 1.0 && newItem.getQty() > 0.0) {
                Double rateQty = MathOperation.div(newItem.getItemQty(), newItem.getQty(), 8);
                newItem.setQty(1.0);
                newItem.setItemQty(rateQty);
            }
            // 加入到 BOM 清单中
            bomData.add(newItem);
            // 如果有子阶，继续递归
            if (Objects.equals("X", item.getPartType())) {
                expendBomChild(organizeCode, newItem.getItemPartNo(), null, bomData);
            }
        }
    }

    public List<MesBom> findMesBomByPartNoAndBomVersion(String partNo, String organizeCode, String bomVersion) {

        //MES汇报查询BOM是否携带结束查询条件
        MesConfig config = configService.getCfgValueByCode(organizeCode, MesPcnExtConstWords.MES_REPORT_FIND_BOM_WITH_EFFENDTIME);
        Boolean isWithEffEndTime = (null != config && !StringUtils.isEmpty(config.getCfgValue()) && config.getCfgValue().equals(CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValueStr())) ? true : false;

        DdlPackBean ddlPackBean = DdlPackBean.getDdlPackBean(organizeCode);
        DdlPreparedPack.getStringEqualPack(partNo, MesPcnExtConstWords.PART_NO, ddlPackBean);
        DdlPreparedPack.getStringEqualPack(bomVersion, MesPcnExtConstWords.BOM_VERSION, ddlPackBean);
        DdlPreparedPack.getStringSmallerPack(TimeTool.getNowTime(true), MesPcnExtConstWords.EFF_START_TIME, ddlPackBean);
        if (isWithEffEndTime) DdlPreparedPack.getStringBiggerPack(TimeTool.getNowTime(true), MesPcnExtConstWords.EFF_END_TIME, ddlPackBean);
        DdlPreparedPack.getOrderBy(MesPcnExtConstWords.EFF_START_TIME, CommonEnumUtil.ASC_OR_DESC.DESC.getValue(), ddlPackBean);

        List<MesBom> mesBoms = mesBomRDao.findByHqlWhere(ddlPackBean);
        if (org.springframework.util.CollectionUtils.isEmpty(mesBoms)) {
            LOGGER.info("物料{}生产版本{}对应bom信息不存在", partNo, bomVersion );
            MesPcnException.throwMesBusiException("物料【%s】生产版本【%s】对应bom信息不存在", partNo, bomVersion);
        }
        MesBom next = mesBoms.iterator().next();
        //如果存在多个bomCode取最新的一个
        return mesBoms.stream().collect(Collectors.groupingBy(t -> t.getBomCode() + t.getEffStartTime())).get(next.getBomCode() + next.getEffStartTime());
    }

    private MesProductVersion getMesProductVersion(MesWorkOrder mesWorkOrder) {
        DdlPackBean ddlPackBean = DdlPackBean.getDdlPackBean(mesWorkOrder.getOrganizeCode());
        DdlPreparedPack.getStringEqualPack(mesWorkOrder.getPartNo(), "partNo", ddlPackBean);
        DdlPreparedPack.getStringEqualPack(mesWorkOrder.getErpWorkCenter(), "workCenterCode", ddlPackBean);
//        DdlPreparedPack.getStringEqualPack(mesWorkOrder.getProductVersion(), "productVersion", ddlPackBean);
        MesProductVersion mesProductVersion = mesProductVersionRDao.getByProperty(ddlPackBean);
        if (null == mesProductVersion) {
            LOGGER.info("物料{}生产版本信息不存在", mesWorkOrder.getPartNo());
            MesPcnException.throwMesBusiException("物料【%s】生产版本信息不存在", mesWorkOrder.getPartNo());
        }
        return mesProductVersion;
    }


    private void updateProductionRecord(String organizeCode, String userName, Long id) {
        DdlPackBean ddlPackBean = DdlPackBean.getDdlPackBean(organizeCode);
        DdlPreparedPack.getNumEqualPack(id, "id", ddlPackBean);
//        DdlPreparedPack.getStringEqualPack(sn, "custSn", ddlPackBean);
        productionRecordRao.updateByPropertiesNoSync(new String[]{"modifyUser", "modifyDatetime"},
                new Object[]{userName, DateUtil.format(new Date(), MesPcnExtConstWords.DATE_FORMAT_SSS)}, ddlPackBean);
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED, readOnly = false, rollbackFor = Exception.class)
    public void doProductReportByRecord(MesProductionRecord productionRecord, String organizeCode, String userName) {
        //新增初始化 todo
        // 根据
        //根据物料获取已发布的工单
        try {
            List<MesWorkOrder> oldMesWorkOrderList = getMesWorkOrder(productionRecord, organizeCode);
            if (CollectionUtils.isEmpty(oldMesWorkOrderList)) {
                if (Objects.equals(productionRecord.getReportStatus(), MesExtEnumUtil.REPORT_STATUS.REPORT_STATUS_30.getValue())) {
                    LOGGER.warn("报工条码:{}工单为试制单，更新成无需报工", productionRecord.getProductSn());
                    DdlPackBean ddlPackBean = DdlPackBean.getDdlPackBean(organizeCode);
                    DdlPreparedPack.getNumEqualPack(productionRecord.getId(), "id", ddlPackBean);
                    productionRecordRao.updateByProperties(new String[]{"modifyUser", "modifyDatetime", "reportStatus"},
                            new Object[]{userName, DateUtil.format(new Date(), MesPcnExtConstWords.DATE_FORMAT_SSS), productionRecord.getReportStatus()}, ddlPackBean);
                    return;
                }
                LOGGER.error("报工条码:{}找不到加工单", productionRecord.getProductSn());
                updateProductionRecord(organizeCode, userName, productionRecord.getId());
                return;
            }

            report(productionRecord, organizeCode, userName, oldMesWorkOrderList);
        } catch (Exception e) {
            String msg = e.getMessage();
            if (e instanceof ImppBusiException) {
                msg = ((ImppBusiException) e).getErrorMsg();
            }
            LOGGER.error("条码:{}报工失败,{}", productionRecord.getProductSn(), e.getMessage());
            productionRecordService.updateProductionRecord(organizeCode, userName, productionRecord.getId(), msg);
        }
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, isolation = Isolation.READ_COMMITTED, readOnly = false, rollbackFor = Exception.class)
    public void doProductHasOrderReportByRecord(MesProductionRecord productionRecord, String organizeCode, String userName) {
        //新增初始化 todo
        // 根据
        //根据物料获取已发布的工单
        try {
            LOGGER.info("此次报工的条码【{}】,工单【{}】", productionRecord.getProductSn(), productionRecord.getWorkOrderNo());
            List<MesWorkOrder> oldMesWorkOrders = getMesWorkOrders(productionRecord, organizeCode);
            // 工单加工记录报表-试制工单，汇报状态修改成"无需汇报"
            if (Objects.equals(productionRecord.getReportStatus(), MesExtEnumUtil.REPORT_STATUS.REPORT_STATUS_30.getValue())) {
                LOGGER.warn("报工条码:{}工单为试制单，更新成无需报工", productionRecord.getProductSn());
                DdlPackBean ddlPackBean = DdlPackBean.getDdlPackBean(organizeCode);
                DdlPreparedPack.getNumEqualPack(productionRecord.getId(), "id", ddlPackBean);
                productionRecordRao.updateByProperties(new String[]{"modifyUser", "modifyDatetime", "reportStatus"},
                        new Object[]{userName, TimeTool.getNowTime(true), productionRecord.getReportStatus()}, ddlPackBean);
                return;
            }
            if (CollectionUtils.isEmpty(oldMesWorkOrders)) {
                updateProductionRecord(organizeCode, userName, productionRecord.getId());
                return;
            }
            report(productionRecord, organizeCode, userName, oldMesWorkOrders);
        } catch (Exception e) {
            String msg = e.getMessage();
            if (e instanceof ImppBusiException) {
                msg = ((ImppBusiException) e).getErrorMsg();
            }
            LOGGER.error(String.format("条码:【%s】报工失败，原因:[%s]", productionRecord.getProductSn(), e.getMessage()));
            productionRecordService.updateProductionRecord(organizeCode, userName, productionRecord.getId(), msg);
        }

    }

    /**
     * 获取最优的工单
     *
     * @param oldMesWorkOrderList
     * @param mesWorkCenter
     * @return
     */
    public MesWorkOrder getBestWorkOrder(MesProductionRecord productionRecord, List<MesWorkOrder> oldMesWorkOrderList, MesWorkCenter mesWorkCenter) {

        if (!StringUtils.isEmpty(productionRecord.getWorkOrderNo())) {
            return oldMesWorkOrderList.get(0);
        }

        for (MesWorkOrder mesWorkOrder : oldMesWorkOrderList) {
            double reportdQty = MathOperation.add(1, mesWorkOrder.getReportedQty());
            // 如果是允许，则需要判断是否超过比例
            if (reportdQty <= mesWorkOrder.getQty()) {
                return mesWorkOrder;
            }
            if (Objects.equals(CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue(), mesWorkCenter.getIsCheckOrderQty())) {
                double remainQty = reportdQty - mesWorkOrder.getQty();
                double rate = remainQty / mesWorkOrder.getQty();
                if (rate > mesWorkCenter.getOrderRate() / 100) {
                    continue;
                } else {
                    return mesWorkOrder;
                }
            }

        }
        return null;
    }

    private void report(MesProductionRecord productionRecord, String organizeCode, String userName, List<MesWorkOrder> oldMesWorkOrderList) {
        //

        DdlPackBean ddlPackBean;
        //查询工作中心
        ddlPackBean = DdlPackBean.getDdlPackBean(organizeCode);
        DdlPreparedPack.getStringEqualPack(productionRecord.getWorkCenterCode(), "workCenterCode", ddlPackBean);
        MesWorkCenter mesWorkCenter = mesWorkCenterRDao.getByProperty(ddlPackBean);
        if (Objects.isNull(mesWorkCenter)) {
            LOGGER.error(String.format("产线【%s】不存在", productionRecord.getWorkCenterCode()));
            throw new ImppBusiException(String.format("产线【%s】不存在", productionRecord.getWorkCenterCode()));
        }
        MesWorkOrder oldMesWorkOrder = getBestWorkOrder(productionRecord, oldMesWorkOrderList, mesWorkCenter);
        if (oldMesWorkOrder == null) {
            LOGGER.error(String.format("未找到匹配的加工单, 条码=%s", productionRecord.getCustSn()));
            throw new ImppBusiException(String.format("未找到匹配的加工单"));
        }

        //获取生产版本
        MesProductVersion mesProductVersion = getProductVersion(oldMesWorkOrder.getOrganizeCode(), oldMesWorkOrder.getPartNo(), oldMesWorkOrder.getProductVersion());
        String bomVersion =  mesProductVersion.getAlternativePartList();
        String partProdGroupCode = oldMesWorkOrder.getPartProdGroupCode();
        // partProdGroupCode 不为空，查询的数据不为空，查询数据中的获取bom方式不为空且枚举是 基于默认BOM ，才bomversion = null 否则都是通过生产版本找bom
        if (!StringUtil.isEmpty(partProdGroupCode)) {
            DdlPackBean partProdBean = DdlPackBean.getDdlPackBean(oldMesWorkOrder.getOrganizeCode());
            DdlPreparedPack.getStringEqualPack(partProdGroupCode, "partProdGroupCode", partProdBean);
            List<MesPartProdGroup> mesPartProdGroupList = mesPartProdGroupRao.findByHqlTopWhere(partProdBean, 1);

            // 没有零件生产组用默认找生产版本、有的话找获取bom方式枚举，枚举是基于默认BOM 则直接找
            if (!mesPartProdGroupList.isEmpty()) {
                MesPartProdGroup mesPartProdGroup = mesPartProdGroupList.get(0);
                if (mesPartProdGroup.getEnableBomVersion() != null && mesPartProdGroup.getEnableBomVersion() == MesExtEnumUtil.ENABLE_BOM_VERSION.ENABLE_BOM_VERSION_2.getValue()) {
                    log.info("基于默认BOM获取BOM");
                    bomVersion = "";
                }
            }
        }
        //查询物料信息
        DdlPackBean ddlPackBeanPart = DdlPackBean.getDdlPackBean(organizeCode);
        DdlPreparedPack.getStringEqualPack(productionRecord.getPartNo(), "partNo", ddlPackBeanPart);
        MesPart mesPart = mesPartRDao.getByProperty(ddlPackBeanPart);
        if (null == mesPart) {
            throw new ImppBusiException(String.format("物料【%s】信息不存在", productionRecord.getPartNo()));
        }

        List<MesBom> mesBoms = findBomList(organizeCode, mesPart, bomVersion);


        if (CollectionUtils.isEmpty(mesBoms)) {
            LOGGER.error(String.format("物料【%s】生产版本【%s】对应bom信息不存在",
                    productionRecord.getPartNo(), mesProductVersion.getProductVersion()));
            throw new ImppBusiException(String.format("物料【%s】生产版本【%s】对应bom信息不存在",
                    productionRecord.getPartNo(), mesProductVersion.getProductVersion()));
        }

        Map<String, Object> propertyMap = new HashMap<>();

        LOGGER.info("工单【{}]】报工原数量【{}】", oldMesWorkOrder.getWorkOrderNo(), oldMesWorkOrder.getReportedQty());
        double reportQty = MathOperation.add(productionRecord.getQty(), oldMesWorkOrder.getReportedQty());
        propertyMap.put("reportedQty", reportQty);
        if (StringUtils.isEmpty(productionRecord.getWorkOrderNo())) {
            double unCompleteQty = MathOperation.sub(oldMesWorkOrder.getQty(),  reportQty);
            //未完成数减到0之后不能为负数
            unCompleteQty = unCompleteQty > 0 ? unCompleteQty : 0;

            propertyMap.put(MesPcnExtConstWords.COMPLETE_QTY, reportQty);
            propertyMap.put(MesPcnExtConstWords.UN_COMPLETE_QTY, unCompleteQty);
        }

        if (StringUtil.isEmpty(productionRecord.getWorkOrderNo())) {
            //更新工单状态
            if (reportQty >= oldMesWorkOrder.getQty()) {
                propertyMap.put(MesPcnExtConstWords.WORK_ORDER_STATUS, MesExtEnumUtil.ORDER_STATUS.COMPLETE.getValue());

            } else {
                propertyMap.put(MesPcnExtConstWords.WORK_ORDER_STATUS, MesExtEnumUtil.ORDER_STATUS.PROCESS.getValue());
            }
        }

        //保存数据
        List<MesProductOffLine> mesProductOffLineList = new ArrayList<>();
        List<MesMove> mesMoveList = new ArrayList<>();
        // 排除重复的数据
        Map<MesBom, List<MesBom>> map = mesBoms.stream().collect(Collectors.groupingBy(mesBom -> {
            MesBom bom = new MesBom();
            bom.setPartNo(mesBom.getPartNo());
            bom.setItemPartNo(mesBom.getItemPartNo());
            return bom;
        }));
        if (MesExtEnumUtil.NOSORT_REPORT_TYPE.REPORT.getValue() == productionRecord.getReportType()) {

            DdlPackBean productPackBean = DdlPackBean.getDdlPackBean(productionRecord.getOrganizeCode());
            DdlPreparedPack.getStringEqualPack(oldMesWorkOrder.getPartNo(),"reportPartNo", productPackBean);
            DdlPreparedPack.getStringEqualPack(productionRecord.getProductSn(),"reportSn", productPackBean);
            int count = mesProductOffLineRDao.findByHqlWhereCount(productPackBean);
            if (count > 0) {
                String msg = String.format("产成零件【%s】条码【%s】对应报工信息已存在",
                        oldMesWorkOrder.getPartNo(), productionRecord.getProductSn());
                LOGGER.error(msg);
                LOGGER.info("条码【{}】重复报工更新成已报工", productionRecord.getProductSn());
                DdlPackBean packBean = DdlPackBean.getDdlPackBean(organizeCode);
                DdlPreparedPack.getNumEqualPack(productionRecord.getId(), "id", packBean);
                productionRecordRao.updateByProperties(new String[]{"modifyUser", "modifyDatetime", "workOrderNo", "reportStatus","description"},
                        new Object[]{userName, TimeTool.getNowTime(true), oldMesWorkOrder.getWorkOrderNo(), MesExtEnumUtil.REPORT_STATUS.REPORT_STATUS_20.getValue(), "重复报工更新成已报工"}, packBean);
                return;
            }

            for (Map.Entry<MesBom, List<MesBom>> entry : map.entrySet()) {
                mesProductOffLineList.add(getMesProductOffLine(productionRecord, userName, oldMesWorkOrder, mesProductVersion, entry.getValue().get(0), productionRecord.getProductSn(), false,false,map.size()));
            }
        } else if (MesExtEnumUtil.NOSORT_REPORT_TYPE.CUSTOMER_SUPPLY_MOVE.getValue() == productionRecord.getReportType()) {
            customerSupplyMove(productionRecord, oldMesWorkOrder, mesProductVersion, mesBoms, mesProductOffLineList, mesMoveList, userName);
        } else {
            LOGGER.info("汇报类型不存在");
            return;
        }
        //oldMesWorkOrder.setCompleteQty(reportQty);
        if (propertyMap.containsKey("reportedQty")){
            oldMesWorkOrder.setReportedQty(reportQty);
        }
        if (propertyMap.containsKey(MesPcnExtConstWords.COMPLETE_QTY)){
            oldMesWorkOrder.setCompleteQty(Double.valueOf(propertyMap.get(MesPcnExtConstWords.COMPLETE_QTY).toString()));
        }
        if (propertyMap.containsKey(MesPcnExtConstWords.UN_COMPLETE_QTY)){
            oldMesWorkOrder.setUnCompleteQty(Double.valueOf(propertyMap.get(MesPcnExtConstWords.UN_COMPLETE_QTY).toString()));
        }
        if (propertyMap.containsKey(MesPcnExtConstWords.WORK_ORDER_STATUS)){
            oldMesWorkOrder.setWorkOrderStatus(Integer.valueOf(propertyMap.get(MesPcnExtConstWords.WORK_ORDER_STATUS).toString()));
        }
        LOGGER.info("工单【{}】报工后数量【{}】", oldMesWorkOrder.getWorkOrderNo(), oldMesWorkOrder.getReportedQty());
        ConvertBean.serviceModelUpdate(oldMesWorkOrder, userName);
        oldMesWorkOrder.setModifyDatetime((new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")).format(new Date()));
        propertyMap.put(MesPcnExtConstWords.MODIFY_DATE_TIME, (new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")).format(new Date()));
        propertyMap.put(MesPcnExtConstWords.MODIFY_USER, userName);


        //workOrderExtService.update(oldMesWorkOrder);

        //查询工作中心
        DdlPackBean orderPackBean = DdlPackBean.getDdlPackBean(organizeCode);
        DdlPreparedPack.getNumEqualPack(oldMesWorkOrder.getId(), "id", orderPackBean);

        String[] propertyName = new String[propertyMap.size()];
        Object[] propertyValue = new Object[propertyMap.size()];

        Integer index = 0;
        for (Map.Entry<String, Object> innerEntry : propertyMap.entrySet()) {
            if (null == innerEntry) continue;
            propertyName[index] = innerEntry.getKey();
            propertyValue[index] = innerEntry.getValue();
            index ++;
        }



        workOrderRepository.updateByProperties(new String[]{MesPcnExtConstWords.ID, MesPcnExtConstWords.ORGANIZE_CODE}, new Object[]{oldMesWorkOrder.getId(), oldMesWorkOrder.getOrganizeCode()}, propertyName, propertyValue);
        LOGGER.info("修改工单数量完成,工单号【{}】", oldMesWorkOrder.getWorkOrderNo());


        if(!CollectionUtils.isEmpty(mesProductOffLineList)){
            mesProductOffLineRDao.saveAll(mesProductOffLineList);
        }

        if(!CollectionUtils.isEmpty(mesMoveList)){
            mesMoveRepository.saveAll(mesMoveList);
        }

        // 同步工单信息给加工记录、条码信息
        DdlPackBean snDdlPackBean = DdlPackBean.getDdlPackBean(organizeCode);
        DdlPreparedPack.getNumEqualPack(productionRecord.getId(), "id", snDdlPackBean);

        mesProduceSnRao.updateByProperties(new String[]{"modifyUser", "modifyDatetime", "workOrderNo"},
                new Object[]{userName, TimeTool.getNowTime(true), oldMesWorkOrder.getWorkOrderNo()}, snDdlPackBean);

        DdlPackBean packBean = DdlPackBean.getDdlPackBean(organizeCode);
        DdlPreparedPack.getNumEqualPack(productionRecord.getId(), "id", packBean);
//        DdlPreparedPack.getStringEqualPack(productionRecord.getModifyDatetime(), "modifyDatetime", packBean);

        productionRecordRao.updateByPropertiesNoSync(new String[]{"modifyUser", "modifyDatetime", "workOrderNo", "reportStatus","systemSyncStatus","systemSyncDatetime"},
                new Object[]{userName, DateUtil.format(new Date(), MesPcnExtConstWords.DATE_FORMAT_SSS), oldMesWorkOrder.getWorkOrderNo(), MesExtEnumUtil.REPORT_STATUS.REPORT_STATUS_20.getValue(),CommonEnumUtil.FALSE,""}, packBean);


        LOGGER.info("条码【{}】报工成功,报工工单【{}】", productionRecord.getProductSn(), oldMesWorkOrder.getWorkOrderNo());

    }

    private MesProductOffLine getMesProductOffLine(MesProductionRecord productionRecord, String userName, MesWorkOrder oldMesWorkOrder, MesProductVersion mesProductVersion, MesBom mesBom,String reportSn,boolean isItemReport,boolean saveProductVersionFlag,Integer bomSize) {
        MesProductOffLine newMesProductOffLine = new MesProductOffLine();
        BeanUtils.copyProperties(mesBom, newMesProductOffLine, MesPcnExtConstWords.BASE_BEAN_FIELDS);

        DdlPackBean productPackBean = DdlPackBean.getDdlPackBean(productionRecord.getOrganizeCode());
        DdlPreparedPack.getStringEqualPack(mesBom.getItemPartNo(),"itemPartNo", productPackBean);
        DdlPreparedPack.getStringEqualPack(oldMesWorkOrder.getPartNo(),"reportPartNo", productPackBean);
        DdlPreparedPack.getStringEqualPack(productionRecord.getProductSn(),"reportSn", productPackBean);
        MesProductOffLine productOffLine = mesProductOffLineRDao.getByProperty(productPackBean);
        if (productOffLine != null) {
            String msg = String.format("子零件【%s】产成零件【%s】条码【%s】对应报工信息已存在",
                    mesBom.getItemPartNo(),oldMesWorkOrder.getPartNo(), productionRecord.getProductSn());
            LOGGER.error(msg);
            throw new ImppBusiException(msg);
        }


        newMesProductOffLine.setReportPartNo(mesBom.getPartNo());
        newMesProductOffLine.setReportPartNameRdd(mesBom.getPartName());
        newMesProductOffLine.setAlort(mesProductVersion.getReceiveInventoryPoint());
        newMesProductOffLine.setStgeLoc(mesProductVersion.getShipInventoryPoint());
        if (isItemReport) {
            //装配费没有bom情况
            if (mesBom.getPartNo().equals(oldMesWorkOrder.getPartNo())) {
                newMesProductOffLine.setReportPartNo(mesBom.getItemPartNo());
                newMesProductOffLine.setReportPartNameRdd(mesBom.getItemPartName());
                newMesProductOffLine.setItemPartNo(MesPcnExtConstWords.EMPTY);
                newMesProductOffLine.setItemPartName(MesPcnExtConstWords.EMPTY);
                newMesProductOffLine.setItemUnit(MesPcnExtConstWords.EMPTY);
                newMesProductOffLine.setItemQty(0d);
            }
            newMesProductOffLine.setReportSn(reportSn);
            newMesProductOffLine.setSerialNumber(reportSn);
        } else {
            newMesProductOffLine.setReportSn(productionRecord.getProductSn());
            newMesProductOffLine.setSerialNumber(productionRecord.getSerialNumber());
        }
        newMesProductOffLine.setItemQty(productionRecord.getQty()* mesBom.getItemQty());
        newMesProductOffLine.setQty(productionRecord.getQty());
        newMesProductOffLine.setBomVersion(oldMesWorkOrder.getProductVersion());
        newMesProductOffLine.setUnit(mesBom.getUnit());
        newMesProductOffLine.setBomCode(mesBom.getBomCode());
        newMesProductOffLine.setWorkOrderNo(oldMesWorkOrder.getWorkOrderNo());
        newMesProductOffLine.setWorkOrderType(oldMesWorkOrder.getWorkOrderType());
        newMesProductOffLine.setWorkCenterCode(oldMesWorkOrder.getWorkCenterCode());
        newMesProductOffLine.setWorkCellCode(oldMesWorkOrder.getWorkCellCode());
        newMesProductOffLine.setReportType(productionRecord.getReportType());
        if (Objects.equals(MesExtEnumUtil.ORDER_TYPE.BTO.getValue(), oldMesWorkOrder.getWorkOrderType())){
            newMesProductOffLine.setWorkCenterCode(mesProductVersion.getWorkCenterCode());
        }
        newMesProductOffLine.setSapWorkCenter(saveProductVersionFlag ? mesProductVersion.getProductVersion() : mesProductVersion.getWorkCenterCode());
        newMesProductOffLine.setOrganizeCode(oldMesWorkOrder.getOrganizeCode());
        newMesProductOffLine.setDescription(productionRecord.getCompleteDateTime());
        newMesProductOffLine.setProductionRecordId(productionRecord.getId().toString());
        newMesProductOffLine.setBomTotalSize(bomSize);
        ConvertBean.serviceModelInitialize(newMesProductOffLine, userName);
        return newMesProductOffLine;
    }

    /**
     * 获取对应的工单
     *
     * @param productionRecord
     * @param organizeCode
     * @return
     */
    private List<MesWorkOrder> getMesWorkOrder(MesProductionRecord productionRecord, String organizeCode) {
        MesWorkOrder mesWorkOrder = new MesWorkOrder();

        List<MesWorkOrder> workOrders = new ArrayList<>();
        /**
         * 根据产线物料获取所有工单
         */
        List<MesWorkOrder> oldMesWorkOrders = getMesWorkOrders(productionRecord, organizeCode);
        if (CollectionUtils.isEmpty(oldMesWorkOrders)) {
            return null;
        }
        // 获取当前班次
        String shiftCode = productionRecord.getShiftCode();
        String workCenterCode = productionRecord.getWorkCenterCode();

        //查询工作中心
        DdlPackBean ddlPackBean = DdlPackBean.getDdlPackBean(organizeCode);
        DdlPreparedPack.getStringEqualPack(workCenterCode, "workCenterCode", ddlPackBean);
        MesWorkCenter mesWorkCenter = mesWorkCenterRDao.getByProperty(ddlPackBean);
        if (Objects.isNull(mesWorkCenter)) {
            throw new ImppBusiException(String.format("产线【%s】不存在",workCenterCode));
        }

        List<MesShift> shiftList = mesShiftService.queryMesShift(organizeCode, workCenterCode);
        // 如果当前班次是早班
        if (CollectionUtils.isEmpty(shiftList)) {
            throw new ImppBusiException(String.format("班次信息未维护,产线=%s",workCenterCode));
        }
        List<MesShift> shifts = shiftList.stream().filter(mesShift -> Objects.equals(mesShift.getWorkCenterCode(), workCenterCode)).collect(Collectors.toList());
        // 获取产品加工记录对应时间的班次

        Integer hours = transfer(productionRecord.getCompleteDateTime()).getHours();

        Date currentDate = TimeTool.stringParseToDate(productionRecord.getCompleteDateTime());
        String lotNo = productionRecord.getLotNo();
        // 基于加工时间判断具体的班次
        MesShift currentShift = shiftList.stream().filter(shift -> {
            String startTime = lotNo + " " + shift.getStartTime();
            String endTime = lotNo + " " + shift.getEndTime();
            // 如果是晚班，12点前，结束时间要加一天，如果是12点后，开始时间要减一天
            if (Objects.equals(shift.getShiftCode(),"20")) {
                if (hours < 12) {
                    startTime = getDateTime(lotNo, 1) + " " + shift.getStartTime();
                } else {
                    endTime = getDateTime(lotNo, 2) + " " + shift.getEndTime();
                }
            }
            Date startDate = DateUtil.parse(startTime);
            Date endDate = DateUtil.parse(endTime);
            return currentDate.getTime() >= startDate.getTime() && currentDate.getTime() <= endDate.getTime();
        }).findFirst().orElse(null);



        String orderDate = productionRecord.getLotNo();
        if (currentShift.getStartTime().compareTo(currentShift.getEndTime()) > 0) {
            // 如果比0点大，需要找日期前一天的班次的工单号,如果是0点前，date =lotNO  如果是0点后，则找lot的前一天
            if (hours < 12) {
                orderDate = getDate(productionRecord.getLotNo(), 1);
            } else {
                orderDate = getDate(productionRecord.getLotNo(), 0);
            }

        }

        List<MesWorkOrder> list = new ArrayList<>();
        for (MesWorkOrder oldMesWorkOrder : oldMesWorkOrders) {
            if (Objects.equals(oldMesWorkOrder.getShiftCode(), productionRecord.getShiftCode()) && Objects.equals(oldMesWorkOrder.getProduceTime(), orderDate)) {
                list.add(oldMesWorkOrder);
            }
        }
        // 如果找不到则再根据计划开始时间找
        if (CollectionUtils.isEmpty(list)) {
            list = oldMesWorkOrders.stream().filter(workOrder -> !StringUtils.isEmpty(workOrder.getPlanStartTime()) && !StringUtils.isEmpty(workOrder.getPlanEndTime()) &&
                    transfer(productionRecord.getCompleteDateTime()).getTime() >= transfer(workOrder.getPlanStartTime()).getTime() && transfer(productionRecord.getCompleteDateTime()).getTime() <= transfer(workOrder.getPlanEndTime()).getTime()).collect(Collectors.toList());

        }
        workOrders = list;


        MesWorkOrder bestWorkOrder = getBestWorkOrder(productionRecord, workOrders, mesWorkCenter);

        String date = productionRecord.getLotNo();
        // 如果找不到需要找 当天的其它班次
        if (CollectionUtils.isEmpty(workOrders) || bestWorkOrder == null) {
            // 如果开始时间大于结束时间，说明跨天了，跨天说明是晚班,需要找, 如果是晚班, 则找早中班的工单
            if (currentShift.getStartTime().compareTo(currentShift.getEndTime()) > 0) {
                // 如果比0点大，需要找日期前一天的班次的工单号,如果是0点前，date =lotNO  如果是0点后，则找lot的前一天
                if (hours < 12) {
                    date = getDate(productionRecord.getLotNo(), 1);
                } else {
                    date = getDate(productionRecord.getLotNo(), 0);
                }
                String endTime = date + " " + currentShift.getStartTime();
                String startTime = date + " " + currentShift.getEndTime();

                workOrders = oldMesWorkOrders.stream().filter(workOrder -> !StringUtils.isEmpty(workOrder.getPlanStartTime()) && !StringUtils.isEmpty(workOrder.getPlanEndTime()) &&
                        transfer(startTime).getTime() >= transfer(workOrder.getPlanStartTime()).getTime() && transfer(endTime).getTime() <= transfer(workOrder.getPlanEndTime()).getTime()).collect(Collectors.toList());

            } else {
                // 早班或者中班 则找当天非当前班次的最早单号
                String startTime = date + " " + currentShift.getEndTime();
                String endTime = getDate(date, 2) + " " + currentShift.getStartTime();
                for (MesWorkOrder workOrder : oldMesWorkOrders) {
                    if (transfer(startTime).getTime() >= transfer(workOrder.getPlanStartTime()).getTime() && transfer(endTime).getTime() <= transfer(workOrder.getPlanEndTime()).getTime()) {
                        break;
                    }
                }
                workOrders = oldMesWorkOrders.stream().filter(workOrder -> transfer(startTime).getTime() >= transfer(workOrder.getPlanStartTime()).getTime() && transfer(endTime).getTime() <= transfer(workOrder.getPlanEndTime()).getTime()).collect(Collectors.toList());
            }
            if (CollectionUtils.isEmpty(workOrders)) {
                // todo 需要记录并且第二天要用job跑
                return null;
            }
        }
        return workOrders;
    }

    private List<MesWorkOrder> getMesWorkOrders(MesProductionRecord productionRecord, String organizeCode) {
        List<Integer> list = Stream.of(MesExtEnumUtil.ORDER_STATUS.RELEASE.getValue(), MesExtEnumUtil.ORDER_STATUS.PROCESS.getValue(), MesExtEnumUtil.ORDER_STATUS.COMPLETE.getValue()).collect(Collectors.toList());

        DdlPackBean ddlPackBean = DdlPackBean.getDdlPackBean(organizeCode);
        DdlPreparedPack.getStringEqualPack(productionRecord.getPartNo(), "partNo", ddlPackBean);

        // 获取5分钟之前的时间
        String startTime = TimeTool.timeCalc(new Date(), Calendar.MINUTE, -2, "yyyy-MM-dd HH:mm:ss");

        DdlPreparedPack.getStringSmallerNotEqualPack(startTime, "createDatetime", ddlPackBean);
        DdlPreparedPack.getStringEqualPack(productionRecord.getWorkCenterCode(), "workCenterCode", ddlPackBean);
        if (!StringUtils.isEmpty(productionRecord.getWorkOrderNo())) {
            DdlPreparedPack.getStringEqualPack(productionRecord.getWorkOrderNo(), "workOrderNo", ddlPackBean);
        } else {
            DdlPreparedPack.getInPackList(list, "workOrderStatus", ddlPackBean);
        }
        DdlPreparedPack.getOrderByPack(new Object[]{CommonEnumUtil.ASC_OR_DESC.ASC.getValue()}, new String[]{"modifyDatetime"}, ddlPackBean);
        // 新增班次逻辑
        //DdlPreparedPack.
        List<MesWorkOrder> oldMesWorkOrders = workOrderRepository.findByHqlWhere(ddlPackBean);
        if (!CollectionUtils.isEmpty(oldMesWorkOrders)) {
            oldMesWorkOrders = oldMesWorkOrders.stream().filter(mesWorkOrder -> !Objects.equals(MesExtEnumUtil.ORDER_TYPE_IDENTIFICATION.P.getValue(), mesWorkOrder.getOrderFlag())).collect(Collectors.toList());
            if (CollectionUtils.isEmpty(oldMesWorkOrders)) {
                productionRecord.setReportStatus(MesExtEnumUtil.REPORT_STATUS.REPORT_STATUS_30.getValue());
            }
        }
        return oldMesWorkOrders;
    }

    /**
     * flag = 1 减少 1天， = 2 加一天
     *
     * @param lotNo
     * @param flag
     * @return
     */
    public String getDate(String lotNo, int flag) {
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        Date d = null;
        try {
            d = df.parse(lotNo);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(d);
        if (flag == 1) {
            cal.add(Calendar.DATE, -1); //减1天
        } else if (flag == 2) {
            cal.add(Calendar.DATE, 1); //加1天
        }
        return df.format(cal.getTime()).substring(0, 10);
    }

    @Override
    public List<MesProductionRecord> getUnReportMesProduceSn(String organizeCode) {

        //1.获取生产加工记录production_record表中，report_status为待汇报状态的工单
        DdlPackBean ddlPackBean = DdlPackBean.getDdlPackBean(organizeCode);
        List<Integer> statusList = Stream.of(MesExtEnumUtil.REPORT_STATUS.REPORT_STATUS_10.getValue(), MesExtEnumUtil.REPORT_STATUS.REPORT_STATUS_40.getValue()).collect(Collectors.toList());
        DdlPreparedPack.getInPackList(statusList, "reportStatus", ddlPackBean);
        DdlPreparedPack.getIsNull("workOrderNo", ddlPackBean);
        String workCenterCodes = configService.getCfgValue(organizeCode, "MES_PCN_REPORT_CENTER");

        if (!StringUtils.isEmpty(workCenterCodes)) {
            List<String> centerList = Arrays.asList(workCenterCodes.split(","));
            DdlPreparedPack.getNotInPack(centerList, "workCenterCode", ddlPackBean);
        }
        /*String workCenterCodes ="K03CK01A";
        if (!StringUtils.isEmpty(workCenterCodes)) {
            List<String> centerList = Arrays.asList(workCenterCodes.split(","));
            DdlPreparedPack.getInPack(centerList, "workCenterCode", ddlPackBean);
        }*/
        DdlPreparedPack.getOrderByPack(new Object[]{CommonEnumUtil.ASC_OR_DESC.ASC.getValue()}, new String[]{"modifyDatetime"}, ddlPackBean);

        String numStr = configService.getCfgValue(organizeCode, "MES_PCN_REPORT_NUM");
        int num = StringUtils.isEmpty(numStr) ? 50 : Integer.valueOf(numStr);

        List<MesProductionRecord> mesProductionRecordList = productionRecordRao.findByHqlTopWhere(ddlPackBean, num);
        List<MesProductionRecord> recordList = new ArrayList<>();
        // 根据零件号分组
        /*if(!CollectionUtils.isEmpty(mesProductionRecordList)) {
            Map<String, List<MesProductionRecord>> map = mesProductionRecordList.stream().collect(Collectors.groupingBy(MesProductionRecord::getPartNo));
            map.forEach((k, v) -> {
                recordList.add(v.get(0));
            });
        }*/
        recordList.addAll(mesProductionRecordList);

        return recordList;
    }

    @Override
    public List<MesProductionRecord> getUnReportHasOrderMesProduceSn(String organizeCode) {
        //1.获取生产加工记录production_record表中，report_status为待汇报状态的工单
        DdlPackBean ddlPackBean = DdlPackBean.getDdlPackBean(organizeCode);
        List<Integer> statusList = Stream.of(MesExtEnumUtil.REPORT_STATUS.REPORT_STATUS_10.getValue(), MesExtEnumUtil.REPORT_STATUS.REPORT_STATUS_40.getValue()).collect(Collectors.toList());
        DdlPreparedPack.getInPackList(statusList, "reportStatus", ddlPackBean);
        DdlPreparedPack.getIsNotNull("workOrderNo", ddlPackBean);
        String workCenterCodes = configService.getCfgValue(organizeCode, "MES_PCN_REPORT_CENTER");
        //String workCenterCodes ="B19CK01A";
        if (!StringUtils.isEmpty(workCenterCodes)) {
            List<String> centerList = Arrays.asList(workCenterCodes.split(","));
            DdlPreparedPack.getNotInPack(centerList, "workCenterCode", ddlPackBean);
        }
        DdlPreparedPack.getOrderByPack(new Object[]{CommonEnumUtil.ASC_OR_DESC.ASC.getValue()}, new String[]{"modifyDatetime"}, ddlPackBean);

        String numStr = configService.getCfgValue(organizeCode, "MES_PCN_REPORT_NUM");
        int num = StringUtils.isEmpty(numStr) ? 50 : Integer.valueOf(numStr);

        List<MesProductionRecord> mesProductionRecordList = productionRecordRao.findByHqlTopWhere(ddlPackBean, num);
        List<MesProductionRecord> recordList = new ArrayList<>();
        // 根据零件号分组
        /*if(!CollectionUtils.isEmpty(mesProductionRecordList)) {
            Map<String, List<MesProductionRecord>> map = mesProductionRecordList.stream().collect(Collectors.groupingBy(MesProductionRecord::getPartNo));
            map.forEach((k, v) -> {
                DdlPackBean packBean = DdlPackBean.getDdlPackBean();
                DdlPreparedPack.getStringEqualPack(k, "partNo", packBean);
                // 排除掉试制造件
                MesPartSap part = partSapRepository.getByProperty(packBean);
                if (part != null && !Objects.equals(CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue(), part.getIsTestPiece())) {
                    recordList.add(v.get(0));
                }
            });
        }*/
        recordList.addAll(mesProductionRecordList);

        return recordList;
    }


    @Override
    public List<MesProductionRecord> getPreDayReportMesProductionRecord(String organizeCode, List<MesShift> mesShiftList, String workCenterCode) {

        //1.获取生产加工记录production_record表中，report_status为待汇报状态的工单
        DdlPackBean ddlPackBean = DdlPackBean.getDdlPackBean(organizeCode);
        DdlPreparedPack.getStringEqualPack(workCenterCode, "workCenterCode", ddlPackBean);
//        DdlPreparedPack.getNumEqualPack( MesExtEnumUtil.REPORT_STATUS.REPORT_STATUS_10.getValue(),"reportStatus",ddlPackBean);
        DdlPreparedPack.getInPackList(
                Arrays.asList(MesExtEnumUtil.REPORT_STATUS.REPORT_STATUS_10.getValue(),
                        MesExtEnumUtil.REPORT_STATUS.REPORT_STATUS_40.getValue()), "reportStatus", ddlPackBean);
        List<MesShift> collect = mesShiftList.stream().filter(s -> s.getShiftCode().equalsIgnoreCase("10")).collect(Collectors.toList());
        String startTime = " 08:00:00";
        if (!CollectionUtils.isEmpty(collect)) {
            MesShift mesShift = collect.get(0);
            startTime = StringUtil.isEmpty(mesShift.getStartTime()) ? " 08:00:00" : " " + mesShift.getStartTime();
        }
        //获取前一天的早班到当天早班的区间的所有加工记录，统计个数
        DdlPreparedPack.timeBuilder(
                new SimpleDateFormat("yyyy-MM-dd").format(TimeTool.getDateBefore(new Date(), 20)) + startTime,
                TimeTool.getToday() + startTime,
                "completeDateTime", ddlPackBean, false);

        return productionRecordRao.findByHqlWhere(ddlPackBean);
    }

    @Override
    public void doProductReport(MesWorkOrderExtModel mesWorkOrder, String userName) {
        DdlPackBean packBean = DdlPackBean.getDdlPackBean(mesWorkOrder.getOrganizeCode());
        DdlPreparedPack.getNumEqualPack(mesWorkOrder.getId(),"id",packBean);
        MesWorkOrder mesWorkOrderDb = mesWorkOrderRDao.getByProperty(packBean);
        if (Objects.isNull(mesWorkOrderDb)) {
            MesPcnException.throwMesBusiException("工单id为【%s】工单信息不存在", mesWorkOrder.getId());
        }

        //查询物料信息
        MesPart mesPart = iMesPartService.getMesPartByPartNo(mesWorkOrderDb.getPartNo(), mesWorkOrderDb.getOrganizeCode());
        //查询产线信息
        MesWorkCenter mesWorkCenter = getMesWorkCenter(mesWorkOrderDb);
        //获取生产版本
        MesProductVersion mesProductVersion = getProductVersion(mesWorkOrderDb.getOrganizeCode(),mesWorkOrderDb.getPartNo(),mesWorkOrderDb.getProductVersion());
        String bomVersion =  mesProductVersion.getAlternativePartList();
        String partProdGroupCode = mesWorkOrderDb.getPartProdGroupCode();
        // partProdGroupCode 不为空，查询的数据不为空，查询数据中的获取bom方式不为空且枚举是 基于默认BOM ，才bomversion = null 否则都是通过生产版本找bom
        if (!StringUtil.isEmpty(partProdGroupCode)) {
            DdlPackBean partProdBean = DdlPackBean.getDdlPackBean(mesWorkOrderDb.getOrganizeCode());
            DdlPreparedPack.getStringEqualPack(partProdGroupCode, "partProdGroupCode", partProdBean);
            List<MesPartProdGroup> mesPartProdGroupList = mesPartProdGroupRao.findByHqlTopWhere(partProdBean, 1);

            // 没有零件生产组用默认找生产版本、有的话找获取bom方式枚举，枚举是基于默认BOM 则直接找
            if (!mesPartProdGroupList.isEmpty()) {
                MesPartProdGroup mesPartProdGroup = mesPartProdGroupList.get(0);
                if (mesPartProdGroup.getEnableBomVersion() != null && mesPartProdGroup.getEnableBomVersion() == MesExtEnumUtil.ENABLE_BOM_VERSION.ENABLE_BOM_VERSION_2.getValue()) {
                    log.info("基于默认BOM获取BOM");
                    bomVersion = "";
                }
            }
        }
        //物料+生产版本获取bom信息
        List<MesBom> mesBoms = findBomList(mesPart.getOrganizeCode(), mesPart, bomVersion);

        //汇报数量与未完成数量的修改 先给入参对象赋值, 下面没有报错的情况下再赋给工单DB对象
        mesWorkOrder.setReportedQty(MathOperation.add(mesWorkOrder.getNum(), mesWorkOrderDb.getReportedQty()));
        double unCompleteQty = MathOperation.sub(mesWorkOrderDb.getQty(), mesWorkOrderDb.getReportedQty());
        mesWorkOrder.setUnCompleteQty(unCompleteQty > 0 ? unCompleteQty : 0);

        //更新工单状态
        if (mesWorkOrder.getReportedQty() > mesWorkOrderDb.getQty()) {
            // 以下则是超工单逻辑
            // 如果产线中没有配置超工单，则直接阻断
            if (!Objects.equals(CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue(), mesWorkCenter.getIsCheckOrderQty())) {
                MesPcnException.throwMesBusiException("请检查工单数量,工单号[%s],工单数量[%s]报工数量[%s],且未配置超工单!", mesWorkOrderDb.getWorkOrderNo(), mesWorkOrderDb.getQty(), mesWorkOrderDb.getReportedQty());
            }
            // 如果配置了超工单，且比例已经超过了配置的超工单比例，也需要阻断
            double rate = MathOperation.div((mesWorkOrder.getReportedQty() - mesWorkOrderDb.getQty()), mesWorkOrderDb.getQty());
            if (rate > MathOperation.div(mesWorkCenter.getOrderRate(), 100)) {
                MesPcnException.throwMesBusiException("请检查工单数量,工单号[%s],工单数量[%s]报工数量[%s],配置了超工单,但超过了比例[%s]!", mesWorkOrderDb.getWorkOrderNo(), mesWorkOrderDb.getQty(), mesWorkOrderDb.getReportedQty(), mesWorkCenter.getOrderRate());
            }
            mesWorkOrderDb.setWorkOrderStatus(MesExtEnumUtil.ORDER_STATUS.COMPLETE.getValue());
        } else if (Objects.equals(mesWorkOrder.getReportedQty(), mesWorkOrderDb.getQty())) {
            mesWorkOrderDb.setWorkOrderStatus(MesExtEnumUtil.ORDER_STATUS.COMPLETE.getValue());
        } else {
            mesWorkOrderDb.setWorkOrderStatus(MesExtEnumUtil.ORDER_STATUS.PROCESS.getValue());
        }

        mesWorkOrderDb.setDescription(mesWorkOrder.getDescription());
        mesWorkOrderDb.setReportedQty(mesWorkOrder.getReportedQty());
        mesWorkOrderDb.setUnCompleteQty(mesWorkOrder.getUnCompleteQty());
        mesWorkOrderDb.setRemark(mesWorkOrder.getRemark());

        mesWorkOrderDb.setCompleteQty(mesWorkOrderDb.getReportedQty());

        String sn = getReportSn(mesWorkOrder.getOrganizeCode(), userName, mesPart);
        //更新工单
        updateMesWorkOrder(mesWorkOrderDb, userName, MesExtEnumUtil.WORK_ORDER_LOG_TYPE.REPORT, mesWorkOrder.getNum());
        //记录条码表&加工记录表
        //insertMesProductionRecord(insertMesProduceSn(mesPart, sn, userName, mesWorkOrder),mesWorkOrderDb);
        insertMesProduceSn(mesPart, sn, userName, mesWorkOrder);
        //试制单不报工
        if (Objects.isNull(mesWorkOrderDb.getOrderFlag()) || !MesExtEnumUtil.ORDER_TYPE_IDENTIFICATION.P.getValue().equals(mesWorkOrderDb.getOrderFlag())) {
            //保存数据
            List<MesProductOffLine> mesProductOffLineList = new ArrayList<>();
            String nowTime = StringUtil.isEmpty(mesWorkOrder.getDescription()) ? TimeTool.getNowTime(true) : mesWorkOrder.getDescription();
            for (MesBom mesBom : mesBoms) {
                mesProductOffLineList.add(creatMesProductOffLine(userName, mesWorkOrder, mesProductVersion, sn, mesBom, nowTime, mesBoms.size()));
            }
            mesProductOffLineRDao.saveAll(mesProductOffLineList);
        }
    }

    @Override
    public void doProductReportReversal(MesWorkOrder mesWorkOrder,MesProductVersion mesProductVersion, String userName) {
        //查询物料信息
        MesPart mesPart = iMesPartService.getMesPartByPartNo(mesWorkOrder.getPartNo(), mesWorkOrder.getOrganizeCode());
        //物料+生产版本获取bom信息
        List<MesBom> mesBomList = findBomList(mesPart.getOrganizeCode(), mesPart, mesProductVersion.getAlternativePartList());
        //报工调整数量加-
        mesWorkOrder.setAdjustQty((MathOperation.add(mesWorkOrder.getNum(), mesWorkOrder.getAdjustQty())));
        //冲销数量大于工单完成数量报错
        if (mesWorkOrder.getAdjustQty() > mesWorkOrder.getReportedQty()) {
            MesPcnException.throwMesBusiException("冲销数量【%s】大于工单完成数量【%s】,不允许冲销", mesWorkOrder.getAdjustQty(), mesWorkOrder.getReportedQty());
        }
        String sn = getReportSn(mesWorkOrder.getOrganizeCode(), userName, mesPart);
        //更新工单
        updateMesWorkOrder(mesWorkOrder, userName, MesExtEnumUtil.WORK_ORDER_LOG_TYPE.REPORT_ADJUST,mesWorkOrder.getNum());
        //试制单不报工
        if (Objects.isNull(mesWorkOrder.getOrderFlag()) || !MesExtEnumUtil.ORDER_TYPE_IDENTIFICATION.P.getValue().equals(mesWorkOrder.getOrderFlag())) {
            //保存数据
            List<MesProductOffLine> mesProductOffLineList = new ArrayList<>();
            String nowTime = TimeTool.getNowTime(true);
            for (MesBom mesBom : mesBomList) {
                mesProductOffLineList.add(creatMesProductOffLine(userName, mesWorkOrder, mesProductVersion, sn, mesBom, nowTime, mesBomList.size()));
            }
            mesProductOffLineRDao.saveAll(mesProductOffLineList);
        }
    }

    @Override
    public void doMesWorkOrderSortReport(String organizeCode, Integer pageSize, String userName) {
        //数据初始化
        MesWorkOrderSortReportModel model = initMesWorkOrderSortReportModel(organizeCode, pageSize, userName);
        if (CollectionUtils.isEmpty(model.getMesProductionRecordList())) {
            log.info("不存在未处理的加工记录信息");
            return;
        }
        //排序汇报
        List<MesProductOffLine> mesProductOffLineList = null;
        List<MesMove> mesMoveList = null;
        List<MesJisShipping> shippingList = null;
        boolean isReport = false;
        boolean isMove = false;
        for (String orderNo : model.getMesProductionRecordMap().keySet()) {
            mesProductOffLineList = new ArrayList<>();
            mesMoveList = new ArrayList<>();
            shippingList = new ArrayList<>();
            try {
                //加工记录
                MesProductionRecord record = model.getMesProductionRecordMap().get(orderNo).iterator().next();
                //工单信息
                MesWorkOrder workOrder = getMesWorkOrder(model.getMesWorkOrderMap(), record.getWorkOrderNo());
                //生产版本
                MesProductVersion mesProductVersion = getMesProductVersion(model.getMesProductVersionMap(), record.getReportPartNo(), workOrder.getProductVersion());
                isReport = false;
                isMove = false;
                if (MesExtEnumUtil.MES_REPORT_TYPE.REPORT.getValue() == record.getReportType()) {
                    isReport = true;
                } else if (MesExtEnumUtil.MES_REPORT_TYPE.MOVE.getValue() == record.getReportType()) {
                    isMove = true;
                } else if (MesExtEnumUtil.MES_REPORT_TYPE.REPORT_MOVE.getValue() == record.getReportType()) {
                    isReport = true;
                    isMove = true;
                } else if (MesExtEnumUtil.MES_REPORT_TYPE.CUSTOMER_SUPPLY_MOVE.getValue() == record.getReportType()) {
                    customerSupplyMove(model, workOrder, record, mesProductVersion, findMesWorkOrderPartList(model.getMesWorkOrderPartMap(), record.getWorkOrderNo()), mesProductOffLineList, mesMoveList,shippingList);
                } else {
                    MesPcnException.throwMesBusiException("工单:%s汇报类型报工类型未维护", record.getWorkOrderNo());
                }
                //非客供品移库类型
                if (MesExtEnumUtil.MES_REPORT_TYPE.CUSTOMER_SUPPLY_MOVE.getValue() != record.getReportType()) {
                    //成品汇报
                    if (isReport) {
                        for (MesWorkOrderPart mesWorkOrderPart : findMesWorkOrderPartList(model.getMesWorkOrderPartMap(), record.getWorkOrderNo())) {
                            mesProductOffLineList.add(getMesProductOffLine(model, record, workOrder, mesProductVersion, mesWorkOrderPart,false));
                        }
                    }
                    //移库
                    if (isMove) {
                        MesProdRuleSortCfg mesProdRuleSortCfg = getMesProdRuleSortCfg(organizeCode, record);
                        mesMoveList.add(createMove(getMesPartSap(model.getMesPartSapMap(), record.getReportPartNo()), record, mesProdRuleSortCfg.getSrcErpLocation(), mesProdRuleSortCfg.getDestErpLocation(), workOrder.getErpWorkCenter(), 1d, MesExtEnumUtil.MOVE_TYPE.FINISH_PRODUCTS_MOVE.getValue(),userName));
                    }
                }
                //保存报工记录 试制单不报工
                if (CollectionUtils.isNotEmpty(mesProductOffLineList)) {
                    List<MesProductOffLine> finalMesProductOffLineList = mesProductOffLineList;
                    mesProductOffLineList.forEach(item -> item.setBomTotalSize(finalMesProductOffLineList.size()));
                    mesProductOffLineRDao.saveAll(mesProductOffLineList);
                }
                //保存移库记录
                if (CollectionUtils.isNotEmpty(mesMoveList)) {
                    mesMoveRepository.saveAll(mesMoveList);
                }
                //保存发运记录
                if (CollectionUtils.isNotEmpty(shippingList)) {
                    mesJisShippingRepository.saveAll(shippingList);
                }
                //更新加工记录
                productionRecordService.updateProductionRecord(model.getMesProductionRecordMap().get(orderNo), userName, MesExtEnumUtil.REPORT_STATUS.REPORT_STATUS_20.getValue(), "");
                //更新工单汇报数量
                workOrder.setSystemSyncStatus(CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue());
                workOrder.setReportedQty(workOrder.getReportedQty()+MesPcnExtConstWords.ONE);
                workOrder.setModifyDatetime((new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS")).format(new Date()));
                workOrder.setModifyUser(userName);
                workOrderExtService.update(workOrder);
            } catch (Exception e) {
                String msg = e.getMessage();
                if (e instanceof ImppBusiException) {
                    msg = ((ImppBusiException) e).getErrorDetail();
                }
                log.error("工单:{}报工失败{}", orderNo, msg);
                productionRecordService.updateProductionRecord(model.getMesProductionRecordMap().get(orderNo), userName, MesExtEnumUtil.REPORT_STATUS.REPORT_STATUS_40.getValue(), msg);
            }
        }
    }

    @Override
    public void doMesWorkOrderNoSortReport(String organizeCode, Integer pageSize, String userName) {
        String workCenterCodes = configService.getCfgValue(organizeCode, "MES_PCN_REPORT_CENTER");
        //查询非排序工单 有工单
        List<MesProductionRecord> recordNoReportHasOrderNoList = mesProductionRecordDao.findMesProductionRecordNoReport(organizeCode, MesExtEnumUtil.WORK_CENTER_TYPE.NOSORT.getValue(), pageSize, !StringUtils.isEmpty(workCenterCodes) ? Arrays.asList(workCenterCodes.split(",")) : null, true);
        if (CollectionUtils.isEmpty(recordNoReportHasOrderNoList)) {
            LOGGER.error("非排序有加工单加工记录中没有数据，无需处理");
        }
        //查询非排序工单 有工单
        List<MesProductionRecord> recordNoReportNotHasOrderNoList = mesProductionRecordDao.findMesProductionRecordNoReport(organizeCode, MesExtEnumUtil.WORK_CENTER_TYPE.NOSORT.getValue(), pageSize, !StringUtils.isEmpty(workCenterCodes) ? Arrays.asList(workCenterCodes.split(",")) : null, false);
        if (CollectionUtils.isEmpty(recordNoReportNotHasOrderNoList)) {
            LOGGER.error("非排序无加工单加工记录中没有数据，无需处理");
        }

        //处理无工单加工记录
        if(!CollectionUtils.isEmpty(recordNoReportNotHasOrderNoList)){
            for (MesProductionRecord mesProductionRecord : recordNoReportNotHasOrderNoList) {
                LOGGER.info("----------处理无工单加工记录报工开始-------{}", mesProductionRecord.getProductSn());
                doProductReportByRecord(mesProductionRecord, organizeCode, userName);
                LOGGER.info("----------处理无工单加工记录报工结束-------{}", mesProductionRecord.getProductSn());
            }
        }
        //处理有工单加工记录
        if(!CollectionUtils.isEmpty(recordNoReportHasOrderNoList)){
            try {
                for (MesProductionRecord mesProductionRecord : recordNoReportHasOrderNoList) {
                    LOGGER.info("----------处理有工单加工记录报工开始-------{}", mesProductionRecord.getProductSn());
                    doProductHasOrderReportByRecord(mesProductionRecord, organizeCode, userName);
                    LOGGER.info("----------处理有工单加工记录报工结束-------{}", mesProductionRecord.getProductSn());
                }
            } catch (ImppBusiException e) {
            }
        }
    }

    private MesProductVersion getProductVersion(String organizeCode,String partNo,String productVersion) {
        MesProductVersion mesProductVersion = mesProductVersionService.getMesProductVersion(organizeCode,partNo,productVersion);
        if (null == mesProductVersion) {
            throw new ImppBusiException(String.format("物料【%s】生产版本【%s】信息不存在", partNo, productVersion));
        }
        return mesProductVersion;
    }

    private MesWorkCenter getMesWorkCenter(MesWorkOrder mesWorkOrderDb) {
        MesWorkCenter mesWorkCenter = mesProdOrgExtService.getWorkCenterDb(mesWorkOrderDb.getOrganizeCode(), mesWorkOrderDb.getWorkCenterCode());
        if (Objects.isNull(mesWorkCenter)) {
            MesPcnException.throwMesBusiException("产线【%s】不存在", mesWorkOrderDb.getWorkCenterCode());
        }
        return mesWorkCenter;
    }

    @Override
    public MesWorkOrder insertMesWorkOrder(List<MesProductionRecord> mesProductionRecordList, List<MesShift> mesShiftList, String organizeCode, String userName) {

            MesProductionRecord mesProductionRecord = mesProductionRecordList.get(0);

            String workCenterCode = mesProductionRecord.getWorkCenterCode();
            String partNo = mesProductionRecord.getPartNo();
            //查询工作中心
            DdlPackBean ddlPackBean = DdlPackBean.getDdlPackBean(organizeCode);
            DdlPreparedPack.getStringEqualPack(workCenterCode, "workCenterCode", ddlPackBean);
            MesWorkCenter mesWorkCenter = mesWorkCenterRDao.getByProperty(ddlPackBean);
            if (Objects.isNull(mesWorkCenter)) {
                LOGGER.info("产线【%s】不存在:{}", workCenterCode );
                MesPcnException.throwMesBusiException("产线【%s】不存在", workCenterCode);
            }
            MesWorkOrder item = new MesWorkOrder();
            item.setOrganizeCode(organizeCode);
            item.setWorkCenterCode(workCenterCode);
            item.setErpWorkCenter(mesWorkCenter.getErpWorkCenter());
            item.setPartNo(partNo);
            item.setWorkOrderType(MesExtEnumUtil.ORDER_TYPE.UN_SORT.getValue());
            item.setOrderFlag(MesExtEnumUtil.ORDER_TYPE_IDENTIFICATION.N.getValue());
            List<MesShift> collect = mesShiftList.stream().filter(s -> s.getShiftCode().equalsIgnoreCase("20")).collect(Collectors.toList());
            MesShift mesShift = collect.get(0);
            item.setShiftCode(mesShift.getShiftCode());
            item.setShiftName(mesShift.getShiftName());
            String today = TimeTool.getToday();
            String dayBefore = new SimpleDateFormat("yyyy-MM-dd").format(TimeTool.getDateBefore(new Date(), 20));
            item.setPlanStartTime(dayBefore + " " + mesShift.getStartTime());
            item.setPlanEndTime(today + " " + mesShift.getEndTime());

            //生成工单号
            List<String> orderList = new ArrayList<>();
            GenSerialNoModel genSerialNoModel = new GenSerialNoModel("MES_WORK_CENTER_ORDER_NO");
            genSerialNoModel.setPartNo(workCenterCode);
            orderList = syncFuncService.syncSerialNo(genSerialNoModel, userName, organizeCode, 1).getResultList();

            String orderNo = "A-" + orderList.get(0);
            DdlPackBean orderPackBean = DdlPackBean.getDdlPackBean(item.getOrganizeCode());
            DdlPreparedPack.getStringEqualPack(orderNo, "workOrderNo", orderPackBean);
            if (mesWorkOrderRDao.isExitByHql(orderPackBean)) {
                LOGGER.info("产线【%s】单号流水码生成重复:{}", workCenterCode );
                MesPcnException.throwMesBusiException("产线【%s】不存在", workCenterCode);
            }
            item.setWorkOrderNo(orderNo);
            item.setPartName(mesProductionRecord.getPartName());
            //生产时间新增取当天
            item.setProduceTime(TimeTool.getToday());
            item.setProductTime(item.getProduceTime());
            item.setUnCompleteQty(0d);
            item.setQty(mesProductionRecordList.stream().mapToDouble(MesProductionRecord::getQty).sum());
            item.setCompleteQty(item.getQty());
            item.setReportedQty(item.getQty());
            item.setWorkOrderStatus(MesExtEnumUtil.ORDER_STATUS.COMPLETE.getValue());


            //校验标识
            //若工单类型为排序
            if (item.getWorkOrderType() == MesExtEnumUtil.ORDER_TYPE.UN_SORT.getValue()) {
                //校验产线类型和工单类型是否匹配
                if (!Objects.equals(mesWorkCenter.getCenterType(), MesExtEnumUtil.ORDER_TYPE.UN_SORT.getValue())) {
                    MesPcnException.throwMesBusiException("产线【%s】类型为【%s】，请选择非排序产线", mesWorkCenter.getWorkCenterCode(),
                            MesExtEnumUtil.WORK_CENTER_TYPE.valueOfDescription(mesWorkCenter.getCenterType()));
                }

                //校验物料生产版本是否存在
                MesProductVersion mesProductVersion = checkMesProductVersion(item);
                if (Objects.isNull(mesProductVersion)) {
                    return null;
                }
                item.setProductVersion(mesProductVersion.getProductVersion());
                //获取bom信息
                List<MesBom> bomList = findMesBomByPartNoAndBomVersion(item.getPartNo(), item.getOrganizeCode(), mesProductVersion.getAlternativePartList());
                if (org.springframework.util.CollectionUtils.isEmpty(bomList)) {
                    return null;
                }
            }

            item.setSystemSyncStatus(CommonEnumUtil.TRUE_OR_FALSE.FALSE.getValue());
            item.setFid(UUID.randomUUID().toString());
            ConvertBean.serviceModelInitialize(item, userName);
            MesWorkOrder mesWorkOrder = workOrderRepository.insert(item);
            saveMesWorkOrderLog(mesWorkOrder,MesExtEnumUtil.WORK_ORDER_LOG_TYPE.INSERT.getValue(),item.getQty());

        return mesWorkOrder;
    }

    private MesProductVersion checkMesProductVersion(MesWorkOrder item) {
        DdlPackBean ddlPackBean = DdlPackBean.getDdlPackBean(item.getOrganizeCode());
        DdlPreparedPack.getStringEqualPack(item.getPartNo(), "partNo", ddlPackBean);
        DdlPreparedPack.getStringEqualPack(item.getErpWorkCenter(), "workCenterCode", ddlPackBean);
        List<MesProductVersion> mesProductVersions = mesProductVersionRDao.findByHqlWhere(ddlPackBean);
        if (CollectionUtils.isEmpty(mesProductVersions)) {
            LOGGER.info("物料{}产线{}生产版本{}信息不存在",  item.getPartNo(), item.getWorkCenterCode(), item.getProductVersion() );
            MesPcnException.throwMesBusiException("物料【%s】产线【%s】生产版本【%s】信息不存在", item.getPartNo(), item.getWorkCenterCode(), item.getProductVersion());
        }
        MesProductVersion mesProductVersion = null;
        List<MesProductVersion> mesProductOneVersions = mesProductVersions.stream().filter(productVersion -> Objects.equals(productVersion.getProductVersion(),"0001")).collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(mesProductOneVersions)) {
            mesProductVersion = mesProductOneVersions.get(0);
        } else {
            mesProductVersion = mesProductVersions.get(0);
        }
        return mesProductVersion;
    }

    private Date transfer(String dateStr) {
        return DateUtil.parse(dateStr, (DateParser) DatePattern.NORM_DATETIME_FORMAT);
    }

    private String getReportSn(String organizeCode, String userName, MesPart part) {
        return syncFuncService.syncSerialNo(new GenSerialNoModel(MesCommonConstant.REPORT_SN).partSnParam(part.getPartSnParam()).organizeCode(organizeCode),userName, organizeCode, 1).getResultList().iterator().next().toString();
    }

    private MesProductOffLine creatMesProductOffLine(String userName, MesWorkOrder mesWorkOrder, MesProductVersion mesProductVersion,
                                                     String sn, MesBom mesBom, String nowTime,Integer bomTotalSize) {
        MesProductOffLine newMesProductOffLine;
        newMesProductOffLine = new MesProductOffLine();
        newMesProductOffLine.setReportPartNo(mesWorkOrder.getPartNo());
        newMesProductOffLine.setReportPartNameRdd(mesWorkOrder.getPartName());
        newMesProductOffLine.setReportSn(sn);
        newMesProductOffLine.setItemPartNo(mesBom.getItemPartNo());
        newMesProductOffLine.setItemPartName(mesBom.getItemPartName());
        newMesProductOffLine.setItemQty(MathOperation.mul(mesWorkOrder.getNum(), mesBom.getItemQty()));
        newMesProductOffLine.setAlort(mesProductVersion.getReceiveInventoryPoint());
        newMesProductOffLine.setStgeLoc(mesProductVersion.getShipInventoryPoint());
        newMesProductOffLine.setQty(Double.valueOf(mesWorkOrder.getNum()));
        newMesProductOffLine.setBomVersion(mesWorkOrder.getProductVersion());
        newMesProductOffLine.setSerialNumber(sn);
        newMesProductOffLine.setUnit(mesBom.getUnit());
        newMesProductOffLine.setItemUnit(mesBom.getItemUnit());
        newMesProductOffLine.setWorkOrderNo(mesWorkOrder.getWorkOrderNo());
        newMesProductOffLine.setWorkOrderType(mesWorkOrder.getWorkOrderType());
        newMesProductOffLine.setWorkCenterCode(mesProductVersion.getWorkCenterCode());
        newMesProductOffLine.setWorkCellCode(mesWorkOrder.getWorkCellCode());
        newMesProductOffLine.setReportType(MesExtEnumUtil.MES_ALL_REPORT_TYPE.REPORT.getValue());
        newMesProductOffLine.setSapWorkCenter(mesProductVersion.getWorkCenterCode());
        newMesProductOffLine.setOrganizeCode(mesWorkOrder.getOrganizeCode());
        newMesProductOffLine.setDescription(nowTime);
        newMesProductOffLine.setBomTotalSize(bomTotalSize);
        newMesProductOffLine.setBomCode(mesBom.getBomCode());
        ConvertBean.serviceModelInitialize(newMesProductOffLine, userName);
        return newMesProductOffLine;
    }

    private MesProduceSn insertMesProduceSn(MesPart mesPart, String sn, String userName, MesWorkOrder mesWorkOrder) {
        MesProduceSn mesProduceSn = new MesProduceSn();
        mesProduceSn.setSerialNumber(snowflakeIdMaker.nextId() + "");
        mesProduceSn.setProductSn(sn);
        mesProduceSn.setCustSn(sn);
        mesProduceSn.setPartNo(mesPart.getPartNo());
        mesProduceSn.setPartName(mesPart.getPartName());
        mesProduceSn.setProcessLabelTemplate(mesPart.getProcessLabelTemplate());
        mesProduceSn.setCustLabelTemplate(mesPart.getCustLabelTemplate());
        mesProduceSn.setProdLabelTemplate(mesPart.getProductLabelTemplate());
        mesProduceSn.setQty(Double.parseDouble(mesWorkOrder.getNum() + ""));
        mesProduceSn.setSnStatus(MesExtEnumUtil.PRODUCE_SN_STATUS.CREATE.getValue());
        mesProduceSn.setQcStatus(MesExtEnumUtil.PRODUCE_QC_STATUS.QUALIFIED.getValue());
        mesProduceSn.setLotNo(TimeTool.getToday());
        mesProduceSn.setWorkOrderNo(mesWorkOrder.getWorkOrderNo());
        mesProduceSn.setWorkCenterCode(mesWorkOrder.getWorkCenterCode());
        mesProduceSn.setAreaCode(mesWorkOrder.getAreaCode());
        mesProduceSn.setPrintCount(MesPcnExtConstWords.ONE);
        mesProduceSn.setPrintStatus(MesExtEnumUtil.PRINT_STATUS.PRINTED.getValue());
        mesProduceSn.setOrganizeCode(mesPart.getOrganizeCode());
        mesProduceSn.setFid(UUID.randomUUID().toString());
        ConvertBean.serviceModelInitialize(mesProduceSn, userName);
        return mesProduceSnRao.insert(mesProduceSn);
    }

    private void insertMesProductionRecord(MesProduceSn mesProduceSn, MesWorkOrder mesWorkOrder) {
        //生成加工记录
        MesProductionRecord productionRecord = new MesProductionRecord();
        BeanUtils.copyProperties(mesProduceSn, productionRecord);
        productionRecord.setReportType(MesExtEnumUtil.NOSORT_REPORT_TYPE.REPORT.getValue());
        productionRecord.setReportStatus(MesExtEnumUtil.REPORT_STATUS.REPORT_STATUS_20.getValue());
        productionRecord.setModuleStatisticsStatus(CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue());
        productionRecord.setAreaCode(mesWorkOrder.getAreaCode());
        productionRecord.setWorkCenterCode(mesWorkOrder.getWorkCenterCode());
        productionRecord.setWorkCellCode(mesWorkOrder.getWorkCellCode());
        productionRecord.setIsComplete(CommonEnumUtil.TRUE_OR_FALSE.TRUE.getValue());
        productionRecord.setCompleteDateTime(productionRecord.getModifyDatetime());
        productionRecord.setFid(UUID.randomUUID().toString());
        productionRecord.setOneMouldMoreId(UUID.randomUUID().toString());
        productionRecordRao.insert(productionRecord);
    }

    public MesWorkOrderSortReportModel initMesWorkOrderSortReportModel(String organizeCode, Integer pageSize, String userName) {
        MesWorkOrderSortReportModel model = new MesWorkOrderSortReportModel(organizeCode, userName, mesProductionRecordDao.findMesProductionRecordNoReport(organizeCode, MesExtEnumUtil.WORK_CENTER_TYPE.SORT.getValue(), pageSize,null,true));
        model.setMesWorkOrderList(workOrderExtService.getWorkOrderList(organizeCode, model.getWorkOrderList()));
        model.setMesMoveRuleList(mesMoveRuleService.findMesMoveRuleByPartProdGroupCode(model.getPartProdGroupCodeList(), organizeCode,MesExtEnumUtil.MOVE_RULE_GROUP_TYPE.MOVE_RULE_GROUP_TYPE_20.getValue()));
        model.setMesWorkOrderPartList(findMesWorkOrderPartByOrderNo(organizeCode, model.getWorkOrderList()));
        model.setMesPartList(iMesPartService.findMesPartByPartNo(model.getPartNoAllList(), organizeCode));
        model.setMesPartSapList(iMesPartService.findMesPartSapByPartNo(model.getPartNoAllList(), organizeCode));
        model.setMesProductVersionList(mesProductVersionService.findMesProductVersionByPartNo(organizeCode, model.getPartNoList()));
        return model;
    }

    private void customerSupplyMove(MesWorkOrderSortReportModel model, MesWorkOrder workOrder, MesProductionRecord record, MesProductVersion mesProductVersion, List<MesWorkOrderPart> mesWorkOrderPartList, List<MesProductOffLine> mesProductOffLineList, List<MesMove> mesMoveList,List<MesJisShipping> shippingList) {
        boolean isItemMove;
        boolean isItemReport;
        MesMoveRule moveRule;
        //移库规则
        Map<String, List<MesMoveRule>> mesMoveRuleMap = findMesMoveRuleList(model.getMesMoveRuleMap(), workOrder.getPartProdGroupCode()).stream().filter(t -> Objects.nonNull(t.getSrcType())).collect(Collectors.groupingBy(MesMoveRule::getSrcType));
        for (MesWorkOrderPart orderPart : mesWorkOrderPartList) {
            isItemReport = false;
            isItemMove = false;
            MesPart itemPart = getMesPart(model.getMesPartMap(), orderPart.getPartNo());
            if (StringUtil.isEmpty(itemPart.getEsd()) || !mesMoveRuleMap.containsKey(itemPart.getEsd())) {
                LOGGER.info("客供品移库零件【{}】维护的ESD属性未匹配到零件生产组【{}】中的移库规则", orderPart.getPartNo(),workOrder.getPartProdGroupCode());
                continue;
            }
            moveRule = mesMoveRuleMap.get(itemPart.getEsd()).iterator().next();
            if (MesExtEnumUtil.MOVE_TYPE_REPORT_TYPE.REPORT_MOVE.getValue() == moveRule.getReportType()) {
                isItemMove = true;
                isItemReport = true;
            } else if (MesExtEnumUtil.MOVE_TYPE_REPORT_TYPE.REPORT.getValue() == moveRule.getReportType()) {
                isItemReport = true;
            } else if (MesExtEnumUtil.MOVE_TYPE_REPORT_TYPE.MOVE.getValue() == moveRule.getReportType()) {
                isItemMove = true;
            } else {
                LOGGER.info("客供品移库零件【{}】在零件生产组【{}】的移库规则中，汇报类型未维护", orderPart.getPartNo(), workOrder.getPartProdGroupCode());
                continue;
            }
            //汇报
            if (isItemReport) {
                mesProductOffLineList.add(getMesProductOffLine(model, record, workOrder, mesProductVersion,orderPart,true));
            }
            //移库
            if (isItemMove) {
                mesMoveList.add(createMove(getMesPartSap(model.getMesPartSapMap(), orderPart.getPartNo()), record, moveRule.getErpSrcLocateNo(), moveRule.getErpDestLocateNo(), workOrder.getErpWorkCenter(), orderPart.getItemQty(), MesExtEnumUtil.MOVE_TYPE.RAW_MATERIAL_MOVE.getValue(),model.getUserName()));
            }
        }
    }

    private void customerSupplyMove(MesProductionRecord productionRecord,MesWorkOrder mesWorkOrder, MesProductVersion mesProductVersion, List<MesBom> mesBoms, List<MesProductOffLine> mesProductOffLineList, List<MesMove> mesMoveList,String userName) {
        boolean isItemMove;
        boolean isItemReport;
        MesMoveRule moveRule;
        //查询零件生产组的移库规则
        List<MesMoveRule> moveRules = mesMoveRuleService.findMesMoveRuleByPartProdGroupCode(mesWorkOrder.getPartProdGroupCode(), mesWorkOrder.getOrganizeCode(),MesExtEnumUtil.MOVE_RULE_GROUP_TYPE.MOVE_RULE_GROUP_TYPE_20.getValue());
        if(CollectionUtils.isEmpty(moveRules)) {
            LOGGER.info("工单：{} 生产组：{}，移库规则信息不存在",mesWorkOrder.getWorkOrderNo(),mesWorkOrder.getPartProdGroupCode());
            return;
        }
        Map<String, List<MesMoveRule>> mesMoveRuleMap = moveRules.stream().filter(t -> Objects.nonNull(t.getSrcType())).collect(Collectors.groupingBy(MesMoveRule::getSrcType));
        List<String> itemPartNoList = mesBoms.stream().map(MesBom::getItemPartNo).collect(Collectors.toList());
        //子物料SAP下发信息
        Map<String, List<MesPartSap>> mesPartSapMap = getPartSapMap(mesWorkOrder, itemPartNoList);
        //获取物料信息
        Map<String, List<MesPart>> itemPartMap = getItemPartMap(mesWorkOrder, itemPartNoList);
        for (MesBom mesBom : mesBoms) {
            isItemReport = false;
            isItemMove = false;
            moveRule = null;
            MesPart itemPart = null;
            if (itemPartMap.containsKey(mesBom.getItemPartNo())) {
                itemPart = itemPartMap.get(mesBom.getItemPartNo()).iterator().next();
                if (!StringUtil.isEmpty(itemPart.getEsd()) && mesMoveRuleMap.containsKey(itemPart.getEsd())) {
                    moveRule = mesMoveRuleMap.get(itemPart.getEsd()).iterator().next();
                    if (MesExtEnumUtil.MOVE_TYPE_REPORT_TYPE.REPORT_MOVE.getValue() == moveRule.getReportType()) {
                        isItemMove = true;
                        isItemReport = true;
                    } else if (MesExtEnumUtil.MOVE_TYPE_REPORT_TYPE.REPORT.getValue() == moveRule.getReportType()) {
                        isItemReport = true;
                    } else if (MesExtEnumUtil.MOVE_TYPE_REPORT_TYPE.MOVE.getValue() == moveRule.getReportType()) {
                        isItemMove = true;
                    }
                }
            }
            //汇报
            if (isItemReport) {
                String reportSn = String.valueOf(snowflakeIdMaker.nextId());
                MesProductVersion itemVersion = getProductVersion(mesWorkOrder.getOrganizeCode(), mesBom.getItemPartNo(), mesProductVersion.getProductVersion());
                List<MesBom> bomList = null;
                try {
                    bomList = findBomList(mesWorkOrder.getOrganizeCode(), itemPart, itemVersion.getAlternativePartList());
                } catch (Exception e) {
                }
                //bom下的子节汇报
                if (!CollectionUtils.isEmpty(bomList)) {
                    for (MesBom bom : bomList) {
                        mesProductOffLineList.add(getMesProductOffLine(productionRecord, userName, mesWorkOrder, itemVersion, bom, reportSn, true, MesPcnExtConstWords.SAVE_PRODUCT_VERSION_FLAG.equals(itemPart.getEsd()), bomList.size()));
                    }
                } else {
                    mesProductOffLineList.add(getMesProductOffLine(productionRecord, userName, mesWorkOrder, itemVersion, mesBom, reportSn, true,false, MesPcnExtConstWords.ONE));
                }
            }
            //移库
            if (isItemMove) {
                MesPartSap mesPartSap = null;
                if (mesPartSapMap.containsKey(mesBom.getItemPartNo())) {
                    mesPartSap = mesPartSapMap.get(mesBom.getItemPartNo()).iterator().next();
                }
                MesMove move = createMove(mesPartSap, moveRule.getErpSrcLocateNo(), moveRule.getErpDestLocateNo(), mesBom.getOrganizeCode(), mesWorkOrder.getErpWorkCenter(), mesBom.getItemQty(), MesExtEnumUtil.MOVE_TYPE.RAW_MATERIAL_MOVE.getValue());
                move.setMatnr(mesBom.getItemPartNo());
                ConvertBean.serviceModelInitialize(move, userName);
                mesMoveList.add(move);
            }
        }
        //bom下的子节汇报，总成也需要汇报
        if (!CollectionUtils.isEmpty(mesProductOffLineList)) {
            for (MesBom mesBom : mesBoms) {
                mesProductOffLineList.add(getMesProductOffLine(productionRecord, userName, mesWorkOrder, mesProductVersion, mesBom, productionRecord.getProductSn(), false,false,mesBoms.size()));
            }
        }
    }

    private Map<String, List<MesPartSap>> getPartSapMap(MesWorkOrder mesWorkOrder, List<String> itemPartNoList) {
        List<MesPartSap> mesPartSapList = iMesPartService.findMesPartSapByPartNo(itemPartNoList, mesWorkOrder.getOrganizeCode());
        return mesPartSapList.stream().filter(t -> Objects.nonNull(t.getPartNo())).collect(Collectors.groupingBy(MesPartSap::getPartNo));
    }

    private Map<String, List<MesPart>> getItemPartMap(MesWorkOrder mesWorkOrder, List<String> itemPartNoList) {
        List<MesPart> itemPartList = iMesPartService.findMesPartByPartNo(itemPartNoList, mesWorkOrder.getOrganizeCode());
        return itemPartList.stream().filter(t -> Objects.nonNull(t.getPartNo())).collect(Collectors.groupingBy(MesPart::getPartNo));
    }

    private MesWorkOrder getMesWorkOrder(Map<String, List<MesWorkOrder>> workOrderMap, String workOrderNo) {
        MesWorkOrder workOrder = Objects.isNull(workOrderMap) || StringUtils.isEmpty(workOrderNo) || !workOrderMap.containsKey(workOrderNo) ? null : workOrderMap.get(workOrderNo).iterator().next();
        if (Objects.isNull(workOrder)) {
            MesPcnException.throwMesBusiException("工单:【%s】信息不存在", workOrderNo);
        }
        return workOrder;
    }

    private List<MesMoveRule> findMesMoveRuleList(Map<String, List<MesMoveRule>> mesMoveRuleMap, String code) {
        List<MesMoveRule> moveRuleList = Objects.isNull(mesMoveRuleMap) || StringUtils.isEmpty(code) || !mesMoveRuleMap.containsKey(code) ? null : mesMoveRuleMap.get(code);
        if (CollectionUtils.isEmpty(moveRuleList)) {
            MesPcnException.throwMesBusiException("零件生产组:【%s】移库规则信息不存在", code);
        }
        return moveRuleList;
    }

    private List<MesWorkOrderPart> findMesWorkOrderPartList(Map<String, List<MesWorkOrderPart>> workOrderPartMap, String workOrderNo) {
        List<MesWorkOrderPart> workOrderPartList = Objects.isNull(workOrderPartMap) || StringUtils.isEmpty(workOrderNo) || !workOrderPartMap.containsKey(workOrderNo) ? null : workOrderPartMap.get(workOrderNo);
        if (CollectionUtils.isEmpty(workOrderPartList)) {
            MesPcnException.throwMesBusiException("工单:【%s】零件信息表数据不存在", workOrderNo);
        }
        return workOrderPartList;
    }

    private MesPartSap getMesPartSap(Map<String, List<MesPartSap>> partSapMap, String partNo) {
        MesPartSap partSap = Objects.isNull(partSapMap) || StringUtils.isEmpty(partNo) || !partSapMap.containsKey(partNo) ? null : partSapMap.get(partNo).iterator().next();
        if (Objects.isNull(partSap)) {
            MesPcnException.throwMesBusiException("零件号:【%s】信息不存在", partNo);
        }
        return partSap;
    }

    private MesPart getMesPart(Map<String, List<MesPart>> partMap, String partNo) {
        MesPart partSap = Objects.isNull(partMap) || StringUtils.isEmpty(partNo) || !partMap.containsKey(partNo) ? null : partMap.get(partNo).iterator().next();
        if (Objects.isNull(partSap)) {
            MesPcnException.throwMesBusiException("零件号:【%s】信息不存在", partNo);
        }
        return partSap;
    }

    private MesProductVersion getMesProductVersion(Map<String, List<MesProductVersion>> productVersionMap, String reportPartNo, String productVersion) {
        Optional<MesProductVersion> optional = Objects.isNull(productVersionMap) || !productVersionMap.containsKey(reportPartNo) ? Optional.empty() : productVersionMap.get(reportPartNo).stream().filter(t -> t.getProductVersion().equals(productVersion)).findFirst();
        return optional.orElse(null);
    }

    @Override
    public List<MesWorkOrderPart> findMesWorkOrderPartByOrderNo(String organizeCode, List<String> workOrderNoList) {
        if (StringUtils.isEmpty(organizeCode) || CollectionUtils.isEmpty(workOrderNoList)) return null;
        DdlPackBean packBean = DdlPackBean.getDdlPackBean(organizeCode);
        if (workOrderNoList.size() == MesPcnExtConstWords.ONE){
            DdlPreparedPack.getStringEqualPack(workOrderNoList.iterator().next(), "workOrderNo", packBean);
        }else{
            DdlPreparedPack.getInPackList(workOrderNoList, "workOrderNo", packBean);
        }
        return mesWorkOrderPartRepository.findByHqlWhere(packBean);
    }

    @Override
    public MesWorkOrder getWorkOrderNoByCustSn(String organizeCode, String custSn) {
        if (StringUtils.isEmpty(organizeCode) || StringUtils.isEmpty(custSn)) return null;
        DdlPackBean packBean = DdlPackBean.getDdlPackBean(organizeCode);
        DdlPreparedPack.getStringEqualPack(custSn, "custSn", packBean);
        return mesWorkOrderRDao.getByProperty(packBean);
    }

    @Override
    public MesWorkOrder getWorkOrderNoByWorkOrderNo(String organizeCode, String workOrderNo) {
        if (StringUtils.isEmpty(organizeCode) || StringUtils.isEmpty(workOrderNo)) return null;
        DdlPackBean packBean = DdlPackBean.getDdlPackBean(organizeCode);
        DdlPreparedPack.getStringEqualPack(workOrderNo, "workOrderNo", packBean);
        return mesWorkOrderRDao.getByProperty(packBean);
    }

    private MesProdRuleSortCfg getMesProdRuleSortCfg(String organizeCode, MesProductionRecord record) {
        MesProdRuleSortCfg mesProdRuleSortCfg = mesProdRuleSortCfgDao.getMesProdRuleSortCfg(organizeCode, record.getReportPartNo(), record.getWorkCenterCode());
        if(Objects.isNull(mesProdRuleSortCfg)){
            MesPcnException.throwMesBusiException("汇报零件:%s 产线:%s排序加工规则不存在", record.getReportPartNo(), record.getWorkCenterCode());
        }
        return mesProdRuleSortCfg;
    }

    private MesProductOffLine getMesProductOffLine(MesWorkOrderSortReportModel model, MesProductionRecord record, MesWorkOrder workOrder, MesProductVersion mesProductVersion, MesWorkOrderPart mesWorkOrderPart,boolean isItemReport) {
        MesProductOffLine newMesProductOffLine = new MesProductOffLine();
        if(!isItemReport){
            newMesProductOffLine.setReportPartNo(record.getPartNo());
            newMesProductOffLine.setReportPartNameRdd(record.getPartName());
        }
        newMesProductOffLine.setItemPartNo(mesWorkOrderPart.getPartNo());
        newMesProductOffLine.setItemPartName(mesWorkOrderPart.getPartName());
        newMesProductOffLine.setItemQty(mesWorkOrderPart.getItemQty());
        if (mesProductVersion != null){
            newMesProductOffLine.setAlort(mesProductVersion.getReceiveInventoryPoint());
            newMesProductOffLine.setStgeLoc(mesProductVersion.getShipInventoryPoint());
            newMesProductOffLine.setSapWorkCenter(mesProductVersion.getWorkCenterCode());
        }
        newMesProductOffLine.setQty(1d);
        newMesProductOffLine.setReportSn(record.getProductSn());
        newMesProductOffLine.setBomVersion(workOrder.getProductVersion());
        newMesProductOffLine.setSerialNumber(record.getSerialNumber());
        newMesProductOffLine.setItemUnit(mesWorkOrderPart.getItemUnit());
        newMesProductOffLine.setWorkOrderNo(workOrder.getWorkOrderNo());
        newMesProductOffLine.setWorkOrderType(workOrder.getWorkOrderType());
        newMesProductOffLine.setWorkCenterCode(record.getWorkCenterCode());
        newMesProductOffLine.setWorkCellCode(record.getWorkCellCode());
        newMesProductOffLine.setReportType(record.getReportType());
        newMesProductOffLine.setOrganizeCode(model.getOrganizeCode());
        newMesProductOffLine.setDescription(record.getCompleteDateTime());
        ConvertBean.serviceModelInitialize(newMesProductOffLine, model.getUserName());
        return newMesProductOffLine;
    }

    private MesMove createMove(MesPartSap mesPart, MesProductionRecord record, String source, String target, String erpWorkCenterCode, Double qty, Integer moveType,String userName) {
        MesMove move = new MesMove();
        move.setMatnr(mesPart.getPartNo());
        move.setMeins(mesPart.getUnit());
        move.setOrganizeCode(record.getOrganizeCode());
        move.setFactoryCode(record.getOrganizeCode());
        move.setLgort(source);
        move.setUmlgo(target);
        move.setMenge(qty);
        move.setPostDate(TimeTool.getToday());
        move.setProductSn(record.getProductSn());
        move.setMoveType(moveType);
        move.setPostTime(TimeTool.getTimeShortWithColon());
        move.setWorkCenter(erpWorkCenterCode);
        ConvertBean.serviceModelInitialize(move, userName);
        return move;
    }

    private MesMove createMove(MesPartSap mesPart, String source, String target, String org, String workCenterCode, double qty, Integer moveType) {
        MesMove move = new MesMove();
        if (!Objects.isNull(mesPart)) {
            move.setMeins(mesPart.getUnit());
        }
        move.setOrganizeCode(org);
        move.setFactoryCode(org);
        move.setLgort(source);
        move.setUmlgo(target);
        move.setMenge(qty);
        move.setPostDate(TimeTool.getToday());
        move.setMoveType(moveType);
        move.setPostTime(TimeTool.getTimeShortWithColon());
        move.setWorkCenter(workCenterCode);
        return move;
    }
    /**
     * flag = 1 减少 1天， = 2 加一天
     *
     * @param lotNo
     * @param flag
     * @return
     */
    public  String getDateTime(String lotNo, int flag) {
        SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd");
        Date d = null;
        try {
            d = df.parse(lotNo);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        Calendar cal = Calendar.getInstance();
        cal.setTime(d);
        if (flag == 1) {
            cal.add(Calendar.DATE, -1); //减1天
        } else if (flag == 2) {
            cal.add(Calendar.DATE, 1); //加1天
        }
        return df.format(cal.getTime()).substring(0, 10);
    }
}
