package com.sunda.spmswms.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.sunda.spmscommon.Constans;
import com.sunda.spmscommon.ResponseResult;
import com.sunda.spmscommon.feign.IFeignServiceCommon;
import com.sunda.spmscommon.service.ISysLogMqService;
import com.sunda.spmswms.entity.*;
import com.sunda.spmswms.mapper.SapPackageListDtlMapper;
import com.sunda.spmswms.mapper.WhsOutMapper;
import com.sunda.spmswms.service.*;
import feign.FeignException;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.time.DateFormatUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

import static com.sunda.spmscommon.Constans.*;

/**
 * <p>
 * 出库任务表 服务实现类
 * </p>
 *
 * @author Wayne
 * @since 2021-04-23
 */
@Service
@Slf4j
public class WhsOutServiceImpl extends ServiceImpl<WhsOutMapper, WhsOut> implements IWhsOutService {

    @Autowired
    IShelfOnTaskService iShelfOnTaskService;
    
    @Autowired
    IShelfOnTaskDtlService iShelfOnTaskDtlService;

    @Autowired
    ISapDeliveryNoteLogService iSapDeliveryNoteLogService;

    @Autowired
    IWhsStorageInventoryService iWhsStorageInventoryService;

    @Autowired
    IWhsInventoryInfoService iWhsInventoryInfoService;

    @Autowired
    IWhsOperateLogService iWhsOperateLogService;

    @Autowired
    ISapPackageBoxService iSapPackageBoxService;

    @Autowired
    ISapPackageListDtlService iSapPackageListDtlService;
    
    @Autowired
    ISapPackageListService iSapPackageListService;

    @Autowired
    IFeignServiceCommon iFeignServiceCommon;
    
    @Autowired
    IWhsStorageService iWhsStorageService;
    
    @Autowired
    SapPackageListDtlMapper sapPackageListDtlMapper;
    
    @Autowired
    ISysLogMqService iSysLogMqService;
    
    @Autowired
    ISapBoxNoteDtlService iSapBoxNoteDtlService;
    
    @Value("${zxd.sap.client}")
    private String zxdSapClient;


    @Override
    public WhsOut getWhsOutByUuid(String uuid) {
        return this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", uuid));
    }

    @Override
    public WhsOut getWhsOut(String werks, String whsLocationCode, String packageList, String goodsType, String dateOfLoading, String cabinetSerialNumber) {
        QueryWrapper<WhsOut> queryWrapper = new QueryWrapper<WhsOut>()
                .eq("WERKS", werks)
                .eq("WHS_LOCATION_CODE", whsLocationCode)
                .eq("PACKAGE_LIST", packageList)
                .eq("GOODS_TYPE", goodsType)
                .eq("DATE_OF_LOADING", dateOfLoading)
                .eq("CABINET_SERIAL_NUMBER", cabinetSerialNumber);
        return this.baseMapper.selectOne(queryWrapper);
    }

    @Override
    public int insertWhsOut(WhsOut whsOut) {
        return this.baseMapper.insert(whsOut);
    }

    /**
     * {
     * "werks": ["CN01"],
     * "whsLocationCode": ["1011", "1061"],
     * "whsOutNo": "",
     * "packageList": "",
     * "goodsType": "",
     * "dateOfLoading": "",
     * "cabinetSerialNumber": "",
     * "taskStatus": "0,1",
     * "pageNo": 1,
     * "pageSize": 20
     * }
     *
     * @param doc
     * @return
     */
    @Override
    public IPage<WhsOut> getWhsOutList(JSONObject doc) {
        if (!doc.containsKey("werks") || doc.getJSONArray("werks").size() == 0 || !doc.containsKey("whsLocationCode") || doc.getJSONArray("whsLocationCode").size() == 0) {
            return null;
        }
        if (doc.getJSONArray("whsLocationCode").size() == 1 && doc.getJSONArray("whsLocationCode").get(0) == null) {
            return null;
        }
        int pageNo = StringUtils.isEmpty(doc.getString("pageNo")) ? 1 : (doc.getInteger("pageNo") < 1 ? 1 : doc.getInteger("pageNo"));
        int pageSize = StringUtils.isEmpty(doc.getString("pageSize")) ? 20 : doc.getInteger("pageSize");
        Page<WhsOut> page = new Page<>(pageNo, pageSize);
        List<String> werks = doc.getJSONArray("werks").toJavaList(String.class);
        List<String> whsLocationCode = doc.getJSONArray("whsLocationCode").toJavaList(String.class);
        QueryWrapper<WhsOut> queryWrapper = new QueryWrapper<WhsOut>();
        if (werks.size() > 0) {
            queryWrapper.in("WERKS", werks);
        }
        if (whsLocationCode.size() > 0) {
            queryWrapper.in("WHS_LOCATION_CODE", whsLocationCode);
        }
        if (doc.containsKey("whsOutNo") && StringUtils.isNotEmpty(doc.getString("whsOutNo"))) {
            queryWrapper.eq("WHS_OUT_NO", doc.getString("whsOutNo"));
        }
        if (doc.containsKey("packageList") && StringUtils.isNotEmpty(doc.getString("packageList"))) {
            queryWrapper.eq("PACKAGE_LIST", doc.getString("packageList"));
        }
        if (doc.containsKey("goodsType") && StringUtils.isNotEmpty(doc.getString("goodsType"))) {
            queryWrapper.eq("GOODS_TYPE", doc.getString("goodsType"));
        }
        if (doc.containsKey("dateOfLoading") && StringUtils.isNotEmpty(doc.getString("dateOfLoading"))) {
            queryWrapper.eq("DATE_OF_LOADING", doc.getString("dateOfLoading").replaceAll("-", ""));
        }
        if (doc.containsKey("cabinetSerialNumber") && StringUtils.isNotEmpty(doc.getString("cabinetSerialNumber"))) {
            queryWrapper.eq("CABINET_SERIAL_NUMBER", doc.getString("cabinetSerialNumber"));
        }
        if (doc.containsKey("taskStatus") && StringUtils.isNotEmpty(doc.getString("taskStatus"))) {
            List<String> taskStatus = Arrays.asList(doc.getString("taskStatus").split(","));
            queryWrapper.in("TASK_STATUS", taskStatus);
            //queryWrapper.eq("TASK_STATUS", doc.getString("taskStatus"));
        }
        return this.baseMapper.selectPage(page, queryWrapper);
    }

    /**
     * {
     * "werks": ["CN01"],
     * "whsLocationCode": ["1011", "1061"],
     * "whsOutNo": "",
     * "packageList": "",
     * "goodsType": "",
     * "dateOfLoading": "",
     * "cabinetSerialNumber": "",
     * "taskStatus": "0,1",
     * "pageNo": 1,
     * "pageSize": 20
     * }
     *
     * @param doc
     * @return
     */
    @Override
    public Page<Map<String, Object>> getWhsOutListPage(JSONObject doc) {
        if (!doc.containsKey("werks") || doc.getJSONArray("werks").size() == 0 || !doc.containsKey("whsLocationCode") || doc.getJSONArray("whsLocationCode").size() == 0) {
            return null;
        }
        if (doc.getJSONArray("whsLocationCode").size() == 1 && doc.getJSONArray("whsLocationCode").get(0) == null) {
            return null;
        }
        int pageNo = StringUtils.isEmpty(doc.getString("pageNo")) ? 1 : (doc.getInteger("pageNo") < 1 ? 1 : doc.getInteger("pageNo"));
        int pageSize = StringUtils.isEmpty(doc.getString("pageSize")) ? 20 : doc.getInteger("pageSize");
        List<String> werks = doc.getJSONArray("werks").toJavaList(String.class);
        List<String> whsLocationCode = doc.getJSONArray("whsLocationCode").toJavaList(String.class);
        String whsOutNo = StringUtils.isEmpty(doc.getString("whsOutNo")) ? null : doc.getString("whsOutNo");
        String packageList = StringUtils.isEmpty(doc.getString("packageList")) ? null : doc.getString("packageList");
        String shipper = StringUtils.isEmpty(doc.getString("shipper")) ? null : doc.getString("shipper");
        String goodsType = StringUtils.isEmpty(doc.getString("goodsType")) ? null : doc.getString("goodsType");
        String dateOfLoading = StringUtils.isEmpty(doc.getString("dateOfLoading")) ? null : doc.getString("dateOfLoading");
        String cabinetSerialNumber = StringUtils.isEmpty(doc.getString("cabinetSerialNumber")) ? null : doc.getString("cabinetSerialNumber");
        String cabinetNumber = StringUtils.isEmpty(doc.getString("cabinetNumber")) ? null : doc.getString("cabinetNumber");
        List<String> taskStatus = StringUtils.isEmpty(doc.getString("taskStatus")) ? null : Arrays.asList(doc.getString("taskStatus").split(","));
        Page<Map<String, Object>> pages = new Page<>(pageNo, pageSize);
        List<Map<String, Object>> pageMaData = this.baseMapper.getWhsOutListPage(pages, werks, whsLocationCode, whsOutNo, packageList, goodsType, dateOfLoading, cabinetSerialNumber, taskStatus, cabinetNumber, shipper);
        pages.setRecords(pageMaData);
        return pages;
    }


    @Override
    public List<Map<String, Object>> getWhsOutDtl(String goodsType, String uuid) {
        /** 获取 备件货 出库任务明细行项目 */
        if ("X".equals(goodsType)) {
            /** 有额外增加出库箱码的情况，额外出库箱码没有行号，存入出库任务UUID。此处查询要考虑这种情况。 */
            return this.baseMapper.getPackageListDtlX(uuid);
        }
        /** 获取 贸易货 出库任务明细行项目 */
        if ("M".equals(goodsType)) {
            return this.baseMapper.getPackageListDtlM(uuid);
        }
        return null;
    }

    /**
     * 根据出库任务表头获取出库任务明细,下架任务明细信息
     */
    @Override
    public ResponseResult getWhsOutDtlX(String uuid) {
        // 查询出库任务头信息
    	WhsOut whsOut = this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", uuid));
    	/** 有额外增加出库箱码的情况，额外出库箱码没有行号，存入出库任务UUID。此处查询要考虑这种情况。 */
    	ResponseResult shelfOnDtls = iShelfOnTaskService.getShelfOffWithDtlsByRelatedUUID(uuid);
    	// 手动创建的出库任务刚开始是没有出库任务明细的，所以不用查询上下架数据
    	if (shelfOnDtls.getCode() != 200 && !"2".equals(whsOut.getDataSource())) {
    		return shelfOnDtls;
    	} else if (shelfOnDtls.getCode() != 200 && "2".equals(whsOut.getDataSource())) {
    		return ResponseResult.success().add("shelfOffTask", new ShelfOnTask()).add("shelfOffTaskDtls", new ArrayList<ShelfOnTaskDtl>(1)).add("whsOutDtl", this.baseMapper.getPackageListDtlX(uuid)).add("whsOut", whsOut);
    	}
    	return shelfOnDtls.add("whsOutDtl", this.baseMapper.getPackageListDtlX(uuid)).add("whsOut", whsOut);
    }

    @Override
    public List<Map<String, Object>> getBoxNoteDtl(String boxNote) {
        if (StringUtils.isEmpty(boxNote)) {
            return null;
        }
        return this.baseMapper.getBoxNoteDtl(boxNote);
    }

    @Override
    public int initShelfOnTask(String userId, String createDate, String uuid) {
        try{
            WhsOut whsOut = this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", uuid));
            if (whsOut == null || !"0".equals(whsOut.getTaskStatus())) {
                /** 出库任务不存在或该出库任务已下发下架任务 */
                return -1;
            }
            int resp = iShelfOnTaskService.genShelfOnTask(userId, createDate, whsOut);
            /** 下架任务初始化完成，将出库任务状态修改为 执行中1 */
            if (resp > 0) {
                whsOut.setTaskStatus("1");
                int result = this.baseMapper.update(whsOut, new QueryWrapper<WhsOut>().eq("UUID", uuid));
                iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(uuid, userId, WHS_OUT_TASK_UPDATE);
                return result;
            }
            return -1;
        }catch (Exception e){
            e.printStackTrace();
            return -1;
        }

    }

    /**
     * {
     * "pdaSn": "1234567890",
     * "shelfOnTask": {
     * "uuid": "3e74fd02509f4e0b900c6c5433bc19b7",
     * "currentStatus": "7",
     * "targetStatus": "3",
     * "relateDocNumber": "23444500c18c4f898950ac5de825bb01"
     * },
     * "shelfOnTaskDtls": [{
     * "uuid": "3e74fd02509f4e0b900c6c5433bc19b7",
     * "taskRowId": 10,
     * "boxNote": "TEST-1009-0101",
     * "actualOperateQty": "1",
     * "targetStorage": "b95946ac1dab42b79ade3438acb0b513",
     * "unplanned": ""
     * }, {
     * "uuid": "3e74fd02509f4e0b900c6c5433bc19b7",
     * "taskRowId": 20,
     * "boxNote": "TEST-1009-0102",
     * "actualOperateQty": "1",
     * "targetStorage": "b95946ac1dab42b79ade3438acb0b513",
     * "unplanned": ""
     * }, {
     * "uuid": "3e74fd02509f4e0b900c6c5433bc19b7",
     * "taskRowId": 30,
     * "boxNote": "TEST-1003-0101",
     * "actualOperateQty": "1",
     * "targetStorage": "b95946ac1dab42b79ade3438acb0b512",
     * "unplanned": ""
     * }],
     * "whsOut": {
     * "uuid": "23444500c18c4f898950ac5de825bb01",
     * "cabinetNumber": "JZX20217788",
     * "currentStatus": 1,
     * "targetStatus": 2,
     * "werks": "CN01",
     * "whsLocationCode": "1011",
     * "submitDate": "2021-05-12",
     * "actualLoadingDate": "2021-05-12",
     * "monitorLoading": "zhang san",
     * "loadingType": "A",
     * "remarks": "remarks",
     * "cabinetCheckResult": "ok",
     * "leadSeal":"123456789"
     * },
     * "whsOutDtl": [{
     * "packageList": "PL1001",
     * "boxNote": "TEST-1009-0101",
     * "actualOperateQty": 1,
     * "unplanned": ""
     * }, {
     * "packageList": "PL1001",
     * "boxNote": "TEST-1009-0102",
     * "actualOperateQty": "",
     * "unplanned": ""
     * }],
     * "remainingBoxNote": [{
     * "boxNote": "TEST-1003-0101",
     * "quantity": 1
     * }]
     * }
     *
     * @param doc
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized ResponseResult updateWhsOut(JSONObject doc, String userId) throws Exception{

        JSONObject whsOutObj = doc.getJSONObject("whsOut");
        if(whsOutObj.get("targetStatus")!=null && whsOutObj.getString("targetStatus").length()>0){
            //PC端正常传目标状态
        }else{
            //PDA端没传目标状态
            whsOutObj.put("targetStatus", doc.getString("targetStatus"));
        }
        ResponseResult responseResult = checkDataValidate(doc);
        if (responseResult.getCode() == 100) {
            return responseResult;
        }
        /** i.更新出库任务明细，添加出库任务更新日志；
         * ii.应出未出的箱，写回实出 空 或 0 即可；
         * iii.出了计划之外的箱，新增数据到装箱单箱码关系表中；
         * iv.更新出库任务表头状态为已完成，更新出库任务行项目状态为已完成，记录出库任务更新和完成日志。
         * */
        int uWhsOutTask = updateWhsOutTask(doc, userId);
        if (uWhsOutTask < 0) {
            throw new Exception("出库任务更新过程执行异常 updateWhsOutTask ");
        }
        /** 更新仓库库存表，扣减出库明细中的箱码仓库库存，记录仓库库存台账 */
        int uWhsInventoryInfo = updateWhsInventoryInfo(doc, userId);
        if (uWhsInventoryInfo < 0) {
            throw new Exception("仓库库存更新过程执行异常 updateWhsInventoryInfo ");
        }
        /** 先删除下架任务明细，新建下架任务详情，增加下架任务更新日志，更新下架任务表头为已完成，记录下架任务完成日志 */
        List<ShelfOnTaskDtl> insertSheifList = new ArrayList<>();
        int uShelfOnTaskDtl = updateShelfOnTaskDtl(doc, userId, insertSheifList);
        if (uShelfOnTaskDtl < 0) {
            throw new Exception("下架任务更新过程执行异常 updateShelfOnTaskDtl ");
        }
        /** 更新储位库存表，扣减下架明细中的箱码库存，记录储位库存台账 */
        int uStorageInventory = updateStorageInventory(doc, userId, insertSheifList);
        if (uStorageInventory < 0) {
            throw new Exception("储位库存更新过程执行异常 updateStorageInventory ");
        }
        /** 如果下架明细和出库明细存在差异项则转化成上架任务，新建上架任务，新建上架任务明细，记录上架任务新建日志 */
        int aShelfOnTask = addShelfOnTask(doc, userId);
        if (aShelfOnTask < 0) {
            throw new Exception("下架任务生成过程异常 addShelfOnTask ");
        }

        return ResponseResult.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized ResponseResult withDrawWhsOutX(String whsOutUuid, String currentStatus, String userId)throws Exception {
//        try {
            WhsOut whsOut = this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
            if (whsOut == null) {
                return ResponseResult.error("出库任务 " + whsOutUuid + " 不存在");
            }
            if (!currentStatus.equals(whsOut.getTaskStatus())) {
                return ResponseResult.error("出库任务 " + whsOut.getWhsOutNo() + "    " + whsOutUuid + " 状态已更新请刷新后重试");
            }

            String werks = whsOut.getWerks();
            String whsLocationCode = whsOut.getWhsLocationCode();

            // 获取当前出库单所在的工厂仓库的储位，并获取第一个做为撤回后的储位
            List<String> sWerks = new ArrayList<String>();
            sWerks.add(werks);
            List<String> sWhsLocationCode = new ArrayList<String>();
            sWhsLocationCode.add(whsLocationCode);
            List<WhsStorage> whsStorages = iWhsStorageService.getAllWhsStorageList(sWhsLocationCode, sWerks);
            if (null == whsStorages || whsStorages.size() < 0) {
                throw new Exception("不能获取到仓库的储位信息 werks" + werks + " whsLocationCode:" + whsLocationCode);
            }
            WhsStorage targetStorage = whsStorages.get(0);

            //获取出任务行明细
            QueryWrapper<SapPackageListDtl> eq = new QueryWrapper<SapPackageListDtl>().eq("WHS_OUT_UUID", whsOutUuid);
            List<SapPackageListDtl> list = iSapPackageListDtlService.list(eq);
//            List<Map<String, Object>> whsOutDtl = getWhsOutDtl("X", whsOutUuid);
            if (null == list || list.size() <= 0) {
                return ResponseResult.error("无法获取出库任务明细");
            }

            // 判断是否有未完成的下架任务，发果有未完成的下架任务，将下架任务标为完成
            List<ShelfOnTask> shelfOffTasks = iShelfOnTaskService.getUnClosedTasks(whsOutUuid);
            if (null != shelfOffTasks && shelfOffTasks.size() > 0) {
            	QueryWrapper<ShelfOnTask> shelEq = new QueryWrapper<ShelfOnTask>().eq("RELATE_DOC_NUMBER",whsOutUuid).ne("TASK_STATUS",'3');
            	ShelfOnTask shelOn = new ShelfOnTask();
            	shelOn.setTaskStatus("3");
            	iShelfOnTaskService.update(shelOn, shelEq);
            }
            // 根据出库任务ID查询上下架行项目
            Map<String, String> boxnoteStorageMaps = null;
            QueryWrapper<ShelfOnTask> shelEq = new QueryWrapper<ShelfOnTask>().eq("RELATE_DOC_NUMBER",whsOutUuid).orderByDesc("CREATE_TIME");
            List<ShelfOnTask> ShelfOnTaskList = iShelfOnTaskService.list(shelEq);
            if (!CollectionUtils.isEmpty(ShelfOnTaskList)) {
            	QueryWrapper<ShelfOnTaskDtl> shelDtlEq = new QueryWrapper<ShelfOnTaskDtl>().eq("UUID", ShelfOnTaskList.get(0).getUuid());
            	List<ShelfOnTaskDtl> shelDtlList = iShelfOnTaskDtlService.list(shelDtlEq);
            	if (!CollectionUtils.isEmpty(shelDtlList)) {
            		boxnoteStorageMaps = shelDtlList.stream().collect(Collectors.toMap(ShelfOnTaskDtl::getBoxNote, ShelfOnTaskDtl::getTargetStorage));
            	}
            }
            // 如果已经提交到了SAP，需要首先回撤SAP
            if (currentStatus.equals(WHS_OUT_Status_Completed) || currentStatus.equals(WHS_OUT_Status_SuccessedSAP)) {
            	// 撤销前调用接口查询SAP装箱单状态
            	JSONObject toSelectSapData = this.setSelectPackListStatusData(whsOut.getPackageList());
            	JSONObject message1 = JSONObject.parseObject(iFeignServiceCommon.sendRestToSapCommon(JSONObject.parseObject(toSelectSapData.toString())));
            	// 保存接口日志
                iSysLogMqService.addSysLogMq(SEND_MSG, "ZZXD_IF_SPMS_11", "查询装箱单在SAP状态接口", "ZZXD_IF_SPMS_11", toSelectSapData.toJSONString(), message1.toString(), null);
                JSONObject RETURN_DATA1 = message1.getJSONObject("RESPONSE").getJSONObject("RETURN_DATA");
                if ("N".equals(RETURN_DATA1.getString("STATUS"))) {
                	throw new Exception("装箱单在SAP的状态不允许进行撤销："+message1.toJSONString());
                } else if ("E".equals(RETURN_DATA1.getString("STATUS"))) {
                	throw new Exception("装箱单在SAP未找到数据："+message1.toJSONString());
                }
            	// 调用ZZXD_IF_SPMS_08接口进行撤销
            	// 设置推送SAP数据
                JSONObject toSapData = this.setDataToSap(whsOutUuid, whsOut, "X");
                JSONObject dataToSap = JSONObject.parseObject(toSapData.toString());
                JSONObject message = JSONObject.parseObject(iFeignServiceCommon.sendRestToSapCommon((dataToSap)));
                // 保存接口日志
                iSysLogMqService.addSysLogMq(SEND_MSG, "ZZXD_IF_SPMS_08", "出库任务撤销传输至SAP接口", "ZZXD_IF_SPMS_08", dataToSap.toJSONString(), message.toString(), null);
                JSONObject RETURN_DATA = message.getJSONObject("RESPONSE").getJSONObject("RETURN_DATA");
                JSONArray ET_RET = RETURN_DATA.getJSONArray("ET_RET");
                if (ET_RET.size() > 0 && "S".equals(ET_RET.getJSONObject(0).getString("TYPE"))) {
                	
                } else {
                	throw new Exception("撤销同步SAP失败："+message.toJSONString());
                }
            }
            // 进行增加库
            if (currentStatus.equals(WHS_OUT_Status_PendingSyncSAP) || currentStatus.equals(WHS_OUT_Status_Completed) ||
            		currentStatus.equals(WHS_OUT_Status_FailedSAP) || currentStatus.equals(WHS_OUT_Status_SuccessedSAP)) {
                // 原储位加回库存
            	Map<String, List<SapPackageListDtl>> boxNoteMaps = list.stream().collect(Collectors.groupingBy(SapPackageListDtl::getPackageCode));
            	for (String boxNote : boxNoteMaps.keySet()) {
            		SapPackageListDtl dtl = boxNoteMaps.get(boxNote).get(0);
            		double actualOperateQty = null != dtl.getActualOperateQty() ? dtl.getActualOperateQty() : 0;
                    String item = null != dtl.getItem() ? dtl.getItem() : "";
                    if (actualOperateQty > 0) {
                        // 验证箱码在储位库存中不存在，如果存在，则抛出异常
                        WhsStorageInventory isExistStorage = iWhsStorageInventoryService.getBoxNoteStorage(boxNote);
                        if (null != isExistStorage) {
                            throw new Exception("箱码: " + boxNote + " 已经存在于储位: " + isExistStorage.getUuid());
                        }
                        // 加箱码储位库存
                        // 如果上下架行项目中没有储位ID，那么直接使用默认储位ID进行库存增加
                        String storageUuid = boxnoteStorageMaps != null ? boxnoteStorageMaps.get(boxNote) : targetStorage.getUuid();
                        // 查询储位编号
                        WhsStorage whsStorage = iWhsStorageService.getWhsStorageByUuid(storageUuid);
                        ResponseResult insertRes = iWhsStorageInventoryService.insertStorageInventoryX(storageUuid, boxNote, "", userId);
                        if (insertRes.getCode() != 200) {
                            throw new Exception("增加储位库存失败 boxNote: " + boxNote);
                        }
                        // 加日志
                        iWhsOperateLogService.insertWhsOperateLog(
                                Constans.WhsOperateLog_DocumentType_NanShaStorageLog,
                                storageUuid,
                                "",
                                boxNote,
                                1.0,
                                Constans.WhsOperateLog_OperationType_ShelfOn,
                                whsOutUuid,
                                Constans.WHS_OUT_WITHDRAW,
                                userId,
                                Constans.WHS_OUT_WITHDRAW + " WhsOUTNO:" + whsOut.getWhsOutNo() + "储位:" + whsStorage.getStorageNo(),
                                werks,
                                whsLocationCode, item);
                        /** 更新仓库库存表，增加出库明细中的箱码仓库库存，记录仓库库存台账 */
                        if (iWhsInventoryInfoService.addWhsInventory(werks, whsLocationCode, boxNote, 1.0, userId) < 0) {
                            throw new Exception("增加仓库库存失败 boxNote:" + boxNote);
                        }
                        iWhsOperateLogService.insertWhsOperateLog(
                                Constans.WhsOperateLog_DocumentType_NanShaWHSLog,
                                "",
                                "",
                                boxNote,
                                1.0,
                                Constans.WhsOperateLog_OperationType_InHouse,
                                whsOutUuid,
                                Constans.WHS_OUT_WITHDRAW,
                                userId,
                                Constans.WHS_OUT_WITHDRAW + " WhsOUTNO:" + whsOut.getWhsOutNo() + "储位:" + whsStorage.getStorageNo(),
                                werks,
                                whsLocationCode, item);
                    }
            	}
            }
            // 更改原单据状态，增加单据操作日志
            whsOut.setTaskStatus(WHS_OUT_Status_Pending);
            this.baseMapper.update(whsOut, new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
            // 更新装箱单状态
            this.updateSapPackageList(userId, whsOut.getPackageList(), "1");
            // 更新装箱单行项目状态为待完成
            iSapPackageListDtlService.uSapPackageListDtl("1", whsOutUuid);
            iSapDeliveryNoteLogService.insertSapDeliveryNoteLogN(whsOutUuid, userId, Constans.WHS_OUT_WITHDRAW, "");
            return ResponseResult.success().add("whsOut", whsOut);
    }

    /**
     * 初步检查数据的合法性，检查出库任务状态是否能被更新，检查下架任务状态是否能被更新，检查下架任务中储位库存是否够，检查出库任务中仓库库存是否够
     */
    public ResponseResult checkDataValidate(JSONObject doc) {
        try {
            if (!doc.containsKey("whsOut") || !doc.containsKey("whsOutDtl") ||
                    doc.getJSONArray("whsOutDtl").size() == 0 || !doc.containsKey("shelfOnTask") ||
                    !doc.containsKey("shelfOnTaskDtls")) {
                return ResponseResult.error("请求参数错误，参数需包含：whsOut、whsOutDtl、shelfOnTask、shelfOnTaskDtls，请重试");
            }
            String werks = doc.getJSONObject("whsOut").getString("werks");
            String whsLocationCode = doc.getJSONObject("whsOut").getString("whsLocationCode");

            JSONObject whsOutObj = doc.getJSONObject("whsOut");
            String whsOutUuid = whsOutObj.getString("uuid");
            String currentStatus = whsOutObj.getString("currentStatus");
            WhsOut whsOut = this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
            if (whsOut == null) {
                return ResponseResult.error("出库任务 " + whsOutUuid + " 不存在");
            }
            if (!currentStatus.equals(whsOut.getTaskStatus())) {
                return ResponseResult.error("出库任务 " + whsOut.getWhsOutNo() + "    " + whsOutUuid + " 状态已更新请刷新后重试");
            }
            // 手动添加的出库任务不校验上下架
            if (!"2".equals(whsOut.getDataSource())) {
            	JSONObject shelfOnTaskObj = doc.getJSONObject("shelfOnTask");
            	String shelfOnTaskUuid = shelfOnTaskObj.getString("uuid");
            	String shelfOnTaskCurrentStatus = shelfOnTaskObj.getString("currentStatus");
            	ShelfOnTask shelfOnTask = iShelfOnTaskService.getShelfOnTaskByTaskUuid(shelfOnTaskUuid);
            	if (shelfOnTask == null) {
            		return ResponseResult.error("下架任务 " + shelfOnTaskUuid + " 不存在");
            	}
            	if (!shelfOnTaskCurrentStatus.equals(shelfOnTask.getTaskStatus())) {
            		return ResponseResult.error("下架任务 " + shelfOnTask.getTaskId() + "    " + whsOutUuid + " 状态已更新请刷新后重试");
            	}
            	
            	JSONArray shelfOnTaskDtls = doc.getJSONArray("shelfOnTaskDtls");
            	for (int i = 0; i < shelfOnTaskDtls.size(); i++) {
            		JSONObject dtl = shelfOnTaskDtls.getJSONObject(i);
            		String boxNote = dtl.getString("boxNote");
            		String targetStorage = dtl.getString("targetStorage");
            		double actualOperateQty = StringUtils.isEmpty(dtl.getString("actualOperateQty")) ? 0 : dtl.getDouble("actualOperateQty");
            		/** 实际操作数量 > 0 才需要校验对应储位库存是否足够 */
            		if (actualOperateQty > 0) {
            			if (targetStorage == null) {
            				return ResponseResult.error("请求参数错误");
            			}
            			String availableQty = iWhsStorageInventoryService.getAvailableQtyByUuidBoxNote(targetStorage, boxNote);
            			if (availableQty == null || Double.parseDouble(availableQty) < actualOperateQty) {
            				return ResponseResult.error("箱码：" + boxNote + "  在储位：" + targetStorage + " 可用库存不足");
            			}
            		}
            	}
            }

            JSONArray whsOutDtl = doc.getJSONArray("whsOutDtl");
            for (int i = 0; i < whsOutDtl.size(); i++) {
                JSONObject dtl = whsOutDtl.getJSONObject(i);
                String boxNote = dtl.getString("boxNote");
                String targetStorage = dtl.getString("targetStorage");
                double actualOperateQty = StringUtils.isEmpty(dtl.getString("actualOperateQty")) ? 0 : dtl.getDouble("actualOperateQty");
                if (actualOperateQty > 0) {
                    String quantity = iWhsInventoryInfoService.getQuantityByBoxNote(werks, whsLocationCode, boxNote);
                    if (quantity == null || Double.parseDouble(quantity) < actualOperateQty) {
                        return ResponseResult.error("箱码：" + boxNote + "  在仓库：" + werks + whsLocationCode + " 可用库存不足");
                    }
                    // 手动添加的出库任务增加储位校验
                    if ("2".equals(whsOut.getDataSource())) {
                    	if (StringUtils.isEmpty(targetStorage)) {
                    		return ResponseResult.error("箱码：" + boxNote + " 不存在储位");
                    	}
                    	String availableQty = iWhsStorageInventoryService.getAvailableQtyByUuidBoxNote(targetStorage, boxNote);
            			if (availableQty == null || Double.parseDouble(availableQty) < actualOperateQty) {
            				return ResponseResult.error("箱码：" + boxNote + "  在储位：" + targetStorage + " 可用库存不足");
            			}
                    }
                }
            }
            return ResponseResult.success();
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseResult.error(e.toString());
        }

    }

    /**
     * {"shelfOnTask": {
     * "uuid": "3e74fd02509f4e0b900c6c5433bc19b7",
     * "currentStatus": "7",
     * "targetStatus": "3",
     * "relateDocNumber": "23444500c18c4f898950ac5de825bb01"
     * },
     * "shelfOnTaskDtls": [{
     * "uuid": "3e74fd02509f4e0b900c6c5433bc19b7",
     * "taskRowId": 10,
     * "boxNote": "TEST-1009-0101",
     * "actualOperateQty": "1",
     * "targetStorage": "b95946ac1dab42b79ade3438acb0b513"
     * }, {
     * "uuid": "3e74fd02509f4e0b900c6c5433bc19b7",
     * "taskRowId": 20,
     * "boxNote": "TEST-1009-0102",
     * "actualOperateQty": "1",
     * "targetStorage": "b95946ac1dab42b79ade3438acb0b513"
     * }, {
     * "uuid": "3e74fd02509f4e0b900c6c5433bc19b7",
     * "taskRowId": 30,
     * "boxNote": "TEST-1003-0101",
     * "actualOperateQty": "1",
     * "targetStorage": "b95946ac1dab42b79ade3438acb0b512"
     * }]}
     * 先删除下架任务明细，新建下架任务详情，增加下架任务更新日志，更新下架任务表头为已完成，记录下架任务完成日志
     * @param insertSheifList 
     */
    int updateShelfOnTaskDtl(JSONObject doc, String userId, List<ShelfOnTaskDtl> insertSheifList) {
        try {
            JSONObject shelfOnTaskHead = doc.getJSONObject("shelfOnTask");
            JSONArray shelfOnTaskDtls = doc.getJSONArray("shelfOnTaskDtls");
            String shelfOnTaskUuid = shelfOnTaskHead.getString("uuid");
            String targetStatus = shelfOnTaskHead.getString("targetStatus");
            JSONObject whsOutObject = doc.getJSONObject("whsOut");
            String whsOutUuid = whsOutObject.getString("uuid");
            WhsOut whsOut = this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
        	if (StringUtils.isEmpty(shelfOnTaskUuid) && "2".equals(whsOut.getDataSource())) {
        		// 如果上下架数据为空，并且出库任务是手动创建的，需要初始化上下架任务
        		shelfOnTaskUuid = UUID.randomUUID().toString().replaceAll("-", "").toLowerCase();
                ShelfOnTask shelfOnTask = new ShelfOnTask();
                shelfOnTask.setUuid(shelfOnTaskUuid);
                shelfOnTask.setWerks(whsOut.getWerks());
                shelfOnTask.setCreateUserId(userId);
                shelfOnTask.setWhsLocationCode(whsOut.getWhsLocationCode());
                shelfOnTask.setCreateDate(DateFormatUtils.format(new Date(), "yyyyMMdd"));
                /** 出库待下架状态 3-已完成 */
                shelfOnTask.setTaskStatus("3");
                /** 1上架任务；2下架任务； */
                shelfOnTask.setTaskType("2");
                shelfOnTask.setRelateDocNumber(whsOutUuid);
                iShelfOnTaskService.save(shelfOnTask);
                // 查询出库任务行项目
                QueryWrapper<SapPackageListDtl> qw = new QueryWrapper<SapPackageListDtl>().eq("PACKAGE_LIST", whsOut.getPackageList()).eq("WHS_OUT_UUID", whsOut.getUuid());
                List<SapPackageListDtl> packList = iSapPackageListDtlService.list(qw);
                if (!CollectionUtils.isEmpty(packList)) {
                	// 根据箱码分组
                	Map<String, List<SapPackageListDtl>> packCodeMaps = packList.stream().collect(Collectors.groupingBy(SapPackageListDtl::getPackageCode));
                	int sort = 1;
                	for (String packCode : packCodeMaps.keySet()) {
                		List<SapPackageListDtl> list = packCodeMaps.get(packCode);
                		// 根据箱码查询储位编码
                		Map<String, String> storageNoMap = this.baseMapper.selectStorageNo(packCode);
                		if (list.get(0).getActualOperateQty() != null && list.get(0).getActualOperateQty() > 0) {
                			ShelfOnTaskDtl shelfOnTaskDtl = new ShelfOnTaskDtl();
                            shelfOnTaskDtl.setUuid(shelfOnTaskUuid);
                            shelfOnTaskDtl.setTaskRowId(String.valueOf(sort * 10));
                            shelfOnTaskDtl.setBoxNote(packCode);
                            shelfOnTaskDtl.setQuantity(1.00);
                            shelfOnTaskDtl.setActualOperateQty(list.get(0).getActualOperateQty());
                			shelfOnTaskDtl.setTargetStorage(storageNoMap.get("uuid").toString());
                			shelfOnTaskDtl.setUnplanned(list.get(0).getUnplanned());
                			iShelfOnTaskService.insertShelfOnTaskDtl(shelfOnTaskDtl);
                			insertSheifList.add(shelfOnTaskDtl);
                            ++sort;
                		}
                	}
                }
        	} else {
        		/** 删除下架任务详情 */
        		if (shelfOnTaskUuid != null) {
        			iShelfOnTaskService.deleteShelfOnTaskDtl(shelfOnTaskUuid);
        		}
        		for (int i = 0; i < shelfOnTaskDtls.size(); i++) {
        			JSONObject shelfOnTaskDtlObj = shelfOnTaskDtls.getJSONObject(i);
        			ShelfOnTaskDtl shelfOnTaskDtl = new ShelfOnTaskDtl();
        			shelfOnTaskDtl.setUuid(shelfOnTaskDtlObj.getString("uuid"));
        			shelfOnTaskDtl.setTaskRowId(shelfOnTaskDtlObj.getString("taskRowId"));
        			shelfOnTaskDtl.setBoxNote(shelfOnTaskDtlObj.getString("boxNote"));
        			shelfOnTaskDtl.setQuantity(1.0);
        			if (StringUtils.isNotEmpty(shelfOnTaskDtlObj.getString("actualOperateQty"))) {
        				shelfOnTaskDtl.setActualOperateQty(shelfOnTaskDtlObj.getDouble("actualOperateQty"));
        			} else {
        				shelfOnTaskDtl.setActualOperateQty(0.0);
        			}
        			shelfOnTaskDtl.setTargetStorage(shelfOnTaskDtlObj.getString("targetStorage"));
        			shelfOnTaskDtl.setUnplanned(shelfOnTaskDtlObj.getString("unplanned"));
        			/** 新建下架任务详情，实际操作数量 > 0 的详情才需要被记录下来 */
        			if (shelfOnTaskDtl.getActualOperateQty() > 0) {
        				iShelfOnTaskService.insertShelfOnTaskDtl(shelfOnTaskDtl);
        			}
        		}
        	}
            iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(shelfOnTaskUuid, userId, UPDATE_DEDUCT_SHELF_TASK);
            /** 更新下架任务表头状态为已完成 */
            iShelfOnTaskService.updateShelfOnTaskStatus(shelfOnTaskUuid, targetStatus);
            iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(shelfOnTaskUuid, userId, CLOSE_DEDUCT_SHELF_TASK);
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    /**
     * {"shelfOnTask": {
     * "uuid": "3e74fd02509f4e0b900c6c5433bc19b7",
     * "currentStatus": "7",
     * "targetStatus": "3",
     * "relateDocNumber": "23444500c18c4f898950ac5de825bb01"
     * },
     * "shelfOnTaskDtls": [{
     * "uuid": "3e74fd02509f4e0b900c6c5433bc19b7",
     * "taskRowId": 10,
     * "boxNote": "TEST-1009-0101",
     * "actualOperateQty": "1",
     * "targetStorage": "b95946ac1dab42b79ade3438acb0b513"
     * }, {
     * "uuid": "3e74fd02509f4e0b900c6c5433bc19b7",
     * "taskRowId": 20,
     * "boxNote": "TEST-1009-0102",
     * "actualOperateQty": "1",
     * "targetStorage": "b95946ac1dab42b79ade3438acb0b513"
     * }, {
     * "uuid": "3e74fd02509f4e0b900c6c5433bc19b7",
     * "taskRowId": 30,
     * "boxNote": "TEST-1003-0101",
     * "actualOperateQty": "1",
     * "targetStorage": "b95946ac1dab42b79ade3438acb0b512"
     * }]}
     * 更新储位库存表，删除扣减下架明细中实际操作数量大于 0 的箱码的库存，记录储位库存台账
     *
     * @param doc
     * @param userId
     * @param insertSheifList 
     * @return
     */
    int updateStorageInventory(JSONObject doc, String userId, List<ShelfOnTaskDtl> insertSheifList) {
        String whsOutUuid = doc.getJSONObject("shelfOnTask").getString("relateDocNumber");
        JSONObject whsOut = doc.getJSONObject("whsOut");
        try {
        	if (StringUtils.isEmpty(whsOutUuid) && !CollectionUtils.isEmpty(insertSheifList)) {
        		// 手动添加的出库任务减储位库存
        		for (ShelfOnTaskDtl shelf : insertSheifList) {
        			String taskRowId = shelf.getTaskRowId();
        			Double actualOperateQty = shelf.getActualOperateQty();
        			if (actualOperateQty != null && actualOperateQty > 0) {
        				String boxNote = shelf.getBoxNote();
                        String targetStorage = shelf.getTargetStorage();
                        /** 删除该箱码的储位库存由于箱码具有唯一性，扣减箱码库存按箱码删除，记录储位操作日志 */
                        iWhsStorageInventoryService.deleteStorageBoxNote(boxNote);
                        iWhsOperateLogService.insertWhsOperateLogBX(targetStorage, boxNote, actualOperateQty, whsOutUuid, SPARE_PARTS_OUT_STORAGE, userId,
                                whsOut.getString("werks"), whsOut.getString("whsLocationCode"), taskRowId);
        			}
        		}
        	} else {
        		JSONArray shelfOnTaskDtls = doc.getJSONArray("shelfOnTaskDtls");
                for (int i = 0; i < shelfOnTaskDtls.size(); i++) {
                    JSONObject shelfOnTaskDtl = shelfOnTaskDtls.getJSONObject(i);
                    String taskRowId = shelfOnTaskDtl.getString("taskRowId");
                    double actualOperateQty = StringUtils.isEmpty(shelfOnTaskDtl.getString("actualOperateQty")) ?
                            0 : shelfOnTaskDtl.getDouble("actualOperateQty");
                    if (actualOperateQty > 0) {
                        String boxNote = shelfOnTaskDtl.getString("boxNote");
                        String targetStorage = shelfOnTaskDtl.getString("targetStorage");
                        /** 删除该箱码的储位库存由于箱码具有唯一性，扣减箱码库存按箱码删除，记录储位操作日志 */
                        iWhsStorageInventoryService.deleteStorageBoxNote(boxNote);
                        iWhsOperateLogService.insertWhsOperateLogBX(targetStorage, boxNote, actualOperateQty, whsOutUuid, SPARE_PARTS_OUT_STORAGE, userId,
                                whsOut.getString("werks"), whsOut.getString("whsLocationCode"), taskRowId);
                    }
                }
        	}
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    /**
     * i.更新出库任务明细，添加出库任务更新日志；
     * ii.应出未出的箱，写回实出 空 或 0 即可；
     * iii.出了计划之外的箱，根据箱码遍历出物料，增加到装箱单明细行里，同时新增数据到装箱单箱码关系表中；
     * iv.更新出库任务表头状态为已完成，更新出库任务行项目状态为已完成，记录出库任务更新和完成日志。
     */
    int updateWhsOutTask(JSONObject doc, String userId) {
    	JSONObject whsOutObj = doc.getJSONObject("whsOut");
        String whsOutUuid = whsOutObj.getString("uuid");
        String packageList = whsOutObj.getString("packageList");
        try {
            JSONArray whsOutDtls = doc.getJSONArray("whsOutDtl");
            iSapPackageListDtlService.deleteUnplannedSapPackageListDtl(whsOutUuid, "1");
            for (int i = 0; i < whsOutDtls.size(); i++) {
                JSONObject whsOutDtl = whsOutDtls.getJSONObject(i);
//                String packageList = whsOutDtl.getString("packageList");
//                String item = whsOutDtl.getString("item");
                String boxNote = whsOutDtl.getString("boxNote");
                String unplanned = whsOutDtl.getString("unplanned");
                String containerBrand = whsOutDtl.getString("containerBrand");
                String colorNumber = whsOutDtl.getString("colorNumber");
                String thickness = whsOutDtl.getString("thickness");
//                String spmsItemNo = whsOutDtl.getString("spmsItemNo");
                String batchNo = whsOutDtl.getString("batchNo");
                String bukrs = whsOutDtl.getString("bukrs");
                String receivingWerks = whsOutDtl.getString("receivingWerks");
                String consignee = whsOutDtl.getString("consignee");
                String cabinetModel = whsOutDtl.getString("cabinetModel");
                String packageQuantityMax = whsOutDtl.getString("packageQuantityMax");
                String packageUnitMax = whsOutDtl.getString("packageUnitMax");
                String containerLoadingPosition = whsOutDtl.getString("containerLoadingPosition");
//                String cabinetNumber = whsOutDtl.getString("cabinetNumber");
//                String cabinetSerialNumber = whsOutDtl.getString("cabinetSerialNumber");
//                String leadSeal = whsOutDtl.getString("leadSeal");
                String cabinetVolume = whsOutDtl.getString("cabinetVolume");
                String cabinetWeight = whsOutDtl.getString("cabinetWeight");
                String packingUnit = whsOutDtl.getString("packingUnit");
                String plannedPackingNumber = whsOutDtl.getString("plannedPackingNumber");
                String plannedPackingQty = whsOutDtl.getString("plannedPackingQty");
                String netWeight = whsOutDtl.getString("netWeight");
                String volumeUnit = whsOutDtl.getString("volumeUnit");
                String cabinetWeightUnit = whsOutDtl.getString("cabinetWeightUnit");
                String actualAvgGrossWeight = whsOutDtl.getString("actualAvgGrossWeight");
                String actualReceivingWerks = whsOutDtl.getString("actualReceivingWerks");
                double actualPackingNumber = StringUtils.isEmpty(whsOutDtl.getString("actualPackingNumber")) ?
                        0 : whsOutDtl.getDouble("actualPackingNumber");
                double customsDeclarationNumber = StringUtils.isEmpty(whsOutDtl.getString("customsDeclarationNumber")) ?
                        0 : whsOutDtl.getDouble("customsDeclarationNumber");
                String packingSpec = whsOutDtl.getString("packingSpec");
                String loadingType = whsOutDtl.getString("loadingType");
//                String loadingWerks = whsOutDtl.getString("loadingWerks");
                String remarks = whsOutDtl.getString("remarks");
                String shipmentCategory = whsOutDtl.getString("shipmentCategory");
                String shipper = whsOutDtl.getString("shipper");
//                String loadingWhsLocationCode = whsOutDtl.getString("loadingWhsLocationCode");
                double actualOperateQty = StringUtils.isEmpty(whsOutDtl.getString("actualOperateQty")) ?
                        0 : whsOutDtl.getDouble("actualOperateQty");
//                String storageUuidList = whsOutDtl.getJSONArray("storageUuidList").toJSONString();

                SapPackageListDtl sapPackageListDtl = new SapPackageListDtl();
                // 行项目只有待完成
                sapPackageListDtl.setSpmsStatus("1");
                sapPackageListDtl.setUnplanned(unplanned);
                sapPackageListDtl.setActualOperateQty(actualOperateQty);
                sapPackageListDtl.setPackageCode(boxNote);
                sapPackageListDtl.setBukrs(bukrs);
                sapPackageListDtl.setReceivingWerks(receivingWerks);
                sapPackageListDtl.setConsignee(consignee);
                sapPackageListDtl.setCabinetModel(StringUtils.isNotEmpty(cabinetModel) ? cabinetModel : whsOutObj.getString("cabinetModel"));
                sapPackageListDtl.setActualReceivingWerks(StringUtils.isNotEmpty(actualReceivingWerks) ? actualReceivingWerks : receivingWerks);
                sapPackageListDtl.setCabinetSerialNumber(whsOutObj.getString("cabinetSerialNumber"));
                sapPackageListDtl.setCabinetWeight(StringUtils.isNotEmpty(cabinetWeight) ? Double.valueOf(cabinetWeight) : 0);
                sapPackageListDtl.setCabinetVolume(StringUtils.isNotEmpty(cabinetVolume) ? Double.valueOf(cabinetVolume) : 0);
//                sapPackageListDtl.setTempStorage(storageUuidList);
                sapPackageListDtl.setWeightUnit(cabinetWeightUnit);
                sapPackageListDtl.setPackingUnit(packingUnit);
                sapPackageListDtl.setVolumeUnit(volumeUnit);
                sapPackageListDtl.setNetWeight(StringUtils.isNotEmpty(netWeight) ? Double.valueOf(netWeight) : 0);
                sapPackageListDtl.setContainerBrand(containerBrand);
                sapPackageListDtl.setColorNumber(colorNumber);
                sapPackageListDtl.setThickness(thickness);
                sapPackageListDtl.setActualAvgGrossWeight(actualAvgGrossWeight);
                sapPackageListDtl.setActualPackingNumber(actualPackingNumber);
                sapPackageListDtl.setCustomsDeclarationNumber(customsDeclarationNumber);
                sapPackageListDtl.setPackingSpec(packingSpec);
                sapPackageListDtl.setLoadingType(loadingType);
                sapPackageListDtl.setLoadingWerks(whsOutObj.getString("werks"));
                sapPackageListDtl.setLoadingWhsLocationCode(whsOutObj.getString("whsLocationCode"));
                sapPackageListDtl.setBatchNo(batchNo);
                sapPackageListDtl.setPackageQuantityMax(packageQuantityMax);
                sapPackageListDtl.setPackageUnitMax(packageUnitMax);
                sapPackageListDtl.setContainerLoadingPosition(containerLoadingPosition);
                // 柜号、铅封号取头的值
                sapPackageListDtl.setCabinetNumber(whsOutObj.getString("cabinetNumber"));
                sapPackageListDtl.setLeadSeal(whsOutObj.getString("leadSeal"));
                sapPackageListDtl.setRemarks(remarks);
                sapPackageListDtl.setShipmentCategory(shipmentCategory);
                sapPackageListDtl.setShipper(shipper);
                sapPackageListDtl.setPackageList(packageList);

                /** 计划外的行需新增数据到装箱单明细中；计划内的贸易货出库，按 装箱单号 + 行号 更新出库明细实际出库数量、出库储位料号数量等信息。已有的行，item不会为空。 */
                if ("1".equals(unplanned)) {
                    sapPackageListDtl.setWhsOutUuid(whsOutUuid);
                    sapPackageListDtl.setMaterialNo(whsOutDtl.getString("materialNo"));
                    sapPackageListDtl.setPlannedPackingNumber(StringUtils.isNotEmpty(plannedPackingNumber) ? Double.valueOf(plannedPackingNumber) : 0);
                    sapPackageListDtl.setPlannedPackingQty(StringUtils.isNotEmpty(plannedPackingQty) ? Double.valueOf(plannedPackingQty) : 0);
                    sapPackageListDtl.setGoodsType("X");
                    int maxNo = iSapPackageListDtlService.selectMaxSpmsItemNo(packageList);
                    if (actualOperateQty > 0) {
                    	QueryWrapper<SapBoxNoteDtl> boxEq = new QueryWrapper<SapBoxNoteDtl>().eq("BOX_NOTE", boxNote);
                    	List<SapBoxNoteDtl> boxList = iSapBoxNoteDtlService.list(boxEq);
                    	for (SapBoxNoteDtl box : boxList) {
                    		++maxNo;
                    		boxEq.eq("ITEM", box.getItem()).eq("PACKAGE_POSNR", box.getPackagePosnr()).eq("MATERIAL_NO", box.getMaterialNo());
                    		box.setSpmsItemNo(String.valueOf(maxNo));
                    		iSapBoxNoteDtlService.update(box, boxEq);
                    	}
                    	sapPackageListDtl.setSpmsItemNo(String.valueOf(maxNo));
                    } else {
                    	sapPackageListDtl.setSpmsItemNo(String.valueOf(maxNo+1));
                    }
                    iSapPackageListDtlService.insertSapPackageListDtl(sapPackageListDtl);
                } else {
                	QueryWrapper<SapPackageListDtl> queryWrapper = new QueryWrapper<SapPackageListDtl>().eq("PACKAGE_LIST", packageList).eq("PACKAGE_CODE", boxNote).eq("WHS_OUT_UUID", whsOutUuid);
                	iSapPackageListDtlService.update(sapPackageListDtl, queryWrapper);
                }
//                if ("1".equals(unplanned)){
//                    if (actualOperateQty > 0){
//                        /** 多出的箱码，新增数据到装箱单箱码关系表中，unplanned = 1 表示是计划之外新增出库的箱码。不更新出库明细。 */
//                        iSapPackageBoxService.addSapPackageBox(packageList, boxNote, actualOperateQty, "1", whsOutUuid, containerBrand, packingSpec, loadingType);
//                    }
//                }else {
//                    iSapPackageBoxService.updateSapPackageBox(packageList, boxNote, actualOperateQty, whsOutUuid, containerBrand, packingSpec, loadingType);
//                }
            }
            /** 更新出库任务明细表对应的行状态为 2 */
            iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(whsOutUuid, userId, WHS_OUT_TASK_UPDATE);

            WhsOut whsOut = new WhsOut();
            whsOut.setCabinetNumber(whsOutObj.getString("cabinetNumber"));
            whsOut.setTaskStatus(whsOutObj.getString("targetStatus"));
            whsOut.setActualLoadingDate(whsOutObj.getString("actualLoadingDate"));
            whsOut.setMonitorLoading(whsOutObj.getString("monitorLoading"));
            whsOut.setLoadingType(whsOutObj.getString("loadingType"));
            whsOut.setRemarks(whsOutObj.getString("remarks"));
            whsOut.setCabinetCheckResult(whsOutObj.getString("cabinetCheckResult"));
            whsOut.setLeadSeal(whsOutObj.getString("leadSeal"));
            if(StringUtils.isNotEmpty(whsOutObj.getString("operateUser"))){
                whsOut.setOperateUser(whsOutObj.getString("operateUser"));
            }
            whsOut.setPdaSN(StringUtils.isNotEmpty(whsOutObj.getString("pdaSN")) ? whsOutObj.getString("pdaSN") : "");
            whsOut.setPdaTime(StringUtils.isNotEmpty(whsOutObj.getString("pdaTime")) ? whsOutObj.getString("pdaTime") : "");

            iSapPackageListDtlService.uSapPackageListDtl(whsOutObj.getString("targetStatus"), whsOutObj.getString("uuid"));
            this.baseMapper.update(whsOut, new QueryWrapper<WhsOut>().eq("UUID", whsOutObj.getString("uuid")));
            if ("2".equals(whsOutObj.getString("targetStatus"))) {
            	iSapDeliveryNoteLogService.insertSapRequestRecord(whsOutUuid, userId, WHS_OUT_TASK_CLOSE, null, doc.toJSONString());
            }
            // 计算最大包装件数
            // 查询箱码跟物料关系数据
            List<Map<String, Object>> boxNoteAndMaterials = this.baseMapper.selectBoxNoteAndMaterial(whsOutObj.getString("uuid"));
            // 备件货：统计在一个出库任务中一个物料存在箱码的数量
			Map<String, List<Map<String, Object>>> materialNoMaps = boxNoteAndMaterials.stream().collect(Collectors.groupingBy(o -> o.get("materialNo").toString()));
			for (String materialNo : materialNoMaps.keySet()) {
				List<Map<String, Object>> materialList = materialNoMaps.get(materialNo);
				// 将最大包装件数更新到装箱单行中
				for (Map<String, Object> map : materialList) {
					if (map.get("boxNote") == null) {
						continue;
					}
					QueryWrapper<SapBoxNoteDtl> eq = new QueryWrapper<SapBoxNoteDtl>().eq("BOX_NOTE", map.get("boxNote").toString())
							.eq("MATERIAL_NO", map.get("materialNo").toString());
					SapBoxNoteDtl dtl = new SapBoxNoteDtl();
					dtl.setPackageQuantityMax(String.valueOf(materialList.size()));
					dtl.setPackageUnitMax("PCS");
					iSapBoxNoteDtlService.update(dtl, eq);
				}
			}
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    /**
     * 更新仓库库存表，扣减出库明细中的箱码仓库库存，记录仓库库存台账
     */
    int updateWhsInventoryInfo(JSONObject doc, String userId) {
        String whsOutUuid = doc.getJSONObject("whsOut").getString("uuid");
        try {
            JSONObject whsOutObj = doc.getJSONObject("whsOut");
            JSONArray whsOutDtlObj = doc.getJSONArray("whsOutDtl");
            String werks = whsOutObj.getString("werks");
            String whsLocationCode = whsOutObj.getString("whsLocationCode");
            for (int i = 0; i < whsOutDtlObj.size(); i++) {
                JSONObject whsOutDtl = whsOutDtlObj.getJSONObject(i);
                String boxNote = whsOutDtl.getString("boxNote");
                double actualOperateQty = StringUtils.isEmpty(whsOutDtl.getString("actualOperateQty"))
                        ? 0 : whsOutDtl.getDouble("actualOperateQty");
                if (actualOperateQty > 0) {
                    iWhsInventoryInfoService.deleteBoxNoteWhsInventory(werks, whsLocationCode, boxNote);
                    iWhsOperateLogService.insertWhsOperateLogBC(boxNote, actualOperateQty, whsOutUuid, SPARE_PARTS_OUT_WHS, userId, werks, whsLocationCode);
                }
            }
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    /**
     * 如果下架明细和出库明细存在差异项则转化成上架任务，新建上架任务，新建上架任务明细，记录上架任务新建日志
     */
    int addShelfOnTask(JSONObject doc, String userId) {
        try {
            String shelfOnTaskUuid = UUID.randomUUID().toString().replaceAll("-", "").toLowerCase();
            JSONObject whsOutObj = doc.getJSONObject("whsOut");
            JSONArray remainingBoxNote = doc.getJSONArray("remainingBoxNote");
            if (remainingBoxNote.size() > 0) {
                /** 生成（状态1）待上架（类型1）上架任务，生成上架任务明细，记录上架任务初始化日志 */
                ShelfOnTask shelfOnTask = new ShelfOnTask();
                shelfOnTask.setUuid(shelfOnTaskUuid);
                shelfOnTask.setWerks(whsOutObj.getString("werks"));
                shelfOnTask.setWhsLocationCode(whsOutObj.getString("whsLocationCode"));
                shelfOnTask.setCreateUserId(userId);
                shelfOnTask.setCreateDate(doc.getString("submitDate").replaceAll("-", ""));
                shelfOnTask.setTaskStatus("1");
                shelfOnTask.setPdaSN(doc.getString("pdaSn"));
                shelfOnTask.setTaskType("1");
                shelfOnTask.setRelateDocNumber(whsOutObj.getString("uuid"));
                int resp = iShelfOnTaskService.addShelfOnTask(shelfOnTask);

                for (int i = 0; i < remainingBoxNote.size(); i++) {
                    double quantity = StringUtils.isEmpty(remainingBoxNote.getJSONObject(i).getString("quantity")) ?
                            0 : remainingBoxNote.getJSONObject(i).getDouble("quantity");
                    if (quantity > 0) {
                        ShelfOnTaskDtl shelfOnTaskDtl = new ShelfOnTaskDtl();
                        shelfOnTaskDtl.setUuid(shelfOnTaskUuid);
                        shelfOnTaskDtl.setTaskRowId(String.valueOf((i + 1) * 10));
                        shelfOnTaskDtl.setBoxNote(remainingBoxNote.getJSONObject(i).getString("boxNote"));
                        shelfOnTaskDtl.setQuantity(Double.parseDouble(remainingBoxNote.getJSONObject(i).getString("quantity")));
                        iShelfOnTaskService.addShelfOnTaskDtl(shelfOnTaskDtl);
                    }
                }
                /** 记录上架任务关联日志——上架任务初始化；记录出库任务关联日志——上架任务初始化； */
                iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(shelfOnTaskUuid, userId, GENERATE_SHELF_ON_TASK);
                iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(whsOutObj.getString("uuid"), userId, GENERATE_SHELF_ON_TASK);
            }
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    /**
     * {
     * "whsOut": {
     * "uuid": "17e02ac85c8d491b8889e14d0f453057",
     * "werks": "CN01",
     * "whsLocationCode": "1011",
     * "cabinetNumber": "JZX20217788",
     * "currentStatus": 1,
     * "targetStatus": 2,
     * "actualLoadingDate": "2021-05-12",
     * "monitorLoading": "zhang san",
     * "loadingType": "A",
     * "remarks": "remarks",
     * "cabinetCheckResult": "ok",
     * "leadSeal":"123456789"
     * },
     * "whsOutDtl": [{
     * "packageList": "PL1001",
     * "materialNo": "210026516",
     * "actualOperateQty": 10,
     * "storageUuidList": [{
     * "storageUuid": "b95946ac1dab42b79ade3438acb0b511",
     * "qty": 5
     * }, {
     * "storageUuid": "b95946ac1dab42b79ade3438acb0b512",
     * "qty": 5
     * }],
     * "unplanned": ""
     * }, {
     * "packageList": "PL1001",
     * "boxNote": "210026515",
     * "actualOperateQty": "10",
     * "storageUuidList": [{
     * "storageUuid": "b95946ac1dab42b79ade3438acb0b511",
     * "qty": 10
     * }],
     * "unplanned": "1"
     * }]
     * }
     * 贸易货出库任务完成，补单形式.
     * 1.先判断 whsOut.targetStatus 值，1表示保存功能，不做库存检查；2表示提交功能，需检查各物料库存；
     * 2.对于出库明细中 unplanned = '1' 的项则在装箱单明细中新增行；
     * 3.用户提交结果，则更新出库任务表头；记录操作日志；
     * 4.更新出库任务明细/装箱单明细，更新实际出库数量(基本单位)字段；有新增行则新增关联数据；记录操作日志；
     * 5.扣减出库贸易货的储位库存，记录出未出库台账；
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult updateWhsOutM(JSONObject doc, String userId) throws Exception{
        ResponseResult responseResult = checkDataValidateM(doc);
        if (responseResult.getCode() == 100) {
            return responseResult;
        }
        int sWhsOut = saveWhsOut(doc, userId);
        if (sWhsOut < 0) {
            throw new Exception("更新出库任务和出库任务明细过程出错 saveWhsOut");
        }

        int uWhsStorage = updateWhsStorage(doc, userId);
        if (uWhsStorage < 0) {
            throw new Exception("更新贸易货储位库存过程出错 updateWhsStorage");
        }
        return ResponseResult.success();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized ResponseResult withdrawWhsOutM(String whsOutUuid, String currentStatus, String userId) throws Exception{
        WhsOut whsOut = this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
        if (whsOut == null) {
            return ResponseResult.error("出库任务 " + whsOutUuid + " 不存在");
        }
        if (!currentStatus.equals(whsOut.getTaskStatus())) {
            return ResponseResult.error("出库任务 " + whsOut.getWhsOutNo() + "    " + whsOutUuid + " 状态已更新请刷新后重试");
        }
        List<SapPackageListDtl> whsOutDtl = iSapPackageListDtlService.getSapPackageListDtlsByWhsOutUuid(whsOutUuid);
        if (null == whsOutDtl || whsOutDtl.size() <= 0) {
            return ResponseResult.error("获取出库任务明细失败");
        }
        // 如果已经提交到了SAP，需要首先回撤SAP
        if (currentStatus.equals(WHS_OUT_Status_Completed) || currentStatus.equals(WHS_OUT_Status_SuccessedSAP)) {
        	// 撤销前调用接口查询SAP装箱单状态
        	JSONObject toSelectSapData = this.setSelectPackListStatusData(whsOut.getPackageList());
        	JSONObject message1 = JSONObject.parseObject(iFeignServiceCommon.sendRestToSapCommon(JSONObject.parseObject(toSelectSapData.toString())));
        	// 保存接口日志
            iSysLogMqService.addSysLogMq(SEND_MSG, "ZZXD_IF_SPMS_11", "查询装箱单在SAP状态接口", "ZZXD_IF_SPMS_11", toSelectSapData.toJSONString(), message1.toString(), null);
            JSONObject RETURN_DATA1 = message1.getJSONObject("RESPONSE").getJSONObject("RETURN_DATA");
            if ("N".equals(RETURN_DATA1.getString("STATUS"))) {
            	throw new Exception("装箱单在SAP的状态不允许进行撤销："+message1.toJSONString());
            } else if ("E".equals(RETURN_DATA1.getString("STATUS"))) {
            	throw new Exception("装箱单在SAP未找到数据："+message1.toJSONString());
            }
            // 调用ZZXD_IF_SPMS_08接口进行撤销
        	// 设置推送SAP数据
            JSONObject toSapData = this.setDataToSap(whsOutUuid, whsOut, "X");
            JSONObject dataToSap = JSONObject.parseObject(toSapData.toString());
            JSONObject message = JSONObject.parseObject(iFeignServiceCommon.sendRestToSapCommon((dataToSap)));
            // 保存接口日志
            iSysLogMqService.addSysLogMq(SEND_MSG, "ZZXD_IF_SPMS_08", "出库任务撤销传输至SAP接口", "ZZXD_IF_SPMS_08", dataToSap.toJSONString(), message.toString(), null);
            JSONObject RETURN_DATA = message.getJSONObject("RESPONSE").getJSONObject("RETURN_DATA");
            JSONArray ET_RET = RETURN_DATA.getJSONArray("ET_RET");
            if (ET_RET.size() > 0 && "S".equals(ET_RET.getJSONObject(0).getString("TYPE"))) {
            	
            } else {
            	throw new Exception("撤销同步SAP失败："+message.toJSONString());
            }
        }
        // 进行增加库
        if (currentStatus.equals(WHS_OUT_Status_PendingSyncSAP) || currentStatus.equals(WHS_OUT_Status_Completed) ||
        		currentStatus.equals(WHS_OUT_Status_FailedSAP) || currentStatus.equals(WHS_OUT_Status_SuccessedSAP)) {
            // 原储位加回库存
            ResponseResult withdrawStorageRes = withdrawWhsOutStorageM(whsOut, whsOutDtl, userId);
            if (withdrawStorageRes.getCode() != 200) {
                throw new Exception(withdrawStorageRes.getMsg());
            }
        }

        // SAP回撤成功，储位回撤成功
        whsOut.setTaskStatus(WHS_OUT_Status_InProcessing);
        this.baseMapper.update(whsOut, new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
        iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(whsOutUuid, userId, WHS_WhsOut_Withdraw);
        // 更新装箱单状态
        this.updateSapPackageList(userId, whsOut.getPackageList(), "1");
        // 更新装箱单行项目状态为待完成
        iSapPackageListDtlService.uSapPackageListDtl("1", whsOutUuid);
        return ResponseResult.success().add("whsOut", whsOut);
    }


    private JSONObject setSelectPackListStatusData(String packageList) {
    	JSONObject obj = new JSONObject();
        JSONObject REQUEST = new JSONObject();
        JSONObject ESB_ATTRS = new JSONObject();
        JSONObject REQUEST_DATA = new JSONObject();
        ESB_ATTRS.put("App_ID", "SPMS");
        ESB_ATTRS.put("Target_ID", zxdSapClient);
        ESB_ATTRS.put("Application_ID", "00020000000002");
        ESB_ATTRS.put("Transaction_ID", UUID.randomUUID().toString());
        Map<String, Object> fileds = new HashMap<>();
    	fileds.put("XDNUM", packageList);
        REQUEST_DATA.put("fileds", fileds);
        REQUEST_DATA.put("Operation", "ZZXD_IF_SPMS_11");
        REQUEST_DATA.put("Type", "ZZXD_IF_SPMS_11");
        REQUEST.put("ESB_ATTRS", ESB_ATTRS);
        REQUEST.put("REQUEST_DATA", REQUEST_DATA);
        obj.put("REQUEST", REQUEST);
		return obj;
		
	}

	public ResponseResult checkDataValidateM(JSONObject doc) {
        try {
            if (!doc.containsKey("whsOut") || !doc.containsKey("whsOutDtl")) {
                return ResponseResult.error("请求参数错误请重试");
            }
            JSONObject whsOutObj = doc.getJSONObject("whsOut");
            String whsOutUuid = whsOutObj.getString("uuid");
            String currentStatus = whsOutObj.getString("currentStatus");
            String targetStatus = whsOutObj.getString("targetStatus");
            WhsOut whsOut = this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
            if (whsOut == null) {
                return ResponseResult.error("出库任务 " + whsOutUuid + " 不存在");
            }
            if (!currentStatus.equals(whsOut.getTaskStatus())) {
                return ResponseResult.error("出库任务 " + whsOut.getWhsOutNo() + "    " + whsOutUuid + " 状态已更新请刷新后重试");
            }
            /** 贸易货提交出库结果，需要检查库存信息 */
            if ("2".equals(targetStatus)) {
                JSONArray whsOutDtlObj = doc.getJSONArray("whsOutDtl");
                if (whsOutDtlObj.size() == 0) {
                    return ResponseResult.error();
                }
                for (int i = 0; i < whsOutDtlObj.size(); i++) {
                    JSONObject whsOutDtl = whsOutDtlObj.getJSONObject(i);
                    String materialNo = whsOutDtl.getString("materialNo");
                    double actualOperateQty = StringUtils.isEmpty(whsOutDtl.getString("actualOperateQty")) ? 0 : whsOutDtl.getDoubleValue("actualOperateQty");
                    JSONArray storageUuidList = whsOutDtl.getJSONArray("storageUuidList");
                    if (actualOperateQty > 0) {
                        if (storageUuidList.size() == 0) {
                            return ResponseResult.error("参数storageUuidList结构错误请重试");
                        }
                        for (int j = 0; j < storageUuidList.size(); j++) {
                            String storageUuid = storageUuidList.getJSONObject(j).getString("storageUuid");
                            String qtyString = storageUuidList.getJSONObject(j).getString("qty");
                            double qty = StringUtils.isEmpty(qtyString) ? 0 : Double.parseDouble(qtyString);
                            WhsStorageInventory whsStorageInventory = iWhsStorageInventoryService.getStorageMaterialNo(storageUuid, materialNo);
                            if (whsStorageInventory == null || whsStorageInventory.getQuantity() < qty) {
                                return ResponseResult.error("物料 " + materialNo + " 在储位 " + storageUuid + " 库存不足");
                            }
                        }
                    }
                }
            }
            return ResponseResult.success();
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseResult.error().add("error", e.getMessage());
        }
    }

    int saveWhsOut(JSONObject doc, String userId) {
        try {
            JSONObject whsOutObj = doc.getJSONObject("whsOut");
            JSONArray whsOutDtlArray = doc.getJSONArray("whsOutDtl");
            String whsOutUuid = whsOutObj.getString("uuid");
            String targetStatus = whsOutObj.getString("targetStatus");
            String packageList = whsOutObj.getString("packageList");
            /** 每次保存或提交之前，根据出库任务uuid和计划外的行标记，先删除已有的 计划外的出库行项目，重新建立计划外的行项目数据。 */
            iSapPackageListDtlService.deleteUnplannedSapPackageListDtl(whsOutUuid, "1");
            for (int i = 0; i < whsOutDtlArray.size(); i++) {
                JSONObject whsOutDtl = whsOutDtlArray.getJSONObject(i);
//                String packageList = whsOutDtl.getString("packageList");
                String item = whsOutDtl.getString("item");
                String unplanned = whsOutDtl.getString("unplanned");
                String containerBrand = whsOutDtl.getString("containerBrand");
                String colorNumber = whsOutDtl.getString("colorNumber");
                String thickness = whsOutDtl.getString("thickness");
                String spmsItemNo = whsOutDtl.getString("spmsItemNo");
                String batchNo = whsOutDtl.getString("batchNo");
                String bukrs = whsOutDtl.getString("bukrs");
                String consignee = whsOutDtl.getString("consignee");
                String cabinetModel = whsOutDtl.getString("cabinetModel");
                String packageQuantityMax = whsOutDtl.getString("packageQuantityMax");
                String packageUnitMax = whsOutDtl.getString("packageUnitMax");
                String containerLoadingPosition = whsOutDtl.getString("containerLoadingPosition");
//                String cabinetNumber = whsOutDtl.getString("cabinetNumber");
//                String cabinetSerialNumber = whsOutDtl.getString("cabinetSerialNumber");
//                String leadSeal = whsOutDtl.getString("leadSeal");
                String basicUnit = whsOutDtl.getString("basicUnit");
                String packingUnit = whsOutDtl.getString("packingUnit");
                String cabinetVolume = whsOutDtl.getString("cabinetVolume");
                String cabinetWeight = whsOutDtl.getString("cabinetWeight");
                String cabinetWeightUnit = whsOutDtl.getString("cabinetWeightUnit");
                String actualReceivingWerks = whsOutDtl.getString("actualReceivingWerks");
                String actualAvgGrossWeight = whsOutDtl.getString("actualAvgGrossWeight");
                String netWeight = whsOutDtl.getString("netWeight");
                String plannedPackingNumber = whsOutDtl.getString("plannedPackingNumber");
                String plannedPackingQty = whsOutDtl.getString("plannedPackingQty");
                double actualPackingNumber = StringUtils.isEmpty(whsOutDtl.getString("actualPackingNumber")) ?
                        0 : whsOutDtl.getDouble("actualPackingNumber");
                double customsDeclarationNumber = StringUtils.isEmpty(whsOutDtl.getString("customsDeclarationNumber")) ?
                        0 : whsOutDtl.getDouble("customsDeclarationNumber");
                String packingSpec = whsOutDtl.getString("packingSpec");
                String loadingType = whsOutDtl.getString("loadingType");
//                String loadingWerks = whsOutDtl.getString("loadingWerks");
                String receivingWerks = whsOutDtl.getString("receivingWerks");
//                String loadingWhsLocationCode = whsOutDtl.getString("loadingWhsLocationCode");
                String remarks = whsOutDtl.getString("remarks");
                String shipmentCategory = whsOutDtl.getString("shipmentCategory");
                String shipper = whsOutDtl.getString("shipper");
                String volumeQty = whsOutDtl.getString("volumeQty");
                String volumeUnit = whsOutDtl.getString("volumeUnit");
                String weightUnit = whsOutDtl.getString("weightUnit");
                double actualOperateQty = StringUtils.isEmpty(whsOutDtl.getString("actualOperateQty")) ?
                        0 : whsOutDtl.getDouble("actualOperateQty");
                String storageUuidList = whsOutDtl.getJSONArray("storageUuidList").toJSONString();

                SapPackageListDtl sapPackageListDtl = new SapPackageListDtl();
                // 行项目只有待完成、已完成、已关闭状态
                if ("4".equals(targetStatus)) {
                	sapPackageListDtl.setSpmsStatus("4");
                } else if ("3".equals(targetStatus)) {
                	sapPackageListDtl.setSpmsStatus("4");
                } else {
                	sapPackageListDtl.setSpmsStatus("1");
                }
                sapPackageListDtl.setUnplanned(unplanned);
                sapPackageListDtl.setActualOperateQty(actualOperateQty);
                sapPackageListDtl.setTempStorage(storageUuidList);
                sapPackageListDtl.setContainerBrand(containerBrand);
                sapPackageListDtl.setColorNumber(colorNumber);
                sapPackageListDtl.setThickness(thickness);
                sapPackageListDtl.setBasicUnit(basicUnit);
                sapPackageListDtl.setPackingUnit(packingUnit);
                sapPackageListDtl.setBukrs(bukrs);
                sapPackageListDtl.setShipper(shipper);
                sapPackageListDtl.setConsignee(consignee);
                sapPackageListDtl.setLoadingWerks(whsOutObj.getString("werks"));
                sapPackageListDtl.setReceivingWerks(receivingWerks);
                sapPackageListDtl.setLoadingWhsLocationCode(whsOutObj.getString("whsLocationCode"));
                sapPackageListDtl.setCabinetVolume(StringUtils.isNotEmpty(cabinetVolume) ? Double.valueOf(cabinetVolume) : 0);
                sapPackageListDtl.setCabinetWeight(StringUtils.isNotEmpty(cabinetWeight) ? Double.valueOf(cabinetWeight) : 0);
                sapPackageListDtl.setNetWeight(StringUtils.isNotEmpty(netWeight) ? Double.valueOf(netWeight) : 0);
                sapPackageListDtl.setPlannedPackingNumber(StringUtils.isNotEmpty(plannedPackingNumber) ? Double.valueOf(plannedPackingNumber) : 0);
                sapPackageListDtl.setPlannedPackingQty(StringUtils.isNotEmpty(plannedPackingQty) ? Double.valueOf(plannedPackingQty) : 0);
                sapPackageListDtl.setWeightUnit(cabinetWeightUnit);
                sapPackageListDtl.setCabinetSerialNumber(whsOutObj.getString("cabinetSerialNumber"));
                sapPackageListDtl.setCabinetModel(StringUtils.isNotEmpty(cabinetModel) ? cabinetModel : whsOutObj.getString("cabinetModel"));
                sapPackageListDtl.setActualReceivingWerks(StringUtils.isNotEmpty(actualReceivingWerks) ? actualReceivingWerks : receivingWerks);
                sapPackageListDtl.setActualAvgGrossWeight(actualAvgGrossWeight);
                sapPackageListDtl.setActualPackingNumber(actualPackingNumber);
                sapPackageListDtl.setCustomsDeclarationNumber(customsDeclarationNumber);
                sapPackageListDtl.setPackingSpec(packingSpec);
                sapPackageListDtl.setLoadingType(loadingType);
                sapPackageListDtl.setBatchNo(batchNo);
                sapPackageListDtl.setPackageQuantityMax(packageQuantityMax);
                sapPackageListDtl.setPackageUnitMax(packageUnitMax);
                sapPackageListDtl.setContainerLoadingPosition(containerLoadingPosition);
                // 柜号、铅封号取头的值
                sapPackageListDtl.setCabinetNumber(whsOutObj.getString("cabinetNumber"));
                sapPackageListDtl.setLeadSeal(whsOutObj.getString("leadSeal"));
                sapPackageListDtl.setRemarks(remarks);
                sapPackageListDtl.setShipmentCategory(shipmentCategory);
                sapPackageListDtl.setVolumeUnit(volumeUnit);
                sapPackageListDtl.setWeightUnit(weightUnit);
                sapPackageListDtl.setCabinetVolume(StringUtils.isNotEmpty(volumeQty) ? Double.valueOf(volumeQty) : 0);
                sapPackageListDtl.setPackageList(packageList);
                

                /** 计划外的行需新增数据到装箱单明细中；计划内的贸易货出库，按 装箱单号 + 行号 更新出库明细实际出库数量、出库储位料号数量等信息。已有的行，item不会为空。 */
                if ("1".equals(unplanned)) {
                    sapPackageListDtl.setWhsOutUuid(whsOutUuid);
                    sapPackageListDtl.setMaterialNo(whsOutDtl.getString("materialNo"));
                    sapPackageListDtl.setGoodsType("M");
                    int maxNo = iSapPackageListDtlService.selectMaxSpmsItemNo(packageList);
                    sapPackageListDtl.setSpmsItemNo(String.valueOf(maxNo+1));
                    iSapPackageListDtlService.insertSapPackageListDtl(sapPackageListDtl);
                } else {
                	QueryWrapper<SapPackageListDtl> queryWrapper = new QueryWrapper<SapPackageListDtl>().eq("PACKAGE_LIST", packageList).eq("ITEM", item).eq("SPMS_ITEM_NO", spmsItemNo).eq("WHS_OUT_UUID", whsOutUuid);
                	iSapPackageListDtlService.update(sapPackageListDtl, queryWrapper);
                }
            }
            iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(whsOutUuid, userId, WHS_OUT_TASK_UPDATE);

            WhsOut whsOut = new WhsOut();
            whsOut.setCabinetNumber(whsOutObj.getString("cabinetNumber"));
            whsOut.setTaskStatus(targetStatus);
            whsOut.setActualLoadingDate(whsOutObj.getString("actualLoadingDate"));
            whsOut.setMonitorLoading(whsOutObj.getString("monitorLoading"));
            whsOut.setLoadingType(whsOutObj.getString("loadingType"));
            whsOut.setRemarks(whsOutObj.getString("remarks"));
            whsOut.setCabinetCheckResult(whsOutObj.getString("cabinetCheckResult"));
            whsOut.setLeadSeal(whsOutObj.getString("leadSeal"));
            if(StringUtils.isNotEmpty(whsOutObj.getString("operateUser"))){
                whsOut.setOperateUser(whsOutObj.getString("operateUser"));
            }
            this.baseMapper.update(whsOut, new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
            if ("2".equals(targetStatus)) {
            	iSapDeliveryNoteLogService.insertSapRequestRecord(whsOutUuid, userId, WHS_OUT_TASK_CLOSE, null, doc.toJSONString());
            }
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    /**
     * 撤销贸易货出库，原储位加回库存
     */
    @Transactional(rollbackFor = Exception.class)
    ResponseResult withdrawWhsOutStorageM(WhsOut whsOut, List<SapPackageListDtl> packageListDtls, String userId) throws Exception{
//        try {
            String whsOutUuid = whsOut.getUuid();
            String werks = whsOut.getWerks();
            String whsLocationCode = whsOut.getWhsLocationCode();

            for (int i = 0; i < packageListDtls.size(); i++) {
                SapPackageListDtl sapPackageListDtl = packageListDtls.get(i);
                String materialNo = sapPackageListDtl.getMaterialNo();
                String item = sapPackageListDtl.getItem();
                double actualOperateQty = null == sapPackageListDtl.getActualOperateQty() ? 0 : sapPackageListDtl.getActualOperateQty();
                if (actualOperateQty > 0) {
                    JSONArray storageUuidList = JSONArray.parseArray(sapPackageListDtl.getTempStorage());
                    for (int j = 0; j < storageUuidList.size(); j++) {
                        JSONObject storage = storageUuidList.getJSONObject(j);
                        String storageUuid = storage.getString("storageUuid");
                        String qtyString = storage.getString("qty");
                        double qty = StringUtils.isEmpty(qtyString) ? 0 : Double.parseDouble(qtyString);
                        String storageNo = storage.getString("storageNo");
                        if (StringUtils.isEmpty(storageUuid) || StringUtils.isEmpty(storageNo) || qty <= 0) {
                            throw new Exception("error for " + materialNo + " on storageUuid: " +
                                    storageUuid + ",storageNo:" + storageNo +
                                    ", qty: " + qty);
                        }
                        if (qty > 0) {
                            /** 贸易货储位库存加，加成功则记录储位台账 */
                            if (iWhsStorageInventoryService.updateWhsStorageInventoryMOld(storageUuid, materialNo, qty) < 0) {
                                throw new Exception("Update Storage Inventory failed for " + materialNo + " on storageUuid: " +
                                        storageUuid + ",storageNo:" + storageNo +
                                        ", qty: " + qty);
                            }
                            iWhsOperateLogService.insertWhsOperateLog(
                                    Constans.WhsOperateLog_DocumentType_NanShaStorageLog,
                                    storageUuid,
                                    materialNo,
                                    "",
                                    qty,
                                    Constans.WhsOperateLog_OperationType_ShelfOn,
                                    whsOutUuid,
                                    Constans.WHS_WhsOut_Withdraw_ShelfOn,
                                    userId,
                                    Constans.WHS_WhsOut_Withdraw_ShelfOn + materialNo + ":" + qty,
                                    werks,
                                    whsLocationCode, item);
                        }
                    }
                }
            }
            return ResponseResult.success();
//        } catch (Exception e) {
//            e.printStackTrace();
//            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
//            return ResponseResult.error(e.getMessage());
//        }
    }

    /**
     * 贸易货出库，扣减储位库存
     */
    int updateWhsStorage(JSONObject doc, String userId) {
        try {
            JSONObject whsOutObj = doc.getJSONObject("whsOut");
            JSONArray whsOutDtlArray = doc.getJSONArray("whsOutDtl");
            String whsOutUuid = whsOutObj.getString("uuid");
            String targetStatus = whsOutObj.getString("targetStatus");
            String werks = whsOutObj.getString("werks");
            String whsLocationCode = whsOutObj.getString("whsLocationCode");
            /** targetStatus 为2即提交单据时，才需要计算扣减储位库存 */
            if ("2".equals(targetStatus)) {
                for (int i = 0; i < whsOutDtlArray.size(); i++) {
                    String materialNo = whsOutDtlArray.getJSONObject(i).getString("materialNo");
                    double actualOperateQty = StringUtils.isEmpty(whsOutDtlArray.getJSONObject(i).getString("actualOperateQty"))
                            ? 0 : whsOutDtlArray.getJSONObject(i).getDoubleValue("actualOperateQty");
                    JSONArray storageUuidList = whsOutDtlArray.getJSONObject(i).getJSONArray("storageUuidList");
                    if (actualOperateQty > 0) {
                    	if (storageUuidList.size() == 0) {
                            throw new Exception("参数storageUuidList结构错误请重试");
                        }
                        for (int j = 0; j < storageUuidList.size(); j++) {
                            String storageUuid = storageUuidList.getJSONObject(j).getString("storageUuid");
                            String qtyString = storageUuidList.getJSONObject(j).getString("qty");
                            double qty = StringUtils.isEmpty(qtyString) ? 0 : Double.parseDouble(qtyString);

                            if (qty > 0) {
                                /** 贸易货储位库存扣减，扣减成功则记录储位台账 */
                                int resp = iWhsStorageInventoryService.updateWhsStorage(storageUuid, materialNo, qty);
                                if (resp > 0) {
                                    iWhsOperateLogService.insertWhsOperateLogMX(storageUuid, materialNo, qty, whsOutUuid, GOODS_OUT_STORAGE, userId, werks, whsLocationCode);
                                } else {
                                	throw new Exception("物料："+materialNo+"在储位："+storageUuid+"的储位数量不够扣减");
                                }
                            }
                        }
                    }
                }
            }
            return 1;
        } catch (Exception e) {
        	log.error("贸易货出库扣减库存出错："+e.getMessage());
            e.printStackTrace();
            return -1;
        }
    }


    /**
     * 首先根据出库任务uuid查询，同一个装箱单下所有  未成功提交SAP的出库任务  数量，不等于 1 则不是最后一次提交SAP
     * 获取出库任务执行结果提交SAP，提交成功则更新出库任务表头、行项目，建立日志。
     * 返回信息包含 出库任务结果表头、出库任务结果行项目、备件货还包括箱码
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized ResponseResult getWhsOutToSap(String uuid, String userId)throws Exception {
        String confirmLastSubmission = "0";
        /** 获取出库任务同一个装箱单下所有  未成功提交SAP  的出库任务数量，来判断当前任务是否是最后一个提交。最后一个提交会多出 PT_STATE 一些字段； */
        /** 20210713 修改最后一次提交判断逻辑：同一个装箱单下的出库任务，状态不为 3（任务关闭），不为4（提交SAP成功）的出库任务数量如果为 1，则表示最后一次提交 */
        String getWhsOutStatusCount = this.baseMapper.getWhsOutStatusCount(uuid);
        if ("1".equals(getWhsOutStatusCount)) {
            confirmLastSubmission = "1";
        }
        WhsOut whsOut = this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", uuid));
        if (whsOut == null || !"2".equals(whsOut.getTaskStatus())) {
            return ResponseResult.error("任务当前状态禁止提交");
        }
//        ResponseResult getDataToSap = getDataToSap(uuid, confirmLastSubmission, whsOut.getGoodsType(), whsOut.getPackageList());
//        if (getDataToSap.getCode() != 200) {
//            return getDataToSap;
//        }
        // 更新出库任务状态为提交SAP成功
        this.baseMapper.updateWhsOut(uuid, "4");
        // 更新装箱单行项目状态为已完成
        iSapPackageListDtlService.uSapPackageListDtl("4", uuid);
        if ("1".equals(confirmLastSubmission)) {
            // 如果是最后一次提交SAP，且成功的话，就将装箱单表头状态信息更新为 2处理完成；行项目在每次成功提交会自动更新出库任务对应行，此处忽略
            updateSapPackageList(userId, whsOut.getPackageList(), "2");
        }
        // 设置推送SAP数据
        JSONObject toSapData = this.setDataToSap(uuid, whsOut, null);
        JSONObject dataToSap = JSONObject.parseObject(toSapData.toString());
        String nowDate = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss");
        log.info("ZZXD_IF_SPMS_08提交SAP开始："+nowDate+"---"+dataToSap.toJSONString());
        JSONObject message = JSONObject.parseObject(iFeignServiceCommon.sendRestToSapCommon((dataToSap)));
        log.info("ZZXD_IF_SPMS_08提交SAP结束："+nowDate+"---"+message.toJSONString());
        // 保存接口日志
        iSysLogMqService.addSysLogMq(SEND_MSG, "ZZXD_IF_SPMS_08", "实际装柜结果传输至SAP接口", "ZZXD_IF_SPMS_08", dataToSap.toJSONString(), message.toString(), null);
        JSONObject RETURN_DATA = message.getJSONObject("RESPONSE").getJSONObject("RETURN_DATA");
        JSONArray ET_RET = RETURN_DATA.getJSONArray("ET_RET");
        if (ET_RET.size() > 0 && "S".equals(ET_RET.getJSONObject(0).getString("TYPE"))) {
            // SAP会返回装柜实际行项目，需要进行更新
        	if ("M".equals(whsOut.getGoodsType())) {
        		for (int i = 0; i < ET_RET.size(); i++) {
        			JSONObject objs = ET_RET.getJSONObject(i);
        			// 装箱单号
//                	String xdnum = objs.getString("XDNUM");
                	// 装箱单行号
//                	String posnr_2 = StringUtils.isNotEmpty(objs.getString("POSNR_2")) ? objs.getString("POSNR_2").replaceAll("^(0+)", "") : "";
                	// SPMS行项目号
                	String spmsPosnr = objs.getString("SPMS_POSNR").replaceAll("^(0+)", "");
                	QueryWrapper<SapPackageListDtl> eq = new QueryWrapper<SapPackageListDtl>().eq("PACKAGE_LIST", whsOut.getPackageList())
                			.eq("WHS_OUT_UUID", whsOut.getUuid()).eq("SPMS_ITEM_NO", spmsPosnr);
                	SapPackageListDtl dtl = new SapPackageListDtl();
					dtl.setSapRealityItemNo(objs.getString("ZXD_POSNR"));
					dtl.setSapRealitySerialNumber(objs.getString("ZZGLS"));
					iSapPackageListDtlService.update(dtl, eq);
        		}
        	} else {
        		QueryWrapper<SapPackageListDtl> eq = new QueryWrapper<SapPackageListDtl>().eq("PACKAGE_LIST", whsOut.getPackageList())
            			.eq("WHS_OUT_UUID", whsOut.getUuid());
            	List<SapPackageListDtl> dtlList = iSapPackageListDtlService.list(eq);
        		for (int i = 0; i < ET_RET.size(); i++) {
        			JSONObject objs = ET_RET.getJSONObject(i);
        			// 装箱单号
//                	String xdnum = objs.getString("XDNUM");
                	// 装箱单行号
//                	String posnr_2 = StringUtils.isNotEmpty(objs.getString("POSNR_2")) ? objs.getString("POSNR_2").replaceAll("^(0+)", "") : "";
                	// SPMS行项目号
                	String spmsPosnr = objs.getString("SPMS_POSNR").replaceAll("^(0+)", "");
                	if (!CollectionUtils.isEmpty(dtlList)) {
                		for (int j = 0; j < dtlList.size(); j++) {
                			if ("1".equals(dtlList.get(j).getUnplanned())) {
            					QueryWrapper<SapBoxNoteDtl> boxEq = new QueryWrapper<SapBoxNoteDtl>().eq("BOX_NOTE", dtlList.get(j).getPackageCode())
            							.eq("SPMS_ITEM_NO", spmsPosnr);
            					SapBoxNoteDtl box = new SapBoxNoteDtl();
            					box.setSapRealityItemNo(objs.getString("ZXD_POSNR"));
            					box.setSapRealitySerialNumber(objs.getString("ZZGLS"));
            					iSapBoxNoteDtlService.update(box, boxEq);
            				} else {
            					if (spmsPosnr.equals(dtlList.get(j).getSpmsItemNo())) {
            						eq.eq("SPMS_ITEM_NO", spmsPosnr);
            						SapPackageListDtl dtl = new SapPackageListDtl();
            						dtl.setSapRealityItemNo(objs.getString("ZXD_POSNR"));
            						dtl.setSapRealitySerialNumber(objs.getString("ZZGLS"));
            						iSapPackageListDtlService.update(dtl, eq);
            					}
            				}
                		}
                	}
        		}
        	}
            iSapDeliveryNoteLogService.insertSapRequestRecord(uuid, userId, WHS_OUT_TO_SAP, null, null);
            return ResponseResult.success()
                    .add("sapReturnedMessage", message)
                    .add("whsOut", this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", uuid)))
                    .add("whsOutDtl", getWhsOutDtl(whsOut.getGoodsType(), uuid))
                    .add("dataToSap", dataToSap);
        } else {
        	throw new Exception("实际装柜结果传输至SAP失败："+message.toJSONString());
        }
    }


	private JSONObject setDataToSap(String uuid, WhsOut whsOut, String deleFlag) {
		JSONObject obj = new JSONObject();
        JSONObject REQUEST = new JSONObject();
        JSONObject ESB_ATTRS = new JSONObject();
        JSONObject REQUEST_DATA = new JSONObject();
        ESB_ATTRS.put("App_ID", "SPMS");
        ESB_ATTRS.put("Target_ID", zxdSapClient);
        ESB_ATTRS.put("Application_ID", "00020000000002");
        ESB_ATTRS.put("Transaction_ID", UUID.randomUUID().toString());
        // 组装头信息
        SapPackageList sapPackageList = iSapPackageListService.getOne(new QueryWrapper<SapPackageList>().eq("PACKAGE_LIST", whsOut.getPackageList()));
        // 查询装箱单下未关闭、未提交SAP成功的出库任务
        QueryWrapper<WhsOut> eq = new QueryWrapper<WhsOut>().eq("PACKAGE_LIST", sapPackageList.getPackageList()).ne("TASK_STATUS", "3").ne("TASK_STATUS", "4");
        List<WhsOut> selectOutList = this.baseMapper.selectList(eq);
        // 组装头信息
        Map<String, Object> structures = new HashMap<>();
        Map<String, Object> head = new HashMap<>();
        head.put("XDNUM", sapPackageList.getPackageList());
        // 撤销此字段不传值，如果此出库任务是这个装箱单最后一个提交SAP或关闭的字段传2，不是最后一个提交或关闭的字段传1
        if ("X".equals(deleFlag)) {
        	head.put("ZGZT", "");
        } else {
        	if (CollectionUtils.isEmpty(selectOutList)) {
        		// 最后提交SAP或关闭
        		head.put("ZGZT", "2");
        	} else {
        		head.put("ZGZT", "1");
        	}
        }
        structures.put("IS_HEAD", head);
        // 组装行信息
        List<Map<String, Object>> items = new ArrayList<>();
        this.setPackItemData(items, sapPackageList, whsOut, deleFlag, selectOutList);
        Map<String, Object> tables = new HashMap<>();
        tables.put("IT_ITEM", items);
        REQUEST_DATA.put("tables", tables);
        REQUEST_DATA.put("structures", structures);
        REQUEST_DATA.put("Operation", "ZZXD_IF_SPMS_08");
        REQUEST_DATA.put("Type", "ZZXD_IF_SPMS_08");
        REQUEST.put("ESB_ATTRS", ESB_ATTRS);
        REQUEST.put("REQUEST_DATA", REQUEST_DATA);
        obj.put("REQUEST", REQUEST);
		return obj;
	}

	private void setPackItemData(List<Map<String, Object>> items, SapPackageList sapPackageList, WhsOut whsOut, String deleFlag, List<WhsOut> selectOutList) {
		// 查询装箱单行项目
		QueryWrapper<SapPackageListDtl> eq = new QueryWrapper<SapPackageListDtl>().eq("PACKAGE_LIST", sapPackageList.getPackageList()).eq("WHS_OUT_UUID", whsOut.getUuid());
		List<SapPackageListDtl> list = iSapPackageListDtlService.list(eq);
		if ("M".equals(whsOut.getGoodsType())) {
			// 贸易货
			for (SapPackageListDtl dtl : list) {
				if (dtl.getActualOperateQty() > 0 && StringUtils.isNotEmpty(dtl.getTempStorage()) && JSONArray.parseArray(dtl.getTempStorage()).size() > 0) {
					Map<String, Object> map = new HashMap<>();
					// 客户端
//				map.put("MANDT", list);
					// 装箱单号
					map.put("XDNUM", dtl.getPackageList());
					// 行项目号：计划外的不传行项目
					if ("1".equals(dtl.getUnplanned())) {
						map.put("POSNR_2", "");
					} else {
						map.put("POSNR_2", dtl.getItem());
					}
					// 实际装柜结果录入表流水号
					map.put("ZZGLS", dtl.getSapRealitySerialNumber());
					// 装柜实际行项目号
					map.put("POSNR", dtl.getSapRealityItemNo());
					// 出货工厂
					map.put("WERKS_PO", dtl.getLoadingWerks());
					// 收货工厂
					map.put("WERKS", dtl.getActualReceivingWerks());
					// 柜型
					map.put("MAGRV", dtl.getCabinetModel());
					// 柜号
					map.put("ZDECABNO", dtl.getCabinetNumber());
					// 柜序号
					map.put("CABIN", dtl.getCabinetSerialNumber());
					// 物料号
					map.put("MATNR", dtl.getMaterialNo());
					// 物料描述
					Map<String, Object> materialMap = this.baseMapper.selectMaterial(dtl.getMaterialNo());
					map.put("MAKTX", materialMap != null && materialMap.get("materialDesc") != null ? materialMap.get("materialDesc").toString() : "");
					// 批次
					map.put("CHARG", dtl.getBatchNo());
					// 箱码
					map.put("BOX_CODE", dtl.getPackageCode());
					// 单位体积
					map.put("PVOLUM", dtl.getCabinetVolume());
					// 体积单位
					map.put("VOLEH", dtl.getVolumeUnit());
					// 单位净重
					map.put("PNTGEW", dtl.getNetWeight());
					// 单位毛重
					map.put("PBRGEW", dtl.getCabinetWeight());
					// 重量单位
					map.put("GEWEI", dtl.getWeightUnit());
					// 最大包装件数
					map.put("CD_QTY", dtl.getPackageQuantityMax());
					// 最大包装件数单位
					map.put("ZBAGUNIT", dtl.getPackageUnitMax());
					// 包装方式
					map.put("BZFS", dtl.getLoadingType());
					// 外箱品牌
					map.put("WX_BRAND", dtl.getContainerBrand());
					// 实际装柜日期
					map.put("AZDAT", whsOut.getActualLoadingDate());
					// 装柜位置
					map.put("ZZGWZ", dtl.getContainerLoadingPosition());
					// 发货主体
					map.put("S_BUKRS", dtl.getShipper());
					// 收货主体
					map.put("KUNNR", dtl.getConsignee());
					// 分公司
					map.put("C_BUKRS", dtl.getBukrs());
					// SPMS行项目号
					map.put("SPMS_POSNR", dtl.getSpmsItemNo());
					// 实际装箱数量
					map.put("AMENG", String.valueOf(dtl.getActualOperateQty()));
					// 基本单位
					map.put("MEINS", dtl.getBasicUnit());
					// 实际装箱件数
					map.put("AEMNG", String.valueOf(dtl.getActualPackingNumber()));
					// 销售单位
					map.put("VRKME", dtl.getPackingUnit());
					// 实际仓库
					map.put("ALGORT", dtl.getLoadingWhsLocationCode());
					// 实际仓库简称
					map.put("ALGOBE", dtl.getLoadingWhsLocationName());
					// 色号
					map.put("COLOR_CODE", dtl.getColorNumber());
					// 封条号
					map.put("SEALN", dtl.getLeadSeal());
					// 备注
					map.put("MARK", dtl.getRemarks());
					// 装柜状态-字段保留不传值2023-09-04 xzp update
					map.put("ZGZT", "");
					// 删除标记
					if ("X".equals(deleFlag)) {
						map.put("DEL_FLAG", "X");
					} else {
						map.put("DEL_FLAG", "");
					}
					items.add(map);
				}
			}
		} else if ("X".equals(whsOut.getGoodsType())) {
			// 备件货
			// 根据箱码分组
			Map<String, List<SapPackageListDtl>> boxNoteMaps = list.stream().collect(Collectors.groupingBy(SapPackageListDtl::getPackageCode));
			for (String boxNote : boxNoteMaps.keySet()) {
				List<SapPackageListDtl> dtlList = boxNoteMaps.get(boxNote);
				Map<String, List<SapPackageListDtl>> materialNoMaps = dtlList.stream().filter(o -> StringUtils.isNotEmpty(o.getMaterialNo())).collect(Collectors.groupingBy(SapPackageListDtl::getMaterialNo));
				if (dtlList.get(0).getActualOperateQty() > 0) {
					// 根据箱码查询箱码信息
					QueryWrapper<SapBoxNoteDtl> boxqw = new QueryWrapper<SapBoxNoteDtl>().eq("BOX_NOTE", boxNote);
					List<SapBoxNoteDtl> boxList = iSapBoxNoteDtlService.list(boxqw);
					for (SapBoxNoteDtl box : boxList) {
						if (box.getDomesticQtyReceived() > 0) {
							Map<String, Object> map = new HashMap<>();
							// 客户端
//				map.put("MANDT", list);
							// 装箱单号
							map.put("XDNUM", sapPackageList.getPackageList());
							// 行项目号：计划外的不传行项目
							if ("1".equals(dtlList.get(0).getUnplanned())) {
								map.put("POSNR_2", "");
							} else {
								map.put("POSNR_2", materialNoMaps != null && materialNoMaps.get(box.getMaterialNo()) != null ? materialNoMaps.get(box.getMaterialNo()).get(0).getItem() : dtlList.get(0).getItem());
							}
							// 实际装柜结果录入表流水号
							map.put("ZZGLS", "1".equals(dtlList.get(0).getUnplanned()) ? box.getSapRealitySerialNumber() : materialNoMaps != null && materialNoMaps.get(box.getMaterialNo()) != null ? materialNoMaps.get(box.getMaterialNo()).get(0).getSapRealitySerialNumber() : dtlList.get(0).getSapRealitySerialNumber());
							// 装柜实际行项目号
							map.put("POSNR", "1".equals(dtlList.get(0).getUnplanned()) ? box.getSapRealityItemNo() : materialNoMaps != null && materialNoMaps.get(box.getMaterialNo()) != null ? materialNoMaps.get(box.getMaterialNo()).get(0).getSapRealityItemNo() : dtlList.get(0).getSapRealityItemNo());
							// 出货工厂
							map.put("WERKS_PO", dtlList.get(0).getLoadingWerks());
							// 收货工厂
							map.put("WERKS", dtlList.get(0).getActualReceivingWerks());
							// 柜型
							map.put("MAGRV", dtlList.get(0).getCabinetModel());
							// 柜号
							map.put("ZDECABNO", dtlList.get(0).getCabinetNumber());
							// 柜序号
							map.put("CABIN", dtlList.get(0).getCabinetSerialNumber());
							// 物料号
							map.put("MATNR", box.getMaterialNo());
							// 物料描述
							Map<String, Object> materialMap = this.baseMapper.selectMaterial(box.getMaterialNo());
							map.put("MAKTX", materialMap != null && materialMap.get("materialDesc") != null ? materialMap.get("materialDesc").toString() : "");
							// 批次
							map.put("CHARG", dtlList.get(0).getBatchNo());
							// 箱码
							map.put("BOX_CODE", dtlList.get(0).getPackageCode());
							// 包装号 
							map.put("PACKAGE_POSNR", box.getItem());
							// 单位体积
							map.put("PVOLUM", materialMap != null && materialMap.get("volumeQty") != null ? materialMap.get("volumeQty").toString() : "");
							// 体积单位
							map.put("VOLEH", materialMap != null && materialMap.get("volumeUnit") != null ? materialMap.get("volumeUnit").toString() : "");
							// 单位净重
							map.put("PNTGEW", materialMap != null && materialMap.get("netWeight") != null ? materialMap.get("netWeight").toString() : "");
							// 单位毛重
							map.put("PBRGEW", materialMap != null && materialMap.get("grossWeight") != null ? materialMap.get("grossWeight").toString() : "");
							// 重量单位
							map.put("GEWEI", materialMap != null && materialMap.get("weightUnit") != null ? materialMap.get("weightUnit").toString() : "");
							// 最大包装件数
							map.put("CD_QTY", box.getPackageQuantityMax());
							// 最大包装件数单位
							map.put("ZBAGUNIT", box.getPackageUnitMax());
							// 包装方式
							map.put("BZFS", dtlList.get(0).getLoadingType());
							// 外箱品牌
							map.put("WX_BRAND", dtlList.get(0).getContainerBrand());
							// 实际装柜日期
							map.put("AZDAT", whsOut.getActualLoadingDate());
							// 装柜位置
							map.put("ZZGWZ", dtlList.get(0).getContainerLoadingPosition());
							// 发货主体
							map.put("S_BUKRS", dtlList.get(0).getShipper());
							// 收货主体
							map.put("KUNNR", dtlList.get(0).getConsignee());
							// 分公司
							map.put("C_BUKRS", dtlList.get(0).getBukrs());
							// SPMS行项目号
							map.put("SPMS_POSNR", "1".equals(dtlList.get(0).getUnplanned()) ? box.getSpmsItemNo() : materialNoMaps != null && materialNoMaps.get(box.getMaterialNo()) != null ? materialNoMaps.get(box.getMaterialNo()).get(0).getSpmsItemNo() : dtlList.get(0).getSpmsItemNo());
							// 实际装箱数量
							map.put("AMENG", String.valueOf(box.getDomesticQtyReceived()));
							// 基本单位
							map.put("MEINS", box.getBasicUnit());
							// 实际装箱件数
							if (materialMap.get("numerator") != null && materialMap.get("denominator") != null) {
								BigDecimal divide = new BigDecimal(String.valueOf(box.getDomesticQtyReceived())).
										multiply(new BigDecimal(materialMap.get("denominator").toString())).divide(new BigDecimal(materialMap.get("numerator").toString()), 2, BigDecimal.ROUND_HALF_UP);
								map.put("AEMNG", divide.stripTrailingZeros().toPlainString());
							} else {
								map.put("AEMNG", String.valueOf(box.getDomesticQtyReceived()));
							}
							// 销售单位
							map.put("VRKME", materialMap != null && materialMap.get("unitSales") != null ? materialMap.get("unitSales").toString() : box.getBasicUnit());
							// 实际仓库
							map.put("ALGORT", dtlList.get(0).getLoadingWhsLocationCode());
							// 实际仓库简称
							map.put("ALGOBE", dtlList.get(0).getLoadingWhsLocationName());
							// 色号
							map.put("COLOR_CODE", dtlList.get(0).getColorNumber());
							// 封条号
							map.put("SEALN", dtlList.get(0).getLeadSeal());
							// 备注
							map.put("MARK", dtlList.get(0).getRemarks());
							// 装柜状态-字段保留不传值2023-09-04 xzp update
							map.put("ZGZT", "");
							// 删除标记
							if ("X".equals(deleFlag)) {
								map.put("DEL_FLAG", "X");
							} else {
								map.put("DEL_FLAG", "");
							}
							items.add(map);
						}
					}
				}
			}
		}
	}

	ResponseResult getDataToSap(String whsOutUuid, String confirmLastSubmission, String goodsType, String packageList) {
        try {
            JSONObject obj = new JSONObject();
            JSONObject REQUEST = new JSONObject();
            JSONObject ESB_ATTRS = new JSONObject();
            JSONObject REQUEST_DATA = new JSONObject();
            ESB_ATTRS.put("App_ID", "SPMS");
            ESB_ATTRS.put("Target_ID", zxdSapClient);
            ESB_ATTRS.put("Application_ID", "00020000000002");
            ESB_ATTRS.put("Transaction_ID", UUID.randomUUID().toString());
            Map<String, Object> structures = new HashMap<>();
            Map<String, Object> IS_HEAD = new HashMap<>();
            // 组装头信息
            SapPackageList sapPackageList = iSapPackageListService.getOne(new QueryWrapper<SapPackageList>().eq("PACKAGE_LIST", packageList));
            this.setPackHeadData(IS_HEAD, sapPackageList);
            structures.put("IS_HEAD", IS_HEAD);
            // 组装行信息
            List<Map<String, Object>> items = new ArrayList<>();
//            this.setPackItemData(items, sapPackageList);
            Map<String, Object> tables = new HashMap<>();
            tables.put("IT_ITEM", items);
            REQUEST_DATA.put("structures", structures);
            REQUEST_DATA.put("tables", tables);
            REQUEST_DATA.put("Operation", "Z_ZXD_IMPROT_AND_CONFIRM");
            REQUEST_DATA.put("Type", "IMPROT_AND_CONFIRM");

            List<Map<String, Object>> getItemDtl = new ArrayList<>();
            if ("M".equals(goodsType)) {
                getItemDtl = this.baseMapper.getItemDtlM(whsOutUuid);
            } else {
                getItemDtl = this.baseMapper.getItemDtlX(whsOutUuid);
            }
            if (getItemDtl.size() == 0) {
                return ResponseResult.error("出库结果提交SAP时，获取出库详情过程出错");
            }
            for (int i = 0; i < getItemDtl.size(); i++) {
                JSONObject tempDoc = new JSONObject();
                tempDoc.put("zxdidEx", getItemDtl.get(i).get("itemNo").toString());
                tempDoc.put("zspmsTaskid", whsOutUuid);
                tempDoc.put("xdnum", getItemDtl.get(i).get("packageList").toString());
                tempDoc.put("cabno", getItemDtl.get(i).get("cabinetNumber").toString());
                tempDoc.put("azdat", getItemDtl.get(i).get("actualLoadingDate").toString().replaceAll("-", ""));
                tempDoc.put("algort", getItemDtl.get(i).get("whsLocationCode").toString());
                tempDoc.put("algobe", getItemDtl.get(i).get("whsLocationDesc").toString());
                tempDoc.put("sealn", getItemDtl.get(i).get("leadSeal") == null ? "" : getItemDtl.get(i).get("leadSeal"));
                tempDoc.put("matnr", getItemDtl.get(i).get("materialNo").toString());
                tempDoc.put("maktx", getItemDtl.get(i).get("materialZhDesc").toString());
                /** vrkme 销售单位；aemng 实际装箱件数；meins 基本计量单位——留空; ameng 实际装箱数量——留空; cdQty 报关件数; */
                tempDoc.put("vrkme", getItemDtl.get(i).get("unitSales").toString());
                tempDoc.put("meins", getItemDtl.get(i).get("basicUnit").toString());
                tempDoc.put("ameng", getItemDtl.get(i).get("actualOperateQty") == null ? "" : getItemDtl.get(i).get("actualOperateQty").toString());
                /** bzgg 包装规格; jyjg 货柜检验结果; kgBrgew 空柜重量（KG）; wxBrand 外箱品牌;  */
                tempDoc.put("bzgg", getItemDtl.get(i).get("packingSpec") == null ? "" : getItemDtl.get(i).get("packingSpec"));
                tempDoc.put("jyjg", getItemDtl.get(i).get("cabinetCheckResult") == null ? "" : getItemDtl.get(i).get("cabinetCheckResult"));
                tempDoc.put("kgBrgew", getItemDtl.get(i).get("emptyCabinetWeight") == null ? "" : getItemDtl.get(i).get("emptyCabinetWeight").toString());
                tempDoc.put("wxBrand", getItemDtl.get(i).get("containerBrand") == null ? "" : getItemDtl.get(i).get("containerBrand"));
                if ("M".equals(goodsType)) {
                    /** aemng 实际装箱件数；cdQty 报关件数； */
                    tempDoc.put("cdQty", null == getItemDtl.get(i).get("customsDeclarationNumber") ? "" : getItemDtl.get(i).get("customsDeclarationNumber").toString());
                    tempDoc.put("aemng", null == getItemDtl.get(i).get("actualPackingNumber") ? "0" : getItemDtl.get(i).get("actualPackingNumber").toString());
                    tempDoc.put("colorCode", getItemDtl.get(i).get("colorNumber") == null ? "" : getItemDtl.get(i).get("colorNumber").toString());
                    tempDoc.put("zh", getItemDtl.get(i).get("thickness") == null ? "" : getItemDtl.get(i).get("thickness").toString());
                    tempDoc.put("cpsjpjBrgew", getItemDtl.get(i).get("actualAvgGrossWeight") == null ? "" : getItemDtl.get(i).get("actualAvgGrossWeight").toString());
                } else {
                    /** 备件货实际装箱件数，由国内实际数量字段、销售单位和基本单位换算分子分母关系，计算得出保留三位小数；cdQty 报关件数备件货传空； */
                    double actualOperateQty = getItemDtl.get(i).get("actualOperateQty") == null ? 0 : Double.parseDouble(getItemDtl.get(i).get("actualOperateQty").toString());
                    double numerator = Double.parseDouble(getItemDtl.get(i).get("numerator").toString());
                    double denominator = Double.parseDouble(getItemDtl.get(i).get("denominator").toString());
                    String actualPackingNumber = String.format("%.3f", (denominator / numerator) * actualOperateQty);
                    tempDoc.put("aemng", actualPackingNumber);
                    tempDoc.put("cdQty", "");
                    tempDoc.put("colorCode", "");
                    tempDoc.put("zh", "");
                    tempDoc.put("cpsjpjBrgew", "");
                    /** 备件货国内出库结果回传SAP信息结构带上箱码、箱码行号 —— 20211116 */
                    tempDoc.put("serial", getItemDtl.get(i).get("boxNoteItem").toString());
                    tempDoc.put("boxCode", getItemDtl.get(i).get("boxNote").toString());
                }
                tempDoc.put("jzry", getItemDtl.get(i).get("monitorLoading") == null ? "" : getItemDtl.get(i).get("monitorLoading"));
                tempDoc.put("zgfs", getItemDtl.get(i).get("loadingType") == null ? "" : getItemDtl.get(i).get("loadingType"));
                tempDoc.put("mark", getItemDtl.get(i).get("remarks") == null ? "" : getItemDtl.get(i).get("remarks"));
                tempDoc.put("pkmakYn", "N");
                tempDoc.put("del", "");
                tempDoc.put("importFlag", "A");

//                PT_INPUT_LIST.add(tempDoc);
            }
//            MapItems.put("PT_INPUT_LIST", PT_INPUT_LIST);
//            if ("1".equals(confirmLastSubmission)) {
//                JSONObject tempDoc = new JSONObject();
//                tempDoc.put("xdnum", packageList);
//                tempDoc.put("xzhj", "2");
//                tempDoc.put("curtu", "X");
//                PT_STATE.add(tempDoc);
//                MapItems.put("PT_STATE", PT_STATE);
//                PT_CONFIRM = this.baseMapper.getPackageListBoxNote(packageList);
//                MapItems.put("PT_CONFIRM", PT_CONFIRM);
//            }
            REQUEST.put("ESB_ATTRS", ESB_ATTRS);
            REQUEST.put("REQUEST_DATA", REQUEST_DATA);

            obj.put("REQUEST", REQUEST);
            return ResponseResult.success().add("obj", obj);
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseResult.error().add("error", e.getMessage());
        }
    }

    private void setPackHeadData(Map<String, Object> iS_HEAD, SapPackageList sapPackageList) {
		// 装箱单
    	iS_HEAD.put("XDNUM", sapPackageList.getPackageList());
    	// 是否杂货
    	iS_HEAD.put("ZHBS", sapPackageList.getSpareParts());
    	// 起运港
    	iS_HEAD.put("SPORT", sapPackageList.getFromPort());
    	// 起运港描述
    	iS_HEAD.put("BEZEI_S", sapPackageList.getFromPortName());
    	// 目的港
    	iS_HEAD.put("EPORT", sapPackageList.getToPort());
    	// 目的港描述
    	iS_HEAD.put("BEZEI_E", sapPackageList.getToPortName());
    	// 装柜员
    	iS_HEAD.put("MERCH", sapPackageList.getLoader());
    	// 计划员
    	iS_HEAD.put("CRUSR", sapPackageList.getPlanner());
    	// SAP用户名称
    	iS_HEAD.put("BNAME", sapPackageList.getPlannerName());
    	// 计划柜量
    	iS_HEAD.put("PCONT", sapPackageList.getPlannedCabinetsQty());
    	// 单据状态
    	iS_HEAD.put("STATUS", sapPackageList.getSpmsStatus());
	}

	/**
     * 备件货出库任务详情保存:
     * 1.检验出库任务表头当前状态、目标状态状态是否可更新；
     * 2.删除下架任务详情，重新建下架任务详情；表头不更新；记录下架任务操作日志；
     * 3.出库任务详情——SAP_PACKAGE_BOX 删除计划外行数，更新计划内行数据；
     * 4.更新出库任务表头状态及其他字段，记录表头更新操作日志；
     *
     * @param doc
     * @param userId
     * @return
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResponseResult saveWhsOutX(JSONObject doc, String userId) throws Exception{
        ResponseResult checkUpdateWhsOutXValidate = checkUpdateWhsOutX(doc);
        if (checkUpdateWhsOutXValidate.getCode() != 200) {
            return checkUpdateWhsOutXValidate;
        }
        int result = updateWhsOutAndDtl(doc, userId);
        if (result < 0) {
        	throw new Exception("出库任务更新过程执行异常 updateWhsOutAndDtl ");
        }
        int resp = updateShelfOnTaskAndDtl(doc, userId);
        if (resp < 0) {
            throw new Exception("下架任务更新过程执行异常 updateShelfOnTaskAndDtl");
        }
        return ResponseResult.success();
    }

    ResponseResult checkUpdateWhsOutX(JSONObject doc) {
        try {
            JSONObject whsOutObject = doc.getJSONObject("whsOut");
            String whsOutUuid = whsOutObject.getString("uuid");
            String currentStatus = whsOutObject.getString("currentStatus");
            WhsOut whsOut = this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
            if (whsOut == null) {
                return ResponseResult.error("出库任务不存在");
            }
            if (!currentStatus.equals(whsOut.getTaskStatus())) {
                return ResponseResult.error("出库任务状态已更新请刷新后重试");
            }
            return ResponseResult.success();
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseResult.error().add("error", e.getMessage());
        }
    }

    int updateShelfOnTaskAndDtl(JSONObject doc, String userId) {
        try {
            JSONObject shelfOnTaskObject = doc.getJSONObject("shelfOnTask");
            JSONArray shelfOnTaskDtlArray = doc.getJSONArray("shelfOnTaskDtls");
            String shelfOnTaskUuid = shelfOnTaskObject.getString("uuid");
        	// 查询上下架头信息
//        	ShelfOnTask one = iShelfOnTaskService.getOne(new QueryWrapper<ShelfOnTask>().eq("UUID", shelfOnTaskUuid));
        	JSONObject whsOutObject = doc.getJSONObject("whsOut");
            String whsOutUuid = whsOutObject.getString("uuid");
            WhsOut whsOut = this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
        	if (StringUtils.isEmpty(shelfOnTaskUuid) && "2".equals(whsOut.getDataSource())) {
        		// 如果上下架数据为空，并且出库任务是手动创建的，需要初始化上下架任务
        		iShelfOnTaskService.genShelfOnTask(userId, DateFormatUtils.format(new Date(), "yyyyMMdd"), whsOut);
        	} else {
        		if (shelfOnTaskUuid != null) {
            		iShelfOnTaskService.deleteShelfOnTaskDtl(shelfOnTaskUuid);
            	}
            	if (shelfOnTaskDtlArray.size() > 0) {
            		for (int i = 0; i < shelfOnTaskDtlArray.size(); i++) {
            			JSONObject dtlObject = shelfOnTaskDtlArray.getJSONObject(i);
            			ShelfOnTaskDtl shelfOnTaskDtl = new ShelfOnTaskDtl();
            			double actualOperateQty = StringUtils.isEmpty(dtlObject.getString("actualOperateQty")) ?
            					0 : dtlObject.getDouble("actualOperateQty");
            			shelfOnTaskDtl.setUuid(shelfOnTaskUuid);
            			shelfOnTaskDtl.setTaskRowId(dtlObject.getString("taskRowId"));
            			shelfOnTaskDtl.setBoxNote(dtlObject.getString("boxNote"));
            			shelfOnTaskDtl.setQuantity(dtlObject.getDouble("quantity"));
            			shelfOnTaskDtl.setTargetStorage(dtlObject.getString("targetStorage"));
            			shelfOnTaskDtl.setActualOperateQty(actualOperateQty);
            			shelfOnTaskDtl.setUnplanned(dtlObject.getString("unplanned"));
            			/** 新建下架任务详情 */
            			iShelfOnTaskService.insertShelfOnTaskDtl(shelfOnTaskDtl);
            		}
            	}
            	iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(shelfOnTaskUuid, userId, UPDATE_DEDUCT_SHELF_TASK);
        	}
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    int updateWhsOutAndDtl(JSONObject doc, String userId) {
        try {
            JSONObject whsOutObject = doc.getJSONObject("whsOut");
            JSONArray whsOutDtlArray = doc.getJSONArray("whsOutDtl");
            String whsOutUuid = whsOutObject.getString("uuid");
            String packageList = whsOutObject.getString("packageList");
            iSapPackageListDtlService.deleteUnplannedSapPackageListDtl(whsOutUuid, "1");
            for (int i = 0; i < whsOutDtlArray.size(); i++) {
                JSONObject whsOutDtl = whsOutDtlArray.getJSONObject(i);
                SapPackageListDtl sapPackageListDtl = new SapPackageListDtl();
                double actualOperateQty = StringUtils.isEmpty(whsOutDtl.getString("actualOperateQty")) ? 0 : whsOutDtl.getDouble("actualOperateQty");
                // 行项目只有待完成
                sapPackageListDtl.setSpmsStatus("1");
                sapPackageListDtl.setUnplanned(whsOutDtl.getString("unplanned"));
                sapPackageListDtl.setActualOperateQty(actualOperateQty);
                sapPackageListDtl.setPackageCode(whsOutDtl.getString("boxNote"));
                sapPackageListDtl.setBukrs(whsOutDtl.getString("bukrs"));
                sapPackageListDtl.setReceivingWerks(whsOutDtl.getString("receivingWerks"));
                sapPackageListDtl.setConsignee(whsOutDtl.getString("consignee"));
                sapPackageListDtl.setCabinetModel(StringUtils.isNotEmpty(whsOutDtl.getString("cabinetModel")) ? whsOutDtl.getString("cabinetModel") : whsOutObject.getString("cabinetModel"));
                sapPackageListDtl.setActualReceivingWerks(StringUtils.isNotEmpty(whsOutDtl.getString("actualReceivingWerks")) ? whsOutDtl.getString("actualReceivingWerks") : whsOutDtl.getString("receivingWerks"));
                sapPackageListDtl.setCabinetSerialNumber(whsOutObject.getString("cabinetSerialNumber"));
                sapPackageListDtl.setCabinetWeight(StringUtils.isNotEmpty(whsOutDtl.getString("cabinetWeight")) ? Double.valueOf(whsOutDtl.getString("cabinetWeight")) : 0);
                sapPackageListDtl.setCabinetVolume(StringUtils.isNotEmpty(whsOutDtl.getString("cabinetVolume")) ? Double.valueOf(whsOutDtl.getString("cabinetVolume")) : 0);
//                sapPackageListDtl.setTempStorage(storageUuidList);
                sapPackageListDtl.setWeightUnit(whsOutDtl.getString("cabinetWeightUnit"));
                sapPackageListDtl.setPackingUnit(whsOutDtl.getString("packingUnit"));
                sapPackageListDtl.setVolumeUnit(whsOutDtl.getString("volumeUnit"));
                sapPackageListDtl.setNetWeight(StringUtils.isNotEmpty(whsOutDtl.getString("netWeight")) ? Double.valueOf(whsOutDtl.getString("netWeight")) : 0);
                sapPackageListDtl.setContainerBrand(whsOutDtl.getString("containerBrand"));
                sapPackageListDtl.setColorNumber(whsOutDtl.getString("colorNumber"));
                sapPackageListDtl.setThickness(whsOutDtl.getString("thickness"));
                sapPackageListDtl.setActualAvgGrossWeight(whsOutDtl.getString("actualAvgGrossWeight"));
                sapPackageListDtl.setActualPackingNumber(StringUtils.isEmpty(whsOutDtl.getString("actualPackingNumber")) ? 0 : whsOutDtl.getDouble("actualPackingNumber"));
                sapPackageListDtl.setCustomsDeclarationNumber(StringUtils.isEmpty(whsOutDtl.getString("customsDeclarationNumber")) ? 0 : whsOutDtl.getDouble("customsDeclarationNumber"));
                sapPackageListDtl.setPackingSpec(whsOutDtl.getString("packingSpec"));
                sapPackageListDtl.setLoadingType(whsOutDtl.getString("loadingType"));
                sapPackageListDtl.setLoadingWerks(whsOutObject.getString("werks"));
                sapPackageListDtl.setLoadingWhsLocationCode(whsOutObject.getString("whsLocationCode"));
                sapPackageListDtl.setBatchNo(whsOutDtl.getString("batchNo"));
                sapPackageListDtl.setPackageQuantityMax(whsOutDtl.getString("packageQuantityMax"));
                sapPackageListDtl.setPackageUnitMax(whsOutDtl.getString("packageUnitMax"));
                sapPackageListDtl.setContainerLoadingPosition(whsOutDtl.getString("containerLoadingPosition"));
                // 柜号、铅封号取头的值
                sapPackageListDtl.setCabinetNumber(whsOutObject.getString("cabinetNumber"));
                sapPackageListDtl.setLeadSeal(whsOutObject.getString("leadSeal"));
                sapPackageListDtl.setRemarks(whsOutDtl.getString("remarks"));
                sapPackageListDtl.setShipmentCategory(whsOutDtl.getString("shipmentCategory"));
                sapPackageListDtl.setShipper(whsOutDtl.getString("shipper"));
                sapPackageListDtl.setPackageList(packageList);
                /** 计划外的行需新增数据到装箱单明细中；计划内的贸易货出库，按 装箱单号 + 行号 更新出库明细实际出库数量、出库储位料号数量等信息。已有的行，item不会为空。 */
                if ("1".equals(whsOutDtl.getString("unplanned"))) {
                    sapPackageListDtl.setWhsOutUuid(whsOutUuid);
                    sapPackageListDtl.setMaterialNo(whsOutDtl.getString("materialNo"));
                    sapPackageListDtl.setPlannedPackingNumber(StringUtils.isNotEmpty(whsOutDtl.getString("plannedPackingNumber")) ? Double.valueOf(whsOutDtl.getString("plannedPackingNumber")) : 0);
                    sapPackageListDtl.setPlannedPackingQty(StringUtils.isNotEmpty(whsOutDtl.getString("plannedPackingQty")) ? Double.valueOf(whsOutDtl.getString("plannedPackingQty")) : 0);
                    sapPackageListDtl.setGoodsType("X");
                    int maxNo = iSapPackageListDtlService.selectMaxSpmsItemNo(packageList);
                    if (actualOperateQty > 0) {
                    	QueryWrapper<SapBoxNoteDtl> boxEq = new QueryWrapper<SapBoxNoteDtl>().eq("BOX_NOTE", whsOutDtl.getString("boxNote"));
                    	List<SapBoxNoteDtl> boxList = iSapBoxNoteDtlService.list(boxEq);
                    	for (SapBoxNoteDtl box : boxList) {
                    		++maxNo;
                    		boxEq.eq("ITEM", box.getItem()).eq("PACKAGE_POSNR", box.getPackagePosnr()).eq("MATERIAL_NO", box.getMaterialNo());
                    		box.setSpmsItemNo(String.valueOf(maxNo));
                    		iSapBoxNoteDtlService.update(box, boxEq);
                    	}
                    	sapPackageListDtl.setSpmsItemNo(String.valueOf(maxNo));
                    } else {
                    	sapPackageListDtl.setSpmsItemNo(String.valueOf(maxNo+1));
                    }
                    iSapPackageListDtlService.insertSapPackageListDtl(sapPackageListDtl);
                } else {
                	QueryWrapper<SapPackageListDtl> queryWrapper = new QueryWrapper<SapPackageListDtl>().eq("PACKAGE_LIST", packageList).eq("PACKAGE_CODE", whsOutDtl.getString("boxNote")).eq("WHS_OUT_UUID", whsOutUuid);
                	iSapPackageListDtlService.update(sapPackageListDtl, queryWrapper);
                }
            }
            
            WhsOut whsOut = new WhsOut();
            whsOut.setUuid(whsOutUuid);
            whsOut.setTaskStatus(whsOutObject.getString("targetStatus"));
            whsOut.setCabinetNumber(whsOutObject.getString("cabinetNumber"));
            whsOut.setActualLoadingDate(whsOutObject.getString("actualLoadingDate"));
            whsOut.setMonitorLoading(whsOutObject.getString("monitorLoading"));
            whsOut.setLoadingType(whsOutObject.getString("loadingType"));
            whsOut.setRemarks(whsOutObject.getString("remarks"));
            whsOut.setCabinetCheckResult(whsOutObject.getString("cabinetCheckResult"));
            whsOut.setLeadSeal(whsOutObject.getString("leadSeal"));
            if(StringUtils.isNotEmpty(whsOutObject.getString("operateUser"))){
                whsOut.setOperateUser(whsOutObject.getString("operateUser"));
            }
            /** 更新出库任务表头 */
            this.baseMapper.update(whsOut, new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
            iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(whsOutUuid, userId, WHS_OUT_TASK_UPDATE);
            // 计算最大包装件数
            // 查询箱码跟物料关系数据
            List<Map<String, Object>> boxNoteAndMaterials = this.baseMapper.selectBoxNoteAndMaterial(whsOutUuid);
            // 备件货：统计在一个出库任务中一个物料存在箱码的数量
			Map<String, List<Map<String, Object>>> materialNoMaps = boxNoteAndMaterials.stream().collect(Collectors.groupingBy(o -> o.get("materialNo").toString()));
			for (String materialNo : materialNoMaps.keySet()) {
				List<Map<String, Object>> materialList = materialNoMaps.get(materialNo);
				// 将最大包装件数更新到装箱单行中
				for (Map<String, Object> map : materialList) {
					if (map.get("boxNote") == null) {
						continue;
					}
					QueryWrapper<SapBoxNoteDtl> eq = new QueryWrapper<SapBoxNoteDtl>().eq("BOX_NOTE", map.get("boxNote").toString())
							.eq("MATERIAL_NO", map.get("materialNo").toString());
					SapBoxNoteDtl dtl = new SapBoxNoteDtl();
					dtl.setPackageQuantityMax(String.valueOf(materialList.size()));
					dtl.setPackageUnitMax("PCS");
					iSapBoxNoteDtlService.update(dtl, eq);
				}
			}
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
            return -1;
        }
    }

    int updateSapPackageList(String userId, String packageList, String targetStatus) {

        this.baseMapper.updateSapPackageList(packageList, targetStatus);
        if ("1".equals(targetStatus)) {
            iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(packageList, userId, UPDATE_SAP_PACKAGE_Withdraw);
        } else if ("2".equals(targetStatus)) {
            iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(packageList, userId, CLOSE_SAP_PACKAGE_LIST);
        }

        return 1;
    }

    @Override
    public ResponseResult getWhsOutDtlList(String packageList, String cabinetSerialNumber, String werks, String whsLocationCode) {
        try {
            if (StringUtils.isEmpty(packageList) || StringUtils.isEmpty(werks) || StringUtils.isEmpty(whsLocationCode)) {
                return ResponseResult.error("请求参数错误请重试");
            }
            if (StringUtils.isEmpty(cabinetSerialNumber)) {
                cabinetSerialNumber = null;
            }
            return ResponseResult.success().add("whsOutDtlList", this.baseMapper.getWhsOutDtlList(packageList, cabinetSerialNumber, werks, whsLocationCode));
        } catch (Exception e) {
            e.printStackTrace();
            return ResponseResult.error().add("error", e.getMessage());
        }
    }

    @Override
    public ShelfOnTask getShelfOnTaskByRelateDocNumber(String whsOutUuid, String taskType) {
        // TASK_TYPE = 1 上架任务; TASK_TYPE = 2 下架任务;
        return iShelfOnTaskService.getByRelateDocNumber(whsOutUuid, taskType);
    }

    @Override
    public List<ShelfOnTaskDtl> getShelfOnTaskDtlByRelateDocNumber(String shelfOnTaskUuid) {
        return iShelfOnTaskService.getDtlByUuid(shelfOnTaskUuid);
    }

    /** 装箱单出库结果删除接口 */
    @Override
    public ResponseResult whsOutCancel(String packageList, String whsOutUuid, String userId){
        try {
            JSONObject dataToSap = new JSONObject();
            JSONObject REQUEST = new JSONObject();
            JSONObject ESB_ATTRS = new JSONObject();
            JSONObject REQUEST_DATA = new JSONObject();
            JSONObject Head = new JSONObject();

            ESB_ATTRS.put("App_ID", "SPMS");
            ESB_ATTRS.put("Application_ID", "00020000000002");
            ESB_ATTRS.put("Transaction_ID", UUID.randomUUID().toString());

            Head.put("imXdnum", packageList);
            Head.put("imZspmsTaskid", whsOutUuid);
            Head.put("imImportFlag", "A");

            REQUEST_DATA.put("Head", Head);
            REQUEST_DATA.put("Operation", "Z_ZXD_DEL");
            REQUEST_DATA.put("Type", "ZXD_DEL");

            REQUEST.put("ESB_ATTRS", ESB_ATTRS);
            REQUEST.put("REQUEST_DATA", REQUEST_DATA);
            dataToSap.put("REQUEST", REQUEST);
            JSONObject message = JSONObject.parseObject(iFeignServiceCommon.sendRestToSapCommon((dataToSap)));
            System.out.println("\n装箱单出库结果删除接口\ndataToSap ====== " + dataToSap + "\nmessage ====== " + message);
            if (message.containsKey("RESPONSE") && message.getJSONObject("RESPONSE").containsKey("RETURN_DATA")
                    && "S".equals(message.getJSONObject("RESPONSE").getJSONObject("RETURN_DATA").getString("EX_TYPE"))){
                iSapDeliveryNoteLogService.insertSapRequestRecord(whsOutUuid, userId, WHS_OUT_CANCEL, message.toJSONString(), dataToSap.toJSONString());
                return ResponseResult.success().add("sendSapReturnedMessage", message).add("dataToSap", dataToSap);
            }
            iSapDeliveryNoteLogService.insertSapRequestRecord(whsOutUuid, userId, WHS_OUT_CANCEL_ERROR, message.toJSONString(), dataToSap.toJSONString());
            return ResponseResult.error().add("sendSapReturnedMessage", message).add("dataToSap", dataToSap);
        }catch (Exception e){
            e.printStackTrace();
            return ResponseResult.error().add("error", e.getMessage());
        }
    }

    /**
     出库任务状态:0待执行，1执行中，2出库完成，3任务关闭，4提交SAP成功
     出库任务关闭操作前，执行出库任务数据是否能被操作校验：
       1.判断该出库任务状态是否能被关闭；出库任务状态为 0，1 的才能被关闭；
       2.判断该出库任务是否有关联的 未完成的上下架任务，如果有则返回错误信息；
       3.更新前判断是否是同装箱单下最后一个未完成的出库任务：
       3.1.如果不是则直接将出库任务状态更新为任务关闭；记录日志信息；
       3.2.如果是则将 "此装箱单是最后一次提交SAP" 的信息告知SAP；连同该装箱单下所有备件箱码及明细告知SAP；请求SAP成功
     则更新该出库任务状态关闭，记录该出库任务提交SAP日志和任务关闭日志，更新装箱单状态完成，记录装箱单更新日志；
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public synchronized ResponseResult whsOutClose(String whsOutUuid, String userId, String remarks)throws Exception {
        ResponseResult checkResult = checkDataValidate(whsOutUuid);
        if (checkResult.getCode() != 200){
            return checkResult;
        }
        // 关闭上下架任务
		List<ShelfOnTask> shelfOnTaskList = iShelfOnTaskService.getUnClosedTasks(whsOutUuid);
		if (shelfOnTaskList != null && shelfOnTaskList.size() > 0) {
			for (ShelfOnTask shelf : shelfOnTaskList) {
				iShelfOnTaskService.updateShelfOnTaskStatus(shelf.getUuid(), "3");
			}
		}
        
//        String confirmLastSubmission = "0";
        /** 获取出库任务同一个装箱单下所有  未成功提交SAP  的出库任务数量，来判断当前任务是否是最后一个提交。最后一个提交会多出 PT_STATE 一些字段； */
        /** 20210713 修改最后一次提交判断逻辑：同一个装箱单下的出库任务，状态不为 3（任务关闭），不为4（提交SAP成功）的出库任务数量如果为 1，则表示最后一次提交
         * 如果关闭的任务是同装箱单下最后一个关闭的出库任务，则需要提交相关信息给到SAP告知是最后一次提交；记录日志；
         * 如果关闭任务不是同装箱单下最后一个关闭的出库任务，则仅在SPMS关闭任务，记录日志即可；
         * 
         * 2022-11-22 xzp update
         * 如果前面的出库任务跟当前任务都是关闭，则不需要调用接口通知SAP，并且将关联的装箱单状态改为已关闭；
         * 如果前面的出库任务都是提交SAP成功的并且当前是最后一个关闭任务的，则需要调用接口通知SAP，并且将关联的装箱单状态改为已完成；
         * */
        WhsOut whsOut = this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
        // 更新出库任务为关闭状态
		WhsOut updateWhsOut = new WhsOut();
		updateWhsOut.setTaskStatus("3");
		updateWhsOut.setRemarks(remarks);
		this.baseMapper.update(updateWhsOut, new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
		// 查询关联装箱单下所有的出库任务数、已关闭任务数、提交SAP成功的任务数
		int allCount = this.baseMapper.getWhsOutAllStatusCount(whsOutUuid);
		int sapCount = this.baseMapper.getWhsOutSapStatusCount(whsOutUuid);
		int closeCount = this.baseMapper.getWhsOutCloseStatusCount(whsOutUuid);
		if (sapCount > 0 && (sapCount + closeCount == allCount)) {
			// 已完成
			updatePLTaskStatus(whsOut.getPackageList(), "2", userId);
		} else if (allCount == closeCount) {
			// 同装箱单下最后一个关闭的出库任务，需更新装箱单状态为关闭-3
			updatePLTaskStatus(whsOut.getPackageList(), "3", userId);
			return ResponseResult.success().add("msg", "同装箱单下最后一次关闭的出库任务");
		}
		// 更新装箱单行项目状态为已关闭
        iSapPackageListDtlService.uSapPackageListDtl("3", whsOutUuid);
		iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(whsOutUuid, userId, WHS_OUT_CLOSE);
		// 如果这个出库任务是这个装箱单下所有出库任务最后关闭的，需要同步SAP
		if (sapCount + closeCount == allCount || allCount == closeCount) {
			// 设置推送SAP数据
	        JSONObject toSapData = this.setDataToSap(whsOutUuid, whsOut, null);
	        JSONObject dataToSap = JSONObject.parseObject(toSapData.toString());
	        JSONObject message = JSONObject.parseObject(iFeignServiceCommon.sendRestToSapCommon((dataToSap)));
	        // 保存接口日志
	        iSysLogMqService.addSysLogMq(SEND_MSG, "ZZXD_IF_SPMS_08", "实际装柜结果传输至SAP接口", "ZZXD_IF_SPMS_08", dataToSap.toJSONString(), message.toString(), null);
	        JSONObject RETURN_DATA = message.getJSONObject("RESPONSE").getJSONObject("RETURN_DATA");
	        JSONArray ET_RET = RETURN_DATA.getJSONArray("ET_RET");
	        if (ET_RET.size() > 0 && "S".equals(ET_RET.getJSONObject(0).getString("TYPE"))) {
	            // SAP会返回装柜实际行项目，需要进行更新
	            for (int i = 0; i < ET_RET.size(); i++) {
	            	JSONObject objs = ET_RET.getJSONObject(i);
	            	// 装箱单号
	            	String xdnum = objs.getString("XDNUM");
	            	// 装箱单行号
	            	String posnr_2 = objs.getString("POSNR_2").replaceAll("^(0+)", "");
	            	// SPMS行项目号
	            	String spmsPosnr = objs.getString("SPMS_POSNR").replaceAll("^(0+)", "");
	            	QueryWrapper<SapPackageListDtl> eq = new QueryWrapper<SapPackageListDtl>().eq("PACKAGE_LIST", whsOut.getPackageList())
	            			.eq("WHS_OUT_UUID", whsOut.getUuid()).eq("ITEM", posnr_2);
	            	List<SapPackageListDtl> dtlList = iSapPackageListDtlService.list(eq);
	            	if (!CollectionUtils.isEmpty(dtlList)) {
	            		for (int j = 0; j < dtlList.size(); j++) {
	            			if ("M".equals(dtlList.get(j).getGoodsType())) {
	            				if (spmsPosnr.equals(dtlList.get(j).getSpmsItemNo())) {
	            					eq.eq("SPMS_ITEM_NO", spmsPosnr);
	            					SapPackageListDtl dtl = new SapPackageListDtl();
	            					dtl.setSapRealityItemNo(objs.getString("ZXD_POSNR"));
	            					dtl.setSapRealitySerialNumber(objs.getString("ZZGLS"));
	            					iSapPackageListDtlService.update(dtl, eq);
	            				}
	            			} else {
	            				if ("1".equals(dtlList.get(j).getUnplanned())) {
	            					QueryWrapper<SapBoxNoteDtl> boxEq = new QueryWrapper<SapBoxNoteDtl>().eq("BOX_NOTE", dtlList.get(j).getPackageCode())
	            							.eq("SPMS_ITEM_NO", spmsPosnr);
	            					SapBoxNoteDtl box = new SapBoxNoteDtl();
	            					box.setSapRealityItemNo(objs.getString("ZXD_POSNR"));
	            					box.setSapRealitySerialNumber(objs.getString("ZZGLS"));
	            					iSapBoxNoteDtlService.update(box, boxEq);
	            				} else {
	            					if (spmsPosnr.equals(dtlList.get(j).getSpmsItemNo())) {
	            						eq.eq("SPMS_ITEM_NO", spmsPosnr);
	            						SapPackageListDtl dtl = new SapPackageListDtl();
	            						dtl.setSapRealityItemNo(objs.getString("ZXD_POSNR"));
	            						dtl.setSapRealitySerialNumber(objs.getString("ZZGLS"));
	            						iSapPackageListDtlService.update(dtl, eq);
	            					}
	            				}
	            			}
	            		}
	            	}
	            }
	            iSapDeliveryNoteLogService.insertSapRequestRecord(whsOutUuid, userId, WHS_OUT_TO_SAP, message.toJSONString(), dataToSap.toJSONString());
	            return ResponseResult.success()
	                    .add("sapReturnedMessage", message)
	                    .add("whsOut", this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid)))
	                    .add("whsOutDtl", getWhsOutDtl(whsOut.getGoodsType(), whsOutUuid))
	                    .add("dataToSap", dataToSap);
	        } else {
	        	throw new Exception("实际装柜结果传输至SAP失败："+message.toJSONString());
	        }
		}
		return ResponseResult.success().add("msg", "出库任务关闭成功");
    }


    ResponseResult checkDataValidate(String whsOutUuid){
        if (StringUtils.isEmpty(whsOutUuid)){
            return ResponseResult.error("请求参数错误");
        }
        WhsOut whsOut = this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
        if (null == whsOut){
            return ResponseResult.error("获取出库任务失败");
        }
        String taskStatus = whsOut.getTaskStatus();
        if (!"0".equals(taskStatus) && !"1".equals(taskStatus)){
            return ResponseResult.error("任务当前状态不可关闭");
        }
//        List<ShelfOnTask> shelfOnTaskList = iShelfOnTaskService.getUnClosedTasks(whsOutUuid);
//        if (shelfOnTaskList != null && shelfOnTaskList.size() > 0){
//            return ResponseResult.error("此出库任务存在未完成的下架或上架任务，请先完成关联的上下架任务");
//        }
        return ResponseResult.success();
    }

    JSONObject whsOutCloseToSap(String packageList){
        JSONObject obj = new JSONObject();
        JSONObject REQUEST = new JSONObject();
        JSONObject ESB_ATTRS = new JSONObject();
        JSONObject REQUEST_DATA = new JSONObject();
        JSONObject Head = new JSONObject();
        JSONObject MapItems = new JSONObject();
        JSONArray PT_INPUT_LIST = new JSONArray();
        JSONArray PT_STATE = new JSONArray();
        List<Map<String, Object>> PT_CONFIRM = new ArrayList<>();

        ESB_ATTRS.put("App_ID", "SPMS");
        ESB_ATTRS.put("Application_ID", "00020000000002");
        ESB_ATTRS.put("Transaction_ID", UUID.randomUUID().toString());

        Head.put("imImportFlag", "A");

        REQUEST_DATA.put("Head", Head);
        REQUEST_DATA.put("Operation", "Z_ZXD_IMPROT_AND_CONFIRM");
        REQUEST_DATA.put("Type", "IMPROT_AND_CONFIRM");

        MapItems.put("PT_INPUT_LIST", PT_INPUT_LIST);
        REQUEST_DATA.put("MapItems", MapItems);
        JSONObject tempDoc = new JSONObject();
        tempDoc.put("xdnum", packageList);
        tempDoc.put("xzhj", "2");
        tempDoc.put("curtu", "X");
        PT_STATE.add(tempDoc);
        MapItems.put("PT_STATE", PT_STATE);
        PT_CONFIRM = this.baseMapper.getPackageListBoxNote(packageList);
        MapItems.put("PT_CONFIRM", PT_CONFIRM);

        REQUEST.put("ESB_ATTRS", ESB_ATTRS);
        REQUEST.put("REQUEST_DATA", REQUEST_DATA);

        obj.put("REQUEST", REQUEST);
        return obj;
    }

    void updatePLTaskStatus(String packageList, String packageListTaskStatus, String userId){
        this.baseMapper.updatePLTaskStatus(packageList, packageListTaskStatus);
        iSapDeliveryNoteLogService.insertSapRequestRecord(packageList, userId, CLOSE_SAP_PACKAGE_LIST, "", "");
    }

	@Override
	@Transactional
	public ResponseResult whsOutOpen(String whsOutUuid, String userId, String remarks) {
		if (StringUtils.isEmpty(whsOutUuid)){
            return ResponseResult.error("请求参数错误");
        }
        WhsOut whsOut = this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
        if (null == whsOut){
            return ResponseResult.error("获取出库任务失败");
        }
        String taskStatus = whsOut.getTaskStatus();
        if (!"3".equals(taskStatus)){
            return ResponseResult.error("任务当前状态不可打开");
        }
//        List<ShelfOnTask> shelfOnTaskList = iShelfOnTaskService.getUnClosedTasks(whsOutUuid);
//        if (shelfOnTaskList != null && shelfOnTaskList.size() > 0){
//            return ResponseResult.error("此出库任务存在未完成的下架或上架任务，请先完成关联的上下架任务");
//        }
        // 更新出库任务为待处理
		WhsOut updateWhsOut = new WhsOut();
		updateWhsOut.setTaskStatus("0");
		updateWhsOut.setRemarks(remarks);
		this.baseMapper.update(updateWhsOut, new QueryWrapper<WhsOut>().eq("UUID", whsOutUuid));
		// 查询关联装箱单下所有的出库任务数、已关闭任务数、提交SAP成功的任务数
		int allCount = this.baseMapper.getWhsOutAllStatusCount(whsOutUuid);
		int sapCount = this.baseMapper.getWhsOutSapStatusCount(whsOutUuid);
		int closeCount = this.baseMapper.getWhsOutCloseStatusCount(whsOutUuid);
		if (sapCount > 0 && (sapCount + closeCount < allCount)) {
			// 处理中
			updatePLTaskStatus(whsOut.getPackageList(), "1", userId);
		} else {
			// 待处理
			updatePLTaskStatus(whsOut.getPackageList(), "1", userId);
			return ResponseResult.success().add("msg", "出库任务打开成功");
		}
		// 更新装箱单行项目状态为待完成
        iSapPackageListDtlService.uSapPackageListDtl("1", whsOutUuid);
		iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(whsOutUuid, userId, WHS_OUT_CLOSE);
		return ResponseResult.success().add("msg", "出库任务打开成功");
	}

	@Override
	public synchronized ResponseResult createWhsOutHead(JSONObject doc, String userId) throws Exception {
		if (doc == null) {
			return ResponseResult.error("参数为空，创建出库任务失败");
		}
		String packageList = doc.getString("packageList");
		String werks = doc.getString("werks");
		String whsLocationCode = doc.getString("whsLocationCode");
		String cabinetSerialNumber = doc.getString("cabinetSerialNumber");
		String goodsType = doc.getString("goodsType");
		String cabinetModel = doc.getString("cabinetModel");
		if (StringUtils.isEmpty(packageList) || StringUtils.isEmpty(werks) || StringUtils.isEmpty(whsLocationCode) || 
				StringUtils.isEmpty(cabinetSerialNumber) || StringUtils.isEmpty(goodsType) || StringUtils.isEmpty(cabinetModel)) {
			return ResponseResult.error("装箱单号、装柜工厂、装柜仓库、柜序号、货物类型、柜型不能为空，创建出库任务失败");
		}
		// 根据装箱单号、装柜工厂、装柜仓库、柜序号、货物类型查询是否存在相同的出库任务，如存在就不能再创建
		QueryWrapper<WhsOut> eq = new QueryWrapper<WhsOut>().eq("WERKS", werks).eq("WHS_LOCATION_CODE", whsLocationCode)
				.eq("PACKAGE_LIST", packageList).eq("GOODS_TYPE", goodsType).eq("CABINET_SERIAL_NUMBER", cabinetSerialNumber);
		Integer count = this.baseMapper.selectCount(eq);
		if (count > 0) {
			return ResponseResult.error("存在相同的出库任务，无法重复创建");
		}
		// 根据装箱单号查询装柜出库头信息 
		QueryWrapper<SapPackageList> query = new QueryWrapper<SapPackageList>().eq("PACKAGE_LIST", packageList);
		SapPackageList sapPackageList = iSapPackageListService.getOne(query);
		// 根据装柜工厂查询其他的出库任务来获取发货主体字段值
		QueryWrapper<WhsOut> otherEq = new QueryWrapper<WhsOut>().eq("WERKS", werks).eq("PACKAGE_LIST", packageList);
		List<WhsOut> outList = this.baseMapper.selectList(otherEq);
		// 生成处理中的出库任务
		WhsOut whsOut1 = new WhsOut();
		String uuid = UUID.randomUUID().toString().replaceAll("-","").toLowerCase();
		whsOut1.setUuid(uuid);
		whsOut1.setWerks(werks);
		whsOut1.setWhsLocationCode(whsLocationCode);
		whsOut1.setPackageList(packageList);
		whsOut1.setGoodsType(goodsType);
		whsOut1.setDateOfLoading(sapPackageList.getPlanLoadingDate());
		whsOut1.setCabinetSerialNumber(cabinetSerialNumber);
		whsOut1.setCreateUserid(userId);
		whsOut1.setCreateDate(DateFormatUtils.format(new Date(), "yyyy-MM-dd"));
		whsOut1.setCabinetModel(sapPackageList.getCabinetModel());
		whsOut1.setShipper(CollectionUtils.isEmpty(outList) ? "" : outList.get(0).getShipper());
		whsOut1.setTaskStatus("1");
		whsOut1.setCabinetModel(cabinetModel);
		// 数据来源：手动创建
		whsOut1.setDataSource("2");
		this.baseMapper.insert(whsOut1);
		// 记录日志-出库任务初始化
		iSapDeliveryNoteLogService.insertSapDeliveryNoteLogM(uuid, userId, WHS_OUT_TASK_GENERATE);
		return ResponseResult.success().add("whsOut", this.baseMapper.selectOne(new QueryWrapper<WhsOut>().eq("UUID", uuid)));
	}

	@Override
	public Page<Map<String, Object>> getWhsOutListPagePda(JSONObject doc) {
		if (!doc.containsKey("packageList") || StringUtils.isEmpty(doc.getString("packageList"))) {
            return null;
        }
        int pageNo = StringUtils.isEmpty(doc.getString("pageNo")) ? 1 : (doc.getInteger("pageNo") < 1 ? 1 : doc.getInteger("pageNo"));
        int pageSize = StringUtils.isEmpty(doc.getString("pageSize")) ? 20 : doc.getInteger("pageSize");
//        List<String> werks = doc.getJSONArray("werks").toJavaList(String.class);
//        List<String> whsLocationCode = doc.getJSONArray("whsLocationCode").toJavaList(String.class);
        String whsOutNo = StringUtils.isEmpty(doc.getString("whsOutNo")) ? null : doc.getString("whsOutNo");
        String packageList = StringUtils.isEmpty(doc.getString("packageList")) ? null : doc.getString("packageList");
        String shipper = StringUtils.isEmpty(doc.getString("shipper")) ? null : doc.getString("shipper");
        String goodsType = StringUtils.isEmpty(doc.getString("goodsType")) ? null : doc.getString("goodsType");
        String dateOfLoading = StringUtils.isEmpty(doc.getString("dateOfLoading")) ? null : doc.getString("dateOfLoading");
        String cabinetSerialNumber = StringUtils.isEmpty(doc.getString("cabinetSerialNumber")) ? null : doc.getString("cabinetSerialNumber");
        String cabinetNumber = StringUtils.isEmpty(doc.getString("cabinetNumber")) ? null : doc.getString("cabinetNumber");
        List<String> taskStatus = StringUtils.isEmpty(doc.getString("taskStatus")) ? null : Arrays.asList(doc.getString("taskStatus").split(","));
        Page<Map<String, Object>> pages = new Page<>(pageNo, pageSize);
        List<Map<String, Object>> pageMaData = this.baseMapper.getWhsOutListPagePda(pages, whsOutNo, packageList, goodsType, dateOfLoading, cabinetSerialNumber, taskStatus, cabinetNumber, shipper);
        pages.setRecords(pageMaData);
        return pages;
	}

	@Override
	public List<Map<String, Object>> selectWhsOutDtl(String goodsType, String uuid) {
		/** 获取 备件货 出库任务明细行项目 */
        if ("X".equals(goodsType)) {
            return this.baseMapper.selectPackageListDtlX(uuid);
        }
        /** 获取 贸易货 出库任务明细行项目 */
        if ("M".equals(goodsType)) {
            return this.baseMapper.getPackageListDtlM(uuid);
        }
        return null;
	}
}
