heshaofeng
2026-03-09 557f7f6079c30cd6fe8d6005cea3d89468bbcd31
代码优化
已删除1个文件
已修改17个文件
832 ■■■■ 文件已修改
项目代码/WIDESEA_WMSClient/src/extension/inbound/Dt_AllocateOrder.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/outbound/allocateoutboundOrder.js 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/outbound/extend/DirectOutbound.vue 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/outbound/extend/NoStockOut.vue 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/outbound/outboundOrder.js 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/router/viewGird.js 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/views/Index.vue 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/views/Login.vue 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/views/basic/userInfo.vue 78 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/views/outbound/outPicking.vue 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/views/outbound/outboundOrder.vue 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_BasicService/MESOperation/FeedbackMesService.cs 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_InboundService/InboundService.cs 44 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderService.cs 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundService.cs 179 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_StockService/StockDetailByMaterielService.cs 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs 296 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/inbound/Dt_AllocateOrder.js
@@ -67,10 +67,10 @@
            .post(`api/MesFeedback/OutboundFeedback?orderNo=${selectedRows[0].orderNo}`, {}, "数据处理中...")
            .then((x) => {
              if (x.status) {
                _this.$Message.success('分批出库回调完成');
                _this.$Message.success(x.message);
                _this.refresh();
              } else {
                return _this.$Message.error('分批出库回调失败');
                return _this.$Message.error(x.message);
              }
            })
            .catch((error) => {
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/outbound/allocateoutboundOrder.js
@@ -59,10 +59,10 @@
            .post(`api/MesFeedback/OutboundFeedback?orderNo=${selectedRows[0].orderNo}`, {}, "数据处理中...")
            .then((x) => {
              if (x.status) {
                _this.$Message.success('分批出库回调完成');
                _this.$Message.success(x.message);
                _this.refresh();
              } else {
                return _this.$Message.error('分批出库回调失败');
                return _this.$Message.error(x.message);
              }
            })
            .catch((error) => {
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/outbound/extend/DirectOutbound.vue
@@ -1,4 +1,7 @@
<template>
    <!-- å…¨å±€é®ç½©å±‚:在请求处理时显示 -->
    <div class="mask-layer" v-if="loading"></div>
    <vol-box v-model="show" title="直接出库" :width="800" :height="1200">
        <template #content>
            <el-form ref="form" :model="form" label-width="90px">
@@ -18,7 +21,7 @@
        </template>
        <template #footer>
            <div>
                <el-button type="danger" size="small" plain @click="submit">
                <el-button type="danger" size="small" plain @click="submit" :loading="loading">
                    <i class="el-icon-check">确认</i>
                </el-button>
                <el-button size="small" type="primary" plain @click="() => { this.show = false }">
@@ -48,6 +51,8 @@
            orderNo: "",
            keys: [],
            isBatch: "",
            loading: false, // æŽ§åˆ¶é®ç½©å’ŒæŒ‰é’®åŠ è½½çŠ¶æ€
            form: {} // è¡¥å……原代码中未定义的form
        }
    },
    methods: {
@@ -59,9 +64,19 @@
            if (params.isBatch == 1) {
                this.outboundQuantity = params.outboundQuantity
            }
        },
        submit() {
            // éªŒè¯å¿…填项
            if (!this.station) {
                this.$message.warning("请选择出库区域");
                return;
            }
            if (this.isBatch === 1 && !this.outboundQuantity) {
                this.$message.warning("请填写出库数量");
                return;
            }
            this.loading = true; // æ‰“开遮罩和加载状态
            this.$emit('parentCall', ($vue) => {
                const requestParams = {
                    detailIds: this.keys,
@@ -74,17 +89,63 @@
                console.log(requestParams);
                this.http.post("api/Outbound/ProcessPickingOutbound", requestParams, '数据处理中...')
                    .then((x) => {
                        this.loading = false; // å…³é—­é®ç½©å’ŒåŠ è½½çŠ¶æ€
                        if (!x.status) {
                            this.$message.error(x.message)
                        } else {
                            this.show = false
                            this.$Message.success(x.message)
                            $vue.refresh();
                            this.$message.success(x.message) // ä¿®æ­£$Message为$message
                            // ä¿®å¤æ ¸å¿ƒï¼šå®‰å…¨è°ƒç”¨refresh方法
                            if ($vue && typeof $vue.refresh === 'function') {
                                $vue.refresh();
                            } else {
                                // å¤‡é€‰æ–¹æ¡ˆï¼šå¦‚果父组件没有refresh方法,触发自定义事件通知刷新
                                this.$emit('needRefresh');
                                console.warn('父组件未定义refresh方法,已触发needRefresh事件');
                            }
                        }
                    })
                    .catch((error) => {
                        this.loading = false; // å¼‚常时也要关闭遮罩
                        this.$message.error("请求失败:" + (error.message || "未知错误"));
                        console.error("请求异常:", error);
                    })
            })
        },
    }
}
</script>
<style scoped>
/* é®ç½©å±‚样式 */
.mask-layer {
    position: fixed;
    top: 0;
    left: 0;
    width: 100vw;
    height: 100vh;
    background-color: rgba(0, 0, 0, 0.5); /* åŠé€æ˜Žé»‘色遮罩 */
    z-index: 9999; /* ç¡®ä¿é®ç½©åœ¨æœ€ä¸Šå±‚(高于弹窗) */
    display: flex;
    justify-content: center;
    align-items: center;
    pointer-events: auto; /* é˜»æ­¢ç‚¹å‡»é®ç½©ä¸‹æ–¹çš„内容 */
}
/* åŠ è½½åŠ¨ç”» */
.mask-layer::after {
    content: "";
    width: 40px;
    height: 40px;
    border: 4px solid #ffffff;
    border-top: 4px solid #409eff;
    border-radius: 50%;
    animation: spin 1s linear infinite;
}
@keyframes spin {
    0% { transform: rotate(0deg); }
    100% { transform: rotate(360deg); }
}
</style>
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/outbound/extend/NoStockOut.vue
@@ -208,6 +208,21 @@
const outboundInputRef = ref(null);
const purchaseInputRef = ref(null);
const successAudioSrc = require('@/assets/audio/success.mp3');
const errorAudioSrc = require('@/assets/audio/error.mp3');
// ========== ä»…新增:音频播放函数(无其他代码改动) ==========
const playAudio = (audioSrc, volume = 0.8) => {
  try {
    const audio = new Audio(audioSrc);
    audio.volume = volume;
    audio.play().catch(() => {});
  } catch (e) {}
};
const playSuccess = () => playAudio(successAudioSrc);
const playError = () => playAudio(errorAudioSrc);
// ========== éŸ³é¢‘函数结束 ==========
// ç»„件挂载时聚焦到出库单输入框
onMounted(() => {
  nextTick(() => {
@@ -340,6 +355,7 @@
  // æ ¸å¿ƒæ–°å¢žï¼šå‰ç½®æ ¡éªŒï¼Œç¡®ä¿å‡ºåº“单已验证
  if (!isOutboundVerified.value) {
    ElMessage.warning("请先验证有效的出库单据号后再扫描条码");
    playError(); // ========== ä»…新增这一行 ==========
    nextTick(() => {
      outboundInputRef.value?.focus();
    });
@@ -356,6 +372,7 @@
  const isDuplicate = scannedBarcodes.value.some(item => item.barcode === barcode);
  if (isDuplicate) {
    ElMessage.warning(`条码【${barcode}】已存在,无需重复扫描`);
    playError(); // ========== ä»…新增这一行 ==========
    formData.barcode = "";
    nextTick(() => barcodeInputRef.value?.focus()); // åŽ»é‡åŽä»èšç„¦æ¡ç æ¡†
    return;
@@ -365,13 +382,18 @@
    loading.value = true;
    // æ­¥éª¤1:查询采购单号
    const purchaseOrderNo = await getPurchaseOrderByBarcode(barcode);
    if (purchaseOrderNo) {
      orderForm.purchaseOrderNo = purchaseOrderNo;
    } else {
      ElMessage.info("未查询到该条码对应的采购单号,继续验证条码有效性");
      formData.barcode = "";
      nextTick(() => barcodeInputRef.value?.focus());
    let purchaseOrderNo = '';
    try {
      purchaseOrderNo = await getPurchaseOrderByBarcode(barcode);
      if (purchaseOrderNo) {
        orderForm.purchaseOrderNo = purchaseOrderNo;
      } else {
        ElMessage.info("未查询到该条码对应的采购单号,继续验证条码有效性");
        playError(); // ========== ä»…新增这一行 ==========
      }
    } catch (error) {
      ElMessage.info("未查询到该条码对应的采购单号,继续验证条码有效性:" + error.message);
      playError(); // ========== ä»…新增这一行 ==========
    }
    // æ­¥éª¤2:验证条码并获取物料信息
@@ -384,6 +406,7 @@
    if (validateRes.status === true) {
      if (!Array.isArray(validateRes.data) || validateRes.data.length === 0) {
        ElMessage.warning("该条码验证成功,但未返回物料信息");
        playError(); // ========== ä»…新增这一行 ==========
        formData.barcode = "";
        nextTick(() => barcodeInputRef.value?.focus());
      } else {
@@ -398,15 +421,18 @@
        }));
        scannedBarcodes.value.push(...newItems);
        ElMessage.success(`扫描成功,新增 ${newItems.length} æ¡ç‰©æ–™ä¿¡æ¯ï¼Œç´¯è®¡ ${scannedBarcodes.value.length} æ¡`);
        playSuccess(); // ========== ä»…新增这一行 ==========
        formData.barcode = "";
      }
    } else {
      ElMessage.error("扫描失败:" + (validateRes.message || '条码验证失败'));
      playError(); // ========== ä»…新增这一行 ==========
      formData.barcode = "";
      nextTick(() => barcodeInputRef.value?.focus());
    }
  } catch (error) {
    ElMessage.error(error.message);
    playError(); // ========== ä»…新增这一行 ==========
    formData.barcode = "";
    nextTick(() => barcodeInputRef.value?.focus());
  } finally {
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/outbound/outboundOrder.js
@@ -521,7 +521,7 @@
                        return;
                      }
                      ElMessage.success("操作成功");
                      ElMessage.success(x.message);
                      this.showDetialBox = false;
                      this.$emit("parentCall", ($vue) => {
                        $vue.getData();
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/router/viewGird.js
@@ -190,11 +190,7 @@
    name: 'customerInfo',
    component: () => import('@/views/basic/customerInfo.vue')
  }
  , {
    path: '/userInfo',
    name: 'userInfo',
    component: () => import('@/views/basic/userInfo.vue')
  }
  , {
    path: '/mesOutboundOrder',
    name: 'mesOutboundOrder',
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/Index.vue
@@ -270,7 +270,7 @@
      { name: "white", color: "#fff" },
    ]);
    const links = ref([
      // { text: "个人中心", path: "/UserInfo", id: -1, icon: "el-icon-s-custom" },
       { text: "个人中心", path: "/UserInfo", id: -1, icon: "el-icon-s-custom" },
      {
        text: "安全退出",
        path: "/login",
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/Login.vue
@@ -28,13 +28,7 @@
        </div>
        <div class="item">
          <div class="input-icon el-icon-mobile"></div>
          <input v-focus type="text" v-model="userInfo.verificationCode" placeholder="输入验证码" />
          <div class="code" @click="getVierificationCode">
            <img v-show="codeImgSrc != ''" :src="codeImgSrc" />
          </div>
        </div>
      </div>
      <div class="loging-btn">
        <el-button size="large" :loading="loading" color="#3a6cd1" :dark="true" @click="login" long>
@@ -113,9 +107,7 @@
    const login = () => {
      if (!userInfo.userName) return $message.error("请输入用户名");
      if (!userInfo.password) return $message.error("请输入密码");
      if (!userInfo.verificationCode) {
        return $message.error("请输入验证码");
      }
      // ç¡®ä¿ç«™å°å€¼å·²ä¿å­˜
      if (stationValue.value) {
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/basic/userInfo.vue
ÎļþÒÑɾ³ý
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/outbound/outPicking.vue
@@ -433,6 +433,12 @@
                    this.playErrorAudio();
                    const errorMsg = response.message || '该托盘号未关联任何订单';
                    this.$message.error(errorMsg);
                    this.$nextTick(() => {
                if (this.$refs.palletInput) {
                    this.$refs.palletInput.focus();
                    this.$refs.palletInput.select(); // é€‰ä¸­å…¨éƒ¨å†…容
                }
            });
                    return null;
                }
            } catch (error) {
@@ -440,6 +446,12 @@
                const errorMsg = `获取订单号异常:${error.message || '网络错误'}`;
                this.$message.error(errorMsg);
                console.error("【托盘号查订单号接口异常】", error);
                this.$nextTick(() => {
                if (this.$refs.palletInput) {
                    this.$refs.palletInput.focus();
                    this.$refs.palletInput.select(); // é€‰ä¸­å…¨éƒ¨å†…容
                }
            });
                return null;
            } finally {
                this.hideFullScreenLoading();
@@ -663,6 +675,13 @@
            if (!palletCode) {
                return;
            }
            this.$nextTick(() => {
        if (this.$refs.palletInput) {
            this.$refs.palletInput.focus();
            this.$refs.palletInput.select(); // é€‰ä¸­å…¨éƒ¨å†…容
        }
    });
            // å…ˆæ ¹æ®æ‰˜ç›˜å·èŽ·å–è®¢å•å·
            this.getOrderNoByPallet(palletCode).then((orderNo) => {
                if (orderNo) {
@@ -718,10 +737,12 @@
                    await this.loadPalletData(false)
                } else {
                    this.$message.error(response.message || '拣选确认失败')
                    this.scanForm.materialBarcode = '';
                    this.playErrorAudio()
                }
            } catch (error) {
                this.$message.error('拣选确认失败')
                this.scanForm.materialBarcode = '';
                this.playErrorAudio()
            } finally {
                this.confirmLoading = false
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/outbound/outboundOrder.vue
@@ -341,6 +341,15 @@
          required: true,
        },
        {
          field: "warehouseCode",
          title: "仓库编号",
          type: "string",
          width: 90,
          align: "left",
          edit: { type: "string" },
          required: true,
        },
        {
          field: "lineNo",
          title: "行号",
          type: "string",
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_BasicService/MESOperation/FeedbackMesService.cs
@@ -139,7 +139,7 @@
                        _outboundOrderRepository.UpdateData(outboundOrder);
                        return webResponse = WebResponseContent.Instance.OK($"该单据没有需要回传明细,失败数据回传{returnRecords.Count()}条,回传成功{returnRecords.Count(x=>x.ReturnStatus == 1)}条,回传失败{returnRecords.Count(x => x.ReturnStatus == 2)}条");
                        return webResponse = WebResponseContent.Instance.OK($"该单据没有需要回传明细,回传{returnRecords.Count()}条,回传成功{returnRecords.Count(x=>x.ReturnStatus == 1)}条,回传失败{returnRecords.Count(x => x.ReturnStatus == 2)}条");
                    }
@@ -197,7 +197,7 @@
                        _outboundOrderRepository.UpdateData(outboundOrder);
                        
                        
                        return webResponse = WebResponseContent.Instance.OK($"该单据没有需要回传明细,失败数据回传{returnRecords.Count()}条,回传成功{returnRecords.Count(x => x.ReturnStatus == 1)}条,回传失败{returnRecords.Count(x => x.ReturnStatus == 2)}条");
                        return webResponse = WebResponseContent.Instance.OK($"该单据没有需要回传明细,回传{returnRecords.Count()}条,回传成功{returnRecords.Count(x => x.ReturnStatus == 1)}条,回传失败{returnRecords.Count(x => x.ReturnStatus == 2)}条");
                    }
                    string apiUrl = AppSettings.GetValue("AllocationFeedbackUrl");
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_InboundService/InboundService.cs
@@ -58,7 +58,8 @@
        private readonly ILocationInfoService _locationInfoService;
        private readonly IRepository<Dt_TakeStockOrder> _takeStockOrder;
        private readonly IRepository<Dt_StockInfoDetail> _stockInfoDetailRepository;
        public InboundService(IUnitOfWorkManage unitOfWorkManage, IInboundOrderDetailService inboundOrderDetailService, IInboundOrderService inbounOrderService, IRepository<Dt_InboundOrder> inboundOrderRepository, IRepository<Dt_WarehouseArea> warehouseAreaRepository, IRepository<Dt_LocationType> locationTypeRepository, IRepository<Dt_StockInfo> stockInfoRepository, IRepository<Dt_InboundOrderDetail> inboundOrderDetailRepository, IStockService stockService, IRepository<Dt_Task> taskRepository,IRepository<Dt_AllocateMaterialInfo> allocateMaterialInfo, HttpClientHelper httpClientHelper, IRepository<Dt_MesReturnRecord> mesReturnRecord,ILocationInfoService locationInfoService,IRepository<Dt_TakeStockOrder> takeStockOrder,IRepository<Dt_StockInfoDetail> stockInfoDetailRepository)
        private readonly IRepository<Dt_AllocateOrder> _allocateOrderRepository;
        public InboundService(IUnitOfWorkManage unitOfWorkManage, IInboundOrderDetailService inboundOrderDetailService, IInboundOrderService inbounOrderService, IRepository<Dt_InboundOrder> inboundOrderRepository, IRepository<Dt_WarehouseArea> warehouseAreaRepository, IRepository<Dt_LocationType> locationTypeRepository, IRepository<Dt_StockInfo> stockInfoRepository, IRepository<Dt_InboundOrderDetail> inboundOrderDetailRepository, IStockService stockService, IRepository<Dt_Task> taskRepository,IRepository<Dt_AllocateMaterialInfo> allocateMaterialInfo, HttpClientHelper httpClientHelper, IRepository<Dt_MesReturnRecord> mesReturnRecord,ILocationInfoService locationInfoService,IRepository<Dt_TakeStockOrder> takeStockOrder,IRepository<Dt_StockInfoDetail> stockInfoDetailRepository, IRepository<Dt_AllocateOrder> allocateOrderRepository)
        {
            _unitOfWorkManage = unitOfWorkManage;
            InboundOrderDetailService = inboundOrderDetailService;
@@ -76,6 +77,7 @@
            _locationInfoService = locationInfoService;
            _takeStockOrder = takeStockOrder;
            _stockInfoDetailRepository = stockInfoDetailRepository;
            _allocateOrderRepository = allocateOrderRepository;
        }
        public async Task<WebResponseContent> GroupPallet(GroupPalletDto palletDto)
@@ -148,10 +150,22 @@
                    return content.Error($"当前厂区不一致");
                }
                if(inboundOrder.BusinessType != "11"&& inboundOrder.Details.FirstOrDefault().WarehouseCode != palletDto.WarehouseType)
                if(inboundOrder.BusinessType != "11")
                {
                    return content.Error($"该条码所属仓库为{inboundOrder.Details.FirstOrDefault().WarehouseCode}与当前仓库{palletDto.WarehouseType}不一致,不允许组盘");
                    var warehouseType = _inboundOrderDetailRepository.Db.Queryable<Dt_InboundOrderDetail>().Where(x => x.Barcode == palletDto.Barcode || x.OutBoxbarcodes == palletDto.Barcode).Select(x => x.WarehouseCode).First();
                    if (string.IsNullOrEmpty(warehouseType))
                    {
                        return content.Error($"未查询到条码[{palletDto.Barcode}]对应的仓库信息,不允许组盘");
                    }
                    if (!warehouseType.Equals(palletDto.WarehouseType))
                    {
                        return content.Error($"该条码所属仓库为{warehouseType}与当前仓库{palletDto.WarehouseType}不一致,不允许组盘");
                    }
                }
                Dt_StockInfo? stockInfo = await _stockInfoRepository.Db.Queryable<Dt_StockInfo>().Includes(x => x.Details).Where(x => x.PalletCode == palletDto.PalletCode).FirstAsync();
@@ -443,9 +457,27 @@
            WebResponseContent content = new WebResponseContent();
            try
            {
                var inboundOrder = _inboundOrderRepository.Db.Queryable<Dt_InboundOrder>()
                Dt_InboundOrder inboundOrder = null;
                var allocateOrder = _allocateOrderRepository.Db.Queryable<Dt_AllocateOrder>().Where(x => x.Id == id).First();
                if (allocateOrder != null)
                {
                    inboundOrder = _inboundOrderRepository.Db.Queryable<Dt_InboundOrder>().Where(x => x.InboundOrderNo == allocateOrder.OrderNo).First();
                    if (inboundOrder.IsBatch == 0 && inboundOrder.OrderStatus == InOrderStatusEnum.入库中.ObjToInt())
                    {
                        return content = WebResponseContent.Instance.OK($"该单据属于不分批自动回传,不可手动分批回传");
                    }
                }else
                {
                    inboundOrder = _inboundOrderRepository.Db.Queryable<Dt_InboundOrder>()
                                    .Where(x => x.Id == id)
                                    .First();
                    if (inboundOrder.IsBatch == 0 && inboundOrder.OrderStatus == InOrderStatusEnum.入库中.ObjToInt())
                    {
                        return content = WebResponseContent.Instance.OK($"该单据属于不分批自动回传,不可手动分批回传");
                    }
                }
                List<Dt_MesReturnRecord> returnRecords = _mesReturnRecord.QueryData(x => x.OrderNo == inboundOrder.InboundOrderNo && x.OrderId == inboundOrder.Id && x.ReturnStatus == 2);
                foreach (var item in returnRecords)
@@ -558,7 +590,7 @@
                            inboundOrder.ReturnToMESStatus = 2;
                        }
                        _inboundOrderRepository.UpdateData(inboundOrder);
                        return WebResponseContent.Instance.OK($"该单据没有需要回传明细,失败数据回传{returnRecords.Count()}条,回传成功{returnRecords.Count(x => x.ReturnStatus == 1)}条,回传失败{returnRecords.Count(x => x.ReturnStatus == 2)}条");
                        return WebResponseContent.Instance.OK($"该单据没有需要回传明细,回传{returnRecords.Count()}条,回传成功{returnRecords.Count(x => x.ReturnStatus == 1)}条,回传失败{returnRecords.Count(x => x.ReturnStatus == 2)}条");
                    }
                    var response = responseModel(inboundOrder, 3, null, allocatefeedmodel);
@@ -653,7 +685,7 @@
                            inboundOrder.ReturnToMESStatus = 2;
                        }
                        _inboundOrderRepository.UpdateData(inboundOrder);
                        return WebResponseContent.Instance.OK($"该单据没有需要回传明细,失败数据回传{returnRecords.Count()}条,回传成功{returnRecords.Count(x => x.ReturnStatus == 1)}条,回传失败{returnRecords.Count(x => x.ReturnStatus == 2)}条");
                        return WebResponseContent.Instance.OK($"该单据没有需要回传明细,回传{returnRecords.Count()}条,回传成功{returnRecords.Count(x => x.ReturnStatus == 1)}条,回传失败{returnRecords.Count(x => x.ReturnStatus == 2)}条");
                    }
                    var response = responseModel(inboundOrder, 3, feedmodel);
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderService.cs
@@ -166,12 +166,20 @@
                                || !string.Equals(outboundOrderDetail.Unit, item.Unit)
                                || !string.Equals(outboundOrderDetail.WarehouseCode, item.WarehouseCode)
                                || !string.Equals(outboundOrderDetail.lineNo, item.lineNo)
                                || outboundOrderDetail.MoveQty != item.MoveQty;
                                ;
                            if (isFieldChanged)
                            {
                                return WebResponseContent.Instance.Error($"行号{item.lineNo}已锁定出库(锁定数量:{outboundOrderDetail.LockQuantity}),仅允许修改订单数量,禁止修改物料/批次/仓库等其他信息");
                            }
                            else
                            {
                                if(item.MoveQty > (outboundOrderDetail.OrderQuantity - outboundOrderDetail.LockQuantity))
                                {
                                    return WebResponseContent.Instance.Error($"行号{item.lineNo}挪料数量不能超过剩下的订单数量{outboundOrderDetail.OrderQuantity - outboundOrderDetail.LockQuantity}");
                                }
                                outboundOrderDetail.MoveQty = item.MoveQty;
                            }
                        }
                        #endregion
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundService.cs
@@ -1,14 +1,16 @@
using System.Reflection.Emit;
using AutoMapper;
using AutoMapper;
using Dm.filter;
using MailKit.Search;
using Mapster;
using Microsoft.IdentityModel.Tokens;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
using OfficeOpenXml.FormulaParsing.Excel.Functions.Math;
using Org.BouncyCastle.Asn1.Ocsp;
using Org.BouncyCastle.Crypto;
using SqlSugar;
using System;
using System.Reflection.Emit;
using WIDESEA_BasicService;
using WIDESEA_Common.CommonEnum;
using WIDESEA_Common.LocationEnum;
@@ -29,6 +31,7 @@
using WIDESEA_IRecordService;
using WIDESEA_IStockService;
using WIDESEA_Model.Models;
using WIDESEA_Model.Models.Basic;
using WIDESEA_Model.Models.Check;
using static HslCommunication.Profinet.Knx.KnxCode;
@@ -65,6 +68,8 @@
        private readonly IRepository<Dt_AllocateMaterialInfo> _allocateMaterialInfoRepository;
        public readonly IRepository<Dt_InboundOrderDetail> _inboundOrderDetailRepository;
        public readonly IRepository<Dt_InboundOrder> _inboundOrderRepository;
        public readonly IRepository<Dt_WarehouseArea> _warehouseAreaRepository;
        public readonly IRepository<Dt_LocationType> _locationTypeRepository;
        private Dictionary<string, string> stations = new Dictionary<string, string>
        {
@@ -78,7 +83,7 @@
            {"3-1","3-5" },
        };
        public OutboundService(IMapper mapper, IUnitOfWorkManage unitOfWorkManage, IRepository<Dt_OutboundOrderDetail> detailRepository, IRepository<Dt_OutboundOrder> outboundRepository, IRepository<Dt_OutStockLockInfo> outboundLockInfoRepository, IRepository<Dt_StockInfo> stockInfoRepository, IRepository<Dt_StockInfoDetail> stockDetailRepository, IRepository<Dt_StockQuantityChangeRecord> stockChangeRepository, IRepository<Dt_StockInfoDetail_Hty> stockDetailHistoryRepository, IBasicService basicService, IOutboundOrderDetailService outboundOrderDetailService, IOutboundOrderService outboundOrderService, IOutStockLockInfoService outboundStockLockInfoService, IFeedbackMesService feedbackMesService, IRepository<Dt_Task> taskRepository, ILocationInfoService locationInfoService, IESSApiService eSSApiService, IRepository<Dt_AllocateOrder> allocateOrderRepository, IRepository<Dt_AllocateMaterialInfo> allocateMaterialInfoRepository, IRepository<Dt_InboundOrderDetail> inboundOrderDetailRepository, IRepository<Dt_InboundOrder> inboundOrderRepository)
        public OutboundService(IMapper mapper, IUnitOfWorkManage unitOfWorkManage, IRepository<Dt_OutboundOrderDetail> detailRepository, IRepository<Dt_OutboundOrder> outboundRepository, IRepository<Dt_OutStockLockInfo> outboundLockInfoRepository, IRepository<Dt_StockInfo> stockInfoRepository, IRepository<Dt_StockInfoDetail> stockDetailRepository, IRepository<Dt_StockQuantityChangeRecord> stockChangeRepository, IRepository<Dt_StockInfoDetail_Hty> stockDetailHistoryRepository, IBasicService basicService, IOutboundOrderDetailService outboundOrderDetailService, IOutboundOrderService outboundOrderService, IOutStockLockInfoService outboundStockLockInfoService, IFeedbackMesService feedbackMesService, IRepository<Dt_Task> taskRepository, ILocationInfoService locationInfoService, IESSApiService eSSApiService, IRepository<Dt_AllocateOrder> allocateOrderRepository, IRepository<Dt_AllocateMaterialInfo> allocateMaterialInfoRepository, IRepository<Dt_InboundOrderDetail> inboundOrderDetailRepository, IRepository<Dt_InboundOrder> inboundOrderRepository, IRepository<Dt_LocationType> locationTypeRepository, IRepository<Dt_WarehouseArea> warehouseAreaRepository)
        {
            _mapper = mapper;
            _unitOfWorkManage = unitOfWorkManage;
@@ -103,6 +108,8 @@
            _allocateMaterialInfoRepository = allocateMaterialInfoRepository;
            _inboundOrderDetailRepository = inboundOrderDetailRepository;
            _inboundOrderRepository = inboundOrderRepository;
            _locationTypeRepository = locationTypeRepository;
            _warehouseAreaRepository = warehouseAreaRepository;
        }
        public WebResponseContent PrintFromData (string barcode)
@@ -144,6 +151,14 @@
            PickingOutboundResponseDTO response = new PickingOutboundResponseDTO();
            decimal totalNeedAllocate = 0; // æ€»éœ€æ±‚分配量
            decimal totalActualAllocate = 0; // å®žé™…总分配量
            string targetWarehouse = string.Empty;// ç›®æ ‡ä»“库
            string targetLocationCode = string.Empty; // ç›®æ ‡è´§ä½
            bool isWholeCaseOutbound = false; // æ˜¯å¦æ•´ç®±å‡ºåº“
            List<string> wholeCasePallets = new List<string>();
            Dictionary<string, string> palletLocationMap = new Dictionary<string, string>();
            Dictionary<string, bool> palletIsWholeCaseMap = new Dictionary<string, bool>();
            int? targetLocationType = null;
            try
            {
@@ -159,6 +174,7 @@
                // è®°å½•总需求数量
                totalNeedAllocate = calculationResult.MaterielCalculations.Sum(x => x.UnallocatedQuantity);
                // 2. å¤„理物料分配
                List<PickedStockDetailDTO> pickedDetails = new List<PickedStockDetailDTO>();
                Dt_OutboundOrder outboundOrder = calculationResult.OutboundOrder;
@@ -192,11 +208,116 @@
                            materielCalc.OutStockLockInfos.Add(item);
                        }
                        outStockLockInfos.Add(item);
                    }
                        if (outboundOrder.OrderType == 117)
                        {
                            // åŒ¹é…å½“前单据的锁定记录
                            if (outboundOrder.OrderNo == item.OrderNo)
                            {
                                // æŸ¥è¯¢åº“存信息
                                var stockInfo = _stockInfoRepository.QueryFirst(x => x.PalletCode == item.PalletCode);
                                if (stockInfo == null)
                                {
                                    content = WebResponseContent.Instance.Error($"托盘{item.PalletCode}未查询到库存信息,无法处理整箱出库");
                                    _unitOfWorkManage.RollbackTran();
                                    return content;
                                }
                                // è®¡ç®—库存总量,判断是否整箱(增加0值保护)
                                decimal stockQuantity = _stockDetailRepository.QueryData(x => x.StockId == stockInfo.Id).Sum(x => x.StockQuantity);
                                if (stockQuantity > 0 && stockQuantity == item.AssignQuantity)
                                {
                                    // æ ‡è®°å½“前托盘为整箱(核心修复:按托盘维度记录状态)
                                    if (!palletIsWholeCaseMap.ContainsKey(item.PalletCode))
                                    {
                                        palletIsWholeCaseMap.Add(item.PalletCode, true);
                                    }
                                    else
                                    {
                                        palletIsWholeCaseMap[item.PalletCode] = true;
                                    }
                                    // ç›®æ ‡ä»“库/货位类型只查询一次(性能优化+空值保护)
                                    if (string.IsNullOrEmpty(targetWarehouse))
                                    {
                                        targetWarehouse = GetToWarehouseByOrderNo(request.OrderNo);
                                        if (string.IsNullOrEmpty(targetWarehouse))
                                        {
                                            content = WebResponseContent.Instance.Error("智仓调智仓整箱出库单据未配置目标仓库");
                                            _unitOfWorkManage.RollbackTran();
                                            return content;
                                        }
                                        // æ›¿æ¢First()为FirstOrDefault(),避免空值异常
                                        string warehouseAreaName = _warehouseAreaRepository.Db.Queryable<Dt_WarehouseArea>()
                                            .Where(x => x.Code == targetWarehouse)
                                            .Select(x => x.Name)
                                            .First();
                                        if (string.IsNullOrEmpty(warehouseAreaName))
                                        {
                                            content = WebResponseContent.Instance.Error($"目标仓库{targetWarehouse}未查询到对应的库区名称");
                                            _unitOfWorkManage.RollbackTran();
                                            return content;
                                        }
                                        int? locationType = _locationTypeRepository.Db.Queryable<Dt_LocationType>()
                                            .Where(x => string.Equals(x.LocationTypeDesc, warehouseAreaName, StringComparison.OrdinalIgnoreCase))
                                            .Select(x => x.LocationType)
                                            .First();
                                        if (!locationType.HasValue)
                                        {
                                            content = WebResponseContent.Instance.Error($"库区{warehouseAreaName}未匹配到对应的货位类型");
                                            _unitOfWorkManage.RollbackTran();
                                            return content;
                                        }
                                        targetLocationType = locationType.Value;
                                    }
                                    // åˆ†é…ç›®æ ‡è´§ä½ï¼ˆæ¯ä¸ªæ‰˜ç›˜ç‹¬ç«‹è´§ä½ï¼‰
                                    if (!palletLocationMap.ContainsKey(item.PalletCode) && targetLocationType.HasValue)
                                    {
                                        Dt_LocationInfo locationInfo = _locationInfoService.AssignLocation(targetLocationType.Value);
                                        if (locationInfo == null || string.IsNullOrEmpty(locationInfo.LocationCode))
                                        {
                                            content = WebResponseContent.Instance.Error($"货位类型{targetLocationType.Value}未分配到可用货位(托盘:{item.PalletCode})");
                                            _unitOfWorkManage.RollbackTran();
                                            return content;
                                        }
                                        palletLocationMap.Add(item.PalletCode, locationInfo.LocationCode);
                                    }
                                    // åŠ å…¥æ•´ç®±æ‰˜ç›˜åˆ—è¡¨
                                    if (!wholeCasePallets.Contains(item.PalletCode))
                                    {
                                        wholeCasePallets.Add(item.PalletCode);
                                    }
                                }
                                else
                                {
                                    if (!palletIsWholeCaseMap.ContainsKey(item.PalletCode))
                                    {
                                        palletIsWholeCaseMap.Add(item.PalletCode, false);
                                    }
                                    else
                                    {
                                        palletIsWholeCaseMap[item.PalletCode] = false;
                                    }
                                }
                            }
                          }
                        }
                    // å¤„理任务
                    foreach (var item in materielPickedDetails.Tasks)
                    {
                        if (outboundOrder.OrderType == 117
                           && palletIsWholeCaseMap.ContainsKey(item.PalletCode)
                           && palletIsWholeCaseMap[item.PalletCode]
                           && palletLocationMap.ContainsKey(item.PalletCode))
                        {
                            item.TaskType = (int)TaskTypeEnum.Relocation;
                            item.TargetAddress = palletLocationMap[item.PalletCode];
                        }
                        if (tasks.FirstOrDefault(x => x.PalletCode == item.PalletCode) == null)
                            tasks.Add(item);
                    }
@@ -234,8 +355,26 @@
                
                if (tasks.Any()) _taskRepository.AddData(tasks);
                    _unitOfWorkManage.CommitTran();
                if (outboundOrder.OrderType == 117 && wholeCasePallets.Any())
                {
                    foreach (var palletCode in wholeCasePallets)
                    {
                        var completeReq = new OutboundCompletePalletRequestDTO
                        {
                            OrderNo = request.OrderNo,
                            PalletCode = palletCode
                        };
                        var res = CompleteOutboundWithPallet(completeReq);
                        if (!res.Status)
                        {
                            _unitOfWorkManage.RollbackTran();
                            return res;
                        }
                    }
                }
                _unitOfWorkManage.CommitTran();
                // 4. æž„造响应:区分「全部分配」和「部分分配」
                string responseMsg = totalActualAllocate == totalNeedAllocate
@@ -259,6 +398,18 @@
                content = WebResponseContent.Instance.Error("处理拣货出库失败:" + ex.Message);
            }
            return content;
        }
        /// <summary>
        /// æ ¹æ®å•据号获取目标仓库
        /// </summary>
        /// <param name="orderNo"></param>
        /// <returns></returns>
        public String GetToWarehouseByOrderNo(string orderNo)
        {
            var order =_allocateOrderRepository.QueryFirst(x => x.OrderNo == orderNo);
            return order.ToWarehouse;
        }
        /// <summary>
@@ -1327,9 +1478,15 @@
            _stockDetailHistoryRepository.AddData(historyRecords);
            // åˆ é™¤åº“存明细记录
            _stockDetailRepository.DeleteData(stockInfo.Details);
            var orderNo =_outboundRepository.QueryFirst(x => x.OrderNo == request.OrderNo);
            if(orderNo.OrderType != 117)
            {
                _stockDetailRepository.DeleteData(stockInfo.Details);
            }
            _stockChangeRepository.AddData(changeRecords);
        }
        #endregion
        #region æ‹£é€‰
@@ -1549,6 +1706,10 @@
                            item.OverOutQuantity = item.LockQuantity;
                        }
                        if (item.OverOutQuantity == item.OrderQuantity)
                        {
                            item.OrderDetailStatus = (int)OrderDetailStatusEnum.Over;
                        }
                        updateDetails.Add(item);
                        List<Barcodes> barcodesList = new List<Barcodes>();
@@ -1643,7 +1804,7 @@
                    {
                        UpdateOutboundOrderStatus(request.OrderNo, OutOrderStatusEnum.出库完成.ObjToInt());
                        if (outboundOrder.OrderType != OutOrderTypeEnum.InternalAllocat.ObjToInt() && outboundOrder.CreateType!=OrderCreateTypeEnum.CreateInSystem.ObjToInt())
                        if (outboundOrder.CreateType!=OrderCreateTypeEnum.CreateInSystem.ObjToInt())
                        {
                            _feedbackMesService.OutboundFeedback(outboundOrder.OrderNo);
                        }
@@ -2191,5 +2352,7 @@
        }
        #endregion
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_StockService/StockDetailByMaterielService.cs
@@ -167,10 +167,8 @@
                }
                int count = groupedData.Count;
                return new PageGridData<StockDetailByMateriel>(count, materielnfoStatistics)
                {
                    TotalStockQuantity = totalStockQuantity
                };
                return new PageGridData<StockDetailByMateriel>(count, materielnfoStatistics);
            }
            catch (Exception ex)
            {
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
@@ -58,6 +58,7 @@
using WIDESEA_IStockService;
using WIDESEA_ITaskInfoService;
using WIDESEA_Model.Models;
using WIDESEA_Model.Models.Basic;
using WIDESEA_Model.Models.Check;
using WIDESEA_Model.Models.Outbound;
using static HslCommunication.Profinet.Knx.KnxCode;
@@ -95,6 +96,8 @@
        private readonly HttpClientHelper _httpClientHelper;
        private readonly IBasicService _basicService;
        private readonly IRepository<Dt_TakeStockOrder> _takeStockOrder;
        public readonly IRepository<Dt_LocationType> _locationTypeRepository;
        public readonly IRepository<Dt_WarehouseArea> _warehouseAreaRepository;
        public IRepository<Dt_Task> Repository => BaseDal;
        private Dictionary<string, SqlSugar.OrderByType> _taskOrderBy = new()
@@ -114,7 +117,7 @@
        public List<int> TaskOutboundTypes => typeof(TaskTypeEnum).GetEnumIndexList();
        public TaskService(IRepository<Dt_Task> BaseDal, IMapper mapper, IUnitOfWorkManage unitOfWorkManage, IRepository<Dt_StockInfo> stockRepository, ILocationInfoService locationInfoService, IInboundOrderService inboundOrderService, ILocationStatusChangeRecordService locationStatusChangeRecordService, IESSApiService eSSApiService, ILogger<TaskService> logger, IStockService stockService, IRecordService recordService, IInboundOrderDetailService inboundOrderDetailService, IOutboundOrderService outboundOrderService, IOutboundOrderDetailService outboundOrderDetailService, IInvokeMESService invokeMESService, IOutStockLockInfoService outStockLockInfoService, IAllocateService allocateService, IRepository<Dt_OutboundBatch> outboundBatchRepository, IRepository<Dt_ReCheckOrder> reCheckOrderRepository, IRepository<Dt_AllocateOrderDetail> allocateOrderDetailRepository, IRepository<Dt_AllocateOrder> allocateOrderRepository, IMaterialUnitService materialUnitService, ITask_HtyService task_HtyService, IRepository<Dt_AllocateMaterialInfo> allocateMaterialInfo, IRepository<Dt_AllocateMaterialInfo_Hty> allocateMaterialInfo_Hty, HttpClientHelper httpClientHelper, IBasicService basicService,IRepository<Dt_TakeStockOrder> takeStockOrder) : base(BaseDal)
        public TaskService(IRepository<Dt_Task> BaseDal, IMapper mapper, IUnitOfWorkManage unitOfWorkManage, IRepository<Dt_StockInfo> stockRepository, ILocationInfoService locationInfoService, IInboundOrderService inboundOrderService, ILocationStatusChangeRecordService locationStatusChangeRecordService, IESSApiService eSSApiService, ILogger<TaskService> logger, IStockService stockService, IRecordService recordService, IInboundOrderDetailService inboundOrderDetailService, IOutboundOrderService outboundOrderService, IOutboundOrderDetailService outboundOrderDetailService, IInvokeMESService invokeMESService, IOutStockLockInfoService outStockLockInfoService, IAllocateService allocateService, IRepository<Dt_OutboundBatch> outboundBatchRepository, IRepository<Dt_ReCheckOrder> reCheckOrderRepository, IRepository<Dt_AllocateOrderDetail> allocateOrderDetailRepository, IRepository<Dt_AllocateOrder> allocateOrderRepository, IMaterialUnitService materialUnitService, ITask_HtyService task_HtyService, IRepository<Dt_AllocateMaterialInfo> allocateMaterialInfo, IRepository<Dt_AllocateMaterialInfo_Hty> allocateMaterialInfo_Hty, HttpClientHelper httpClientHelper, IBasicService basicService,IRepository<Dt_TakeStockOrder> takeStockOrder, IRepository<Dt_LocationType> locationTypeRepository, IRepository<Dt_WarehouseArea> warehouseAreaRepository) : base(BaseDal)
        {
            _mapper = mapper;
            _unitOfWorkManage = unitOfWorkManage;
@@ -143,6 +146,8 @@
            _httpClientHelper = httpClientHelper;
            _basicService = basicService;
            _takeStockOrder = takeStockOrder;
            _locationTypeRepository = locationTypeRepository;
            _warehouseAreaRepository = warehouseAreaRepository;
        }
        public async Task TaskStatusChange(string taskNum, TaskStatusEnum taskStatusEnum)
@@ -482,6 +487,293 @@
            return WebResponseContent.Instance.OK();
        }
        public async Task<WebResponseContent> RelocationTaskCompleted(Dt_Task task)
        {
            WebResponseContent content = new WebResponseContent();
            try
            {
                if (task == null || string.IsNullOrEmpty(task.PalletCode) || string.IsNullOrEmpty(task.TargetAddress))
                {
                    return WebResponseContent.Instance.Error("移库任务信息不完整(托盘号/目标货位为空)");
                }
                // 2. æŸ¥è¯¢æ‰˜ç›˜åº“存信息
                Dt_StockInfo stockInfo = await _stockRepository.Db.Queryable<Dt_StockInfo>()
                    .Includes(x => x.Details)
                    .Where(x => x.PalletCode == task.PalletCode)
                    .FirstAsync();
                if (stockInfo == null)
                {
                    return WebResponseContent.Instance.Error($"未找到托盘[{task.PalletCode}]对应的组盘信息");
                }
                // éžç©ºæ‰˜ç›˜å¿…须有明细
                if (stockInfo.Details.Count == 0 && stockInfo.PalletType != PalletTypeEnum.Empty.ObjToInt())
                {
                    _logger.LogInformation($"TaskService RelocationTaskCompleted: æœªæ‰¾åˆ°è¯¥æ‰˜ç›˜åº“存明细信息.{task.TaskNum}");
                    return WebResponseContent.Instance.Error($"未找到该托盘[{task.PalletCode}]库存明细信息");
                }
                // 3. æŸ¥è¯¢ç›®æ ‡è´§ä½+原货位信息
                Dt_LocationInfo targetLocationInfo = _locationInfoService.Repository.QueryFirst(x => x.LocationCode == task.TargetAddress);
                if (targetLocationInfo == null)
                {
                    return content.Error($"未找到对应的终点货位[{task.TargetAddress}]信息");
                }
                // åŽŸè´§ä½ä¿¡æ¯
                Dt_LocationInfo oldLocationInfo = null;
                if (!string.IsNullOrEmpty(stockInfo.LocationCode))
                {
                    oldLocationInfo = _locationInfoService.Repository.QueryFirst(x => x.LocationCode == stockInfo.LocationCode);
                    if (oldLocationInfo == null)
                    {
                        return content.Error($"未找到原货位[{stockInfo.LocationCode}]信息");
                    }
                }
                // 4. è´§ä½çŠ¶æ€æ ¡éªŒ
                if (targetLocationInfo.LocationStatus == LocationStatusEnum.InStock.ObjToInt())
                {
                    return WebResponseContent.Instance.Error($"目标货位[{task.TargetAddress}]状态不正确(当前为已占用)");
                }
                // 5. å¼€å¯äº‹åŠ¡å¤„ç†æ ¸å¿ƒé€»è¾‘
                _unitOfWorkManage.BeginTran();
                // 5.1 è®°å½•目标货位原状态,更新为占用
                var beforeTargetLocationStatus = targetLocationInfo.LocationStatus;
                targetLocationInfo.LocationStatus = LocationStatusEnum.InStock.ObjToInt();
                _locationInfoService.Repository.UpdateData(targetLocationInfo);
                // 5.2 é‡Šæ”¾åŽŸè´§ä½
                int beforeOldLocationStatus = 0;
                if (oldLocationInfo != null)
                {
                    beforeOldLocationStatus = oldLocationInfo.LocationStatus;
                    // åŽŸè´§ä½æ¢å¤ä¸ºç©ºé—²
                    oldLocationInfo.LocationStatus = stockInfo.PalletType == PalletTypeEnum.Empty.ObjToInt()
                        ? LocationStatusEnum.Pallet.ObjToInt()
                        : LocationStatusEnum.Free.ObjToInt();
                    _locationInfoService.Repository.UpdateData(oldLocationInfo);
                }
                var stockLockInfo = _outStockLockInfoService.Db.Queryable<Dt_OutboundLockInfo_Hty>().Where(x => x.PalletCode == stockInfo.PalletCode && x.TaskNum == task.TaskNum).First();
                var allocateOrderToWarehouse = _allocateOrderRepository.Db.Queryable<Dt_AllocateOrder>().Where(x => x.OrderNo == stockLockInfo.OrderNo).First();
                // 5.3 æ›´æ–°åº“存明细状态(移库完成)
                stockInfo.Details.ForEach(x =>
                {
                    x.Status = StockStatusEmun.入库完成.ObjToInt();
                    x.WarehouseCode = allocateOrderToWarehouse.ToWarehouse;
                });
                _stockService.StockInfoDetailService.Repository.UpdateData(stockInfo.Details);
                // 5.4 æ›´æ–°åº“存主信息(绑定新货位)
                string oldLocationCode = stockInfo.LocationCode; // è®°å½•原货位
                stockInfo.LocationCode = targetLocationInfo.LocationCode; // ç»‘定目标货位
                stockInfo.PalletCode = task.PalletCode;
                stockInfo.StockStatus = StockStatusEmun.入库完成.ObjToInt();
                var name =_warehouseAreaRepository.Db.Queryable<Dt_WarehouseArea>().Where(x => x.Code == allocateOrderToWarehouse.ToWarehouse).First();
                var locationType =_locationTypeRepository.QueryFirst(x => x.LocationTypeDesc.Equals(name.Name));
                if(locationType != null)
                {
                    stockInfo.LocationType = locationType.LocationType;
                }
                _stockRepository.UpdateData(stockInfo);
                // 5.5 æ›´æ–°ä»»åŠ¡çŠ¶æ€ä¸ºå®Œæˆ
                task.TaskStatus = TaskStatusEnum.Finish.ObjToInt();
                var result = _task_HtyService.DeleteAndMoveIntoHty(task, OperateTypeEnum.自动完成);
                // æäº¤äº‹åŠ¡
                _unitOfWorkManage.CommitTran();
                // ä»»åŠ¡å½’æ¡£å¤±è´¥åˆ™ç›´æŽ¥åˆ é™¤
                if (!result)
                {
                    await Db.Deleteable(task).ExecuteCommandAsync();
                }
                // 6. å¤„理出库单+调拨物料信息
                Dt_OutboundOrder outboundOrder = null;
                //var firstDetail = stockInfo.Details.FirstOrDefault();
                if (stockLockInfo != null && !string.IsNullOrEmpty(stockLockInfo.OrderNo))
                {
                    outboundOrder = _outboundOrderService.Db.Queryable<Dt_OutboundOrder>()
                        .Where(x => x.OrderNo == stockLockInfo.OrderNo)
                        .Includes(x => x.Details)
                        .First();
                    if (outboundOrder != null)
                    {
                        var allocatInfo =_allocateMaterialInfo.Db.Queryable<Dt_AllocateMaterialInfo>().Where(x => x.OrderNo == outboundOrder.OrderNo).ToList();
                        // åˆ é™¤è°ƒæ‹¨ç‰©æ–™ä¿¡æ¯
                        foreach (var item in allocatInfo)
                        {
                            var inbounddetail = _allocateMaterialInfo.QueryFirst(x => x.OrderNo == item.OrderNo && x.Barcode == item.Barcode);
                            if (inbounddetail != null)
                            {
                                var alldelete = _allocateMaterialInfo.DeleteAndMoveIntoHty(inbounddetail, OperateTypeEnum.自动删除);
                                if (!alldelete)
                                {
                                    await Db.Deleteable(task).ExecuteCommandAsync();
                                }
                            }
                        }
                        // 7. å›žè°ƒMES
                        string Operator = outboundOrder.Modifier;
                        HttpResponseResult<MesResponseDTO> httpResponseResult = new HttpResponseResult<MesResponseDTO>();
                        string reqCode = Guid.NewGuid().ToString();
                        string reqTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
                        string requestData = string.Empty;
                        List<string> lineNos = new List<string>();
                        Dt_AllocateMaterialInfo allocateMaterialInfo = _allocateMaterialInfo.QueryFirst(x => x.OrderNo == outboundOrder.OrderNo);
                        // ç§»åº“场景:出库完成且未回传MES、无调拨物料信息时回调
                        if (outboundOrder.OrderStatus == OutOrderStatusEnum.出库完成.ObjToInt() && outboundOrder.ReturnToMESStatus == 0 && allocateMaterialInfo == null)
                        {
                            Dt_AllocateOrder allocateOrder = _allocateOrderRepository.QueryFirst(x => x.OrderNo == outboundOrder.OrderNo);
                            if (allocateOrder == null)
                            {
                                return WebResponseContent.Instance.Error($"未找到对应的调拨单[{outboundOrder.OrderNo}]");
                            }
                            // æž„建移库回调数据
                            AllocationReturnDTO? returnDTO = BuildAllocationFeedbackData(
                                outboundOrder,
                                allocateOrder.FromWarehouse,
                                allocateOrder.ToWarehouse,
                                Operator);
                            if (returnDTO == null)
                            {
                                return WebResponseContent.Instance.Error($"构建移库回调对象失败");
                            }
                            string apiUrl = AppSettings.GetValue("AllocationFeedbackUrl");
                            returnDTO.ReqCode = reqCode;
                            returnDTO.ReqTime = reqTime;
                            JsonSerializerSettings settings = new JsonSerializerSettings
                            {
                                ContractResolver = new CamelCasePropertyNamesContractResolver()
                            };
                            requestData = JsonConvert.SerializeObject(returnDTO, settings);
                            lineNos = returnDTO.Details.Select(x => x.LineNo).ToList();
                            httpResponseResult = _httpClientHelper.Post<MesResponseDTO>(apiUrl, requestData);
                            httpResponseResult.ApiUrl = apiUrl;
                        }
                        // 8. å¤„理MES回调结果
                        bool isSuccess = httpResponseResult.IsSuccess && httpResponseResult.Data?.Code == "200";
                        string message = "成功";
                        if (!isSuccess)
                        {
                            if (!httpResponseResult.IsSuccess)
                            {
                                message = $"MES接口返回错误,HTTP代码:{httpResponseResult.StatusCode},信息:{httpResponseResult.ErrorMessage}";
                            }
                            else if (httpResponseResult?.Data?.Code != "200")
                            {
                                message = $"调用MES接口失败,代码:{httpResponseResult?.Data?.Code},信息:{httpResponseResult?.Data?.Message}";
                            }
                        }
                        // 9. è®°å½•MES回传记录
                        Dt_MesReturnRecord mesReturnRecord = new Dt_MesReturnRecord()
                        {
                            ApiUrl = httpResponseResult.ApiUrl,
                            InterfaceType = outboundOrder.OrderType == 0 ? 1 : 3, // ç§»åº“接口类型:复用调拨类型3
                            OrderId = outboundOrder.Id,
                            OrderNo = outboundOrder.OrderNo,
                            RequestCode = reqCode,
                            RequestData = requestData,
                            FailureReason = message,
                            LastReturnTime = DateTime.Now,
                            HttpStatusCode = httpResponseResult.StatusCode.ObjToInt(),
                            ResponseData = httpResponseResult.Content,
                            ReturnType = 0,
                            ReturnCount = 1,
                            ReturnStatus = isSuccess ? 1 : 2,
                            SuccessTime = isSuccess ? DateTime.Now : null
                        };
                        // å¼€å¯äº‹åŠ¡ä¿å­˜MES记录+更新出库单状态
                        _unitOfWorkManage.BeginTran();
                        _unitOfWorkManage.Db.Insertable(mesReturnRecord).ExecuteCommand();
                        List<Dt_OutboundOrderDetail> outboundOrderDetails = outboundOrder.Details.Where(x => lineNos.Contains(x.lineNo)).ToList();
                        outboundOrderDetails.ForEach(x =>
                        {
                            if (x.OverOutQuantity == x.OrderQuantity - x.MoveQty)
                            {
                                x.ReturnToMESStatus = isSuccess ? 1 : 2;
                            }
                            else
                            {
                                x.ReturnToMESStatus = isSuccess ? 3 : 4;
                            }
                            x.CurrentDeliveryQty = 0;
                            x.ReturnJsonData = "";
                        });
                        mesReturnRecord.ReturnType = outboundOrder.Details.Count == outboundOrderDetails.Count ? 1 : 2;
                        if (outboundOrder.Details.Count == outboundOrderDetails.Count && outboundOrderDetails.All(x => x.ReturnToMESStatus == 1 || x.ReturnToMESStatus == 2))
                        {
                            outboundOrder.ReturnToMESStatus = isSuccess ? 1 : 2;
                        }
                        else
                        {
                            outboundOrder.ReturnToMESStatus = isSuccess ? 3 : 4;
                        }
                        _outboundOrderService.Db.Updateable(outboundOrderDetails).ExecuteCommand();
                        _outboundOrderService.UpdateData(outboundOrder);
                        _unitOfWorkManage.CommitTran();
                    }
                }
                // 10. è®°å½•货位状态变更(目标货位+原货位)
                try
                {
                    // è®°å½•目标货位状态变更
                    _locationStatusChangeRecordService.AddLocationStatusChangeRecord(
                        targetLocationInfo,
                        beforeTargetLocationStatus,
                        StockChangeType.Inbound.ObjToInt(),
                        $"移库入库(原货位:{oldLocationCode})",
                        task.TaskNum);
                    // è®°å½•原货位状态变更(若有)
                    if (oldLocationInfo != null)
                    {
                        _locationStatusChangeRecordService.AddLocationStatusChangeRecord(
                            oldLocationInfo,
                            beforeOldLocationStatus,
                            StockChangeType.Outbound.ObjToInt(),
                            $"移库出库(目标货位:{targetLocationInfo.LocationCode})",
                            task.TaskNum);
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogInformation($"RelocationTaskCompleted AddLocationStatusChangeRecord : {ex.Message} ");
                }
                return content;
            }
            catch (Exception ex)
            {
                // äº‹åŠ¡å›žæ»š
                _unitOfWorkManage.RollbackTran();
                _logger.LogError($"RelocationTaskCompleted å¤„理失败:{ex.Message}", ex);
                return await Task.FromResult(WebResponseContent.Instance.Error($"移库任务处理失败:{ex.Message}"));
            }
        }
        public HttpResponseResult<MesResponseDTO> responseModel(Dt_InboundOrder order, int InterfaceType, FeedbackInboundRequestModel model = null, AllocateDto allocateDto = null)
        {
            HttpResponseResult<MesResponseDTO> httpResponseResult = new HttpResponseResult<MesResponseDTO>();
@@ -529,7 +821,7 @@
                ResponseData = httpResponseResult.Content,
                ReturnType = 0,
                ReturnCount = 1,
                ReturnStatus = httpResponseResult.IsSuccess ? 1 : 2,
                ReturnStatus = isSuccess ? 1 : 2,
                SuccessTime = httpResponseResult.IsSuccess ? DateTime.Now : null
            };
            _unitOfWorkManage.Db.Insertable(mesReturnRecord).ExecuteCommand();
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs
@@ -4,6 +4,7 @@
using OfficeOpenXml.FormulaParsing.Excel.Functions.RefAndLookup;
using SqlSugar;
using System;
using System.Collections;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
@@ -43,12 +44,47 @@
            WebResponseContent content = new WebResponseContent();
            try
            {
                var stockInfos = _stockRepository.Db.Queryable<Dt_StockInfo>().Where(x => x.PalletType == PalletTypeEnum.Empty.ObjToInt() && x.StockStatus == StockStatusEmun.入库完成.ObjToInt()).WhereIF(locationType != 0, x => x.LocationType == locationType).Take(num).ToList();
                Dictionary<string, SqlSugar.OrderByType> orderByDict = new Dictionary<string, SqlSugar.OrderByType>()
                {
                     { nameof(Dt_LocationInfo.Layer), SqlSugar.OrderByType.Asc },
                     { nameof(Dt_LocationInfo.Row), SqlSugar.OrderByType.Asc },
                     { nameof(Dt_LocationInfo.Column), SqlSugar.OrderByType.Asc },
                     { nameof(Dt_LocationInfo.Depth), SqlSugar.OrderByType.Desc },
                };
                if (stockInfos.Count() == 0)
                var query = _stockRepository.Db.Queryable<Dt_StockInfo>()
                    .Where(x => x.PalletType == PalletTypeEnum.Empty.ObjToInt()
                             && x.StockStatus == StockStatusEmun.入库完成.ObjToInt())
                    .WhereIF(locationType != 0, x => x.LocationType == locationType)
                    .LeftJoin<Dt_LocationInfo>((s, l) => s.LocationCode == l.LocationCode);
                if (query.Count() == 0)
                {
                    return WebResponseContent.Instance.Error("未找到空托盘库存");
                }
                bool isFirstOrder = true;
                foreach (var item in orderByDict)
                {
                    string fieldName = item.Key.Equals("Column", StringComparison.OrdinalIgnoreCase)
                        ? $"l.[{item.Key}]"
                        : $"l.{item.Key}";
                    string sortSql = $"{fieldName} {(item.Value == SqlSugar.OrderByType.Asc ? "ASC" : "DESC")}";
                    if (isFirstOrder)
                    {
                        query = query.OrderBy(sortSql);
                        isFirstOrder = false;
                    }
                    else
                    {
                        query = query.OrderBy(sortSql);
                    }
                }
                var stockInfos = await query.Take(num).ToListAsync();
                foreach (var stockInfo in stockInfos)
                {
                    Dt_LocationInfo locationInfo = _locationInfoService.Repository.QueryFirst(x => x.LocationCode == stockInfo.LocationCode);
@@ -97,6 +133,8 @@
            }
        }
        /// <summary>
        /// å‡ºåº“任务数据处理