1
z8018
2025-12-17 68389acdc272b78f1cc4d80180d4650e6254edcf
1
已添加4个文件
已修改19个文件
1729 ■■■■■ 文件已修改
项目代码/WIDESEA_WMSClient/src/extension/basic/extend/printView.vue 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/outbound/extend/printView.vue 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/extension/outbound/outboundOrder.js 33 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/views/outbound/out copyPicking.vue 805 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WIDESEA_WMSClient/src/views/outbound/outPicking.vue 277 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/CopilotIndices/17.14.1204.46620/CodeChunks.db 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/CopilotIndices/17.14.1204.46620/SemanticSymbols.db 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_BasicService/BasicService.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_BasicService/MESOperation/FeedbackMesService.cs 205 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Core/Middlewares/ApiLogMiddleware.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Core/Seed/DBSeed.cs 54 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Core/Util/HttpClientHelper.cs 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Core/Util/HttpResponseResult.cs 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_DTO/Base/UnitConvertResultDTO.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_DTO/ReturnMES/AllocationReturnDTO.cs 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_DTO/ReturnMES/FeedbackMesResult.cs 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_IBasicService/MESOperation/IFeedbackMesService.cs 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_OutboundOrderDetail.cs 14 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderDetailService.cs 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundQueryService.cs 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundService.cs 42 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Basic/MesFeedbackController.cs 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目代码/WMS无仓储版/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Outbound/OutboundController.cs 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/basic/extend/printView.vue
@@ -59,13 +59,97 @@
    print() {
      let printContent = document.getElementById("printContent");
      let palletcode=document.getElementById("palletcode");
      var printWindow = window.open("", "");
      printWindow.document.write(printContent.innerHTML);
      printWindow.document.write(palletcode.innerHTML);
      var printWindow = window.open("", "", "width=400,height=400");
      // åˆ›å»ºå®Œæ•´çš„HTML结构以避免空白页
      printWindow.document.write(`
        <!DOCTYPE html>
        <html>
        <head>
          <meta charset="UTF-8">
          <title>打印</title>
          <style>
            @page {
              size: auto;
              margin: 0;
            }
            body {
              margin: 0;
              padding: 10px;
              font-family: Arial, sans-serif;
              background: white;
            }
            .print-container {
              width: 100%;
              height: 100vh;
              display: flex;
              flex-direction: column;
              justify-content: center;
              align-items: center;
              page-break-after: avoid;
            }
            .qrcode-container {
              display: flex;
              justify-content: center;
              align-items: center;
              margin-bottom: 20px;
            }
            .pallet-code {
              display: flex;
              justify-content: center;
              align-items: center;
              font-size: 18px;
              font-weight: bold;
            }
            canvas {
              display: block !important;
              margin: auto !important;
            }
            @media print {
              body {
                margin: 0;
                padding: 5mm;
              }
              .print-container {
                width: 100%;
                height: 100vh;
                page-break-after: avoid;
                page-break-inside: avoid;
              }
            }
          </style>
        </head>
        <body>
          <div class="print-container">
            <div class="qrcode-container">
              ${printContent.innerHTML}
            </div>
            <div class="pallet-code">
              ${palletcode.innerHTML}
            </div>
          </div>
          <script>
            window.onload = function() {
              setTimeout(function() {
                window.print();
                window.close();
              }, 500);
            };
          <\/script>
        </body>
        </html>
      `);
      printWindow.document.close();
      printWindow.focus();
      printWindow.print();
      printWindow.close();
      this.http
            .post("api/palletCodeInfo/PrintStatusUp?printCode="+this.palletCode, null, "数据处理中")
            .then((x) => {
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/outbound/extend/printView.vue
@@ -192,6 +192,7 @@
                        return
                    }
                    printWindow.document.write(`
            <!DOCTYPE html>
            <html>
@@ -235,27 +236,40 @@
                    margin: 0 !important;
                    padding: 0 !important;
                  }
                  body {
                  html {
                    height: auto !important;
                    width: 80mm !important;
                    height: 60mm !important;
                    margin: 0 !important;
                    padding: 0 !important;
                    background: white !important;
                  }
                  body {
                    height: auto !important;
                    width: 80mm !important;
                    margin: 0 !important;
                    padding: 0 !important;
                    background: white !important;
                    overflow: visible !important;
                    min-height: 0 !important;
                    max-height: none !important;
                  }
                  .print-page {
                    width: 80mm !important;
                    height: 60mm !important;
                    page-break-after: always !important;
                    margin: 0 !important;
                    padding: 1mm !important;
                    display: block !important;
                    background: white !important;
                    page-break-inside: avoid !important;
                    position: relative !important;
                    page-break-after: always !important;
                  }
                  .print-page:last-child {
                    page-break-after: avoid !important;
                    page-break-after: auto !important;
                  }
                  
                  .material-card {
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/outbound/outboundOrder.js
@@ -340,18 +340,21 @@
          if (!selectedRows || selectedRows.length === 0) {
            return _this.$Message.warning('请先选择需要处理的单据');
          }
          const requestParams = {
            orderNos: selectedRows.map(row => row.orderNo),
            inout: 2
          };
          if (selectedRows.length > 1) {
            return _this.$Message.warning('请选择一条数据');
          }
          // const requestParams = {
          //   orderNos: selectedRows.map(row => row.orderNo),
          //   inout: 2
          // };
          _this.http
            .post("api/InboundOrder/BatchOrderFeedbackToMes", requestParams, "数据处理中...")
            .post(`api/MesFeedback/OutboundFeedback?orderNo=${selectedRows[0].orderNo}`, {}, "数据处理中...")
            .then((x) => {
              if (x.status) {
                _this.$Message.success('分批出库回调完成');
                _this.refresh();
              } else {
                return _this.$Message.error( '分批出库回调失败');
                return _this.$Message.error('分批出库回调失败');
              }
            })
            .catch((error) => {
@@ -367,14 +370,14 @@
        }
      }
      var EmptyTrayOutboundBtn = this.buttons.find(x => x.value == "EmptyTrayOutbound");
        if (EmptyTrayOutboundBtn != null) {
          EmptyTrayOutboundBtn.onClick = () => {
               this.$refs.gridFooter.open();
            }
      if (EmptyTrayOutboundBtn != null) {
        EmptyTrayOutboundBtn.onClick = () => {
          this.$refs.gridFooter.open();
        }
      }
    },
    onInited() {
@@ -383,7 +386,7 @@
      //this.detailOptions.columns.forEach(column=>{ });
    },
    searchBefore(param) {
      //界面查询前,可以给param.wheres添加查询参数
      //返回false,则不会执行查询
      return true;
@@ -406,9 +409,9 @@
    },
    modelOpenAfter(row) {
      if (this.currentAction === 'Add') { // åˆ¤æ–­å½“前是新建操作
                const currentUser = this.$store.state.userInfo?.userTrueName || 'system';
                this.editFormFields.operator = currentUser;
            }
        const currentUser = this.$store.state.userInfo?.userTrueName || 'system';
        this.editFormFields.operator = currentUser;
      }
      //点击编辑、新建按钮弹出框后,可以在此处写逻辑,如,从后台获取数据
      //(1)判断是编辑还是新建操作: this.currentAction=='Add';
      //(2)给弹出框设置默认值
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/outbound/out copyPicking.vue
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,805 @@
<template>
    <div class="picking-container">
        <!-- é¡¶éƒ¨è®¢å•信息 -->
        <el-card class="order-info-card" shadow="never">
            <div class="order-header">
                <div class="order-title">
                    <i class="el-icon-document"></i>
                    <span class="order-label">订单号:</span>
                    <span class="order-value">{{ orderNo }}</span>
                </div>
                <div class="order-status" v-if="orderInfo">
                    <el-tag :type="getStatusType(orderInfo.status)" size="medium">
                        {{ orderInfo.statusName || '进行中' }}
                    </el-tag>
                </div>
            </div>
        </el-card>
        <!-- æ‰«ç æ“ä½œåŒºåŸŸ -->
        <el-card class="scan-section-card" shadow="never">
            <div class="scan-section">
                <el-alert title="请使用扫码枪扫描,支持回车自动确认" type="info" :closable="false" show-icon class="scan-alert">
                    <template #default>
                        <span>1. è¯·å…ˆæ‰«ææ‰˜ç›˜ç  â†’ 2. å†æ‰«æç‰©æ–™æ¡ç </span>
                    </template>
                </el-alert>
                <el-form :model="scanForm" :rules="scanRules" ref="scanFormRef" class="scan-form">
                    <el-row :gutter="20">
                        <el-col :span="8">
                            <el-form-item label="托盘码" prop="palletCode">
                                <el-input ref="palletInput" v-model="scanForm.palletCode" placeholder="请扫描托盘码"
                                    size="large" clearable @keyup.enter="handlePalletScan">
                                    <template #prefix>
                                        <i class="el-icon-box"></i>
                                    </template>
                                </el-input>
                            </el-form-item>
                        </el-col>
                        <el-col :span="8">
                            <el-form-item label="物料条码" prop="materialBarcode">
                                <el-input ref="materialInput" v-model="scanForm.materialBarcode" placeholder="请扫描物料条码"
                                    size="large" clearable :disabled="unpickedData.length <= 0"
                                    @keyup.enter="handleMaterialScan">
                                    <template #prefix>
                                        <i class="el-icon-s-grid"></i>
                                    </template>
                                </el-input>
                            </el-form-item>
                        </el-col>
                        <el-col :span="8">
                            <el-form-item class="button-form-item">
                                <div class="action-buttons">
                                    <el-button type="primary" size="large" @click="handleConfirmPick"
                                        :loading="confirmLoading" :disabled="!canConfirm">
                                        <i class="el-icon-check"></i>
                                        ç¡®è®¤æ‹£é€‰
                                    </el-button>
                                    <el-button type="warning" size="large" @click="handleEmptyBox"
                                        :disabled="!scanForm.palletCode">
                                        <i class="el-icon-delete"></i>
                                        å–空箱
                                    </el-button>
                                    <el-button type="success" size="large" @click="handleReturnToWarehouse"
                                        :disabled="!scanForm.palletCode">
                                        <i class="el-icon-refresh-left"></i>
                                        å›žåº“
                                    </el-button>
                                </div>
                            </el-form-item>
                        </el-col>
                    </el-row>
                </el-form>
            </div>
        </el-card>
        <!-- æ•°æ®åˆ—表区域 -->
        <div class="tables-section">
            <el-row :gutter="20">
                <!-- æœªæ‹£é€‰åˆ—表 -->
                <el-col :span="12">
                    <el-card class="table-card" shadow="never">
                        <template #header>
                            <div class="card-header">
                                <span class="card-title">
                                    <i class="el-icon-time"></i>
                                    æœªæ‹£é€‰åˆ—表
                                </span>
                                <el-badge :value="unpickedCount" class="badge-item" type="warning">
                                    <el-button size="small" @click="refreshUnpickedTable" icon="el-icon-refresh">
                                        åˆ·æ–°
                                    </el-button>
                                </el-badge>
                            </div>
                        </template>
                        <el-table ref="unpickedTable" :data="unpickedData" height="400" stripe highlight-current-row>
                            <el-table-column type="index" label="序号" width="60" align="center" />
                            <el-table-column prop="materielCode" label="物料编码" width="120" show-overflow-tooltip />
                            <el-table-column prop="materielName" label="物料名称" width="80" show-overflow-tooltip />
                            <el-table-column prop="batchNo" label="批次号" width="100" />
                            <el-table-column prop="assignQuantity" label="分拣数量" width="80" align="right">
                                <template #default="scope">
                                    <el-text type="danger">{{ scope.row.assignQuantity }}</el-text>
                                </template>
                            </el-table-column>
                            <el-table-column prop="sortedQuantity" label="已分拣数量" width="120" align="right">
                                <template #default="scope">
                                    <el-text type="danger">{{ scope.row.sortedQuantity }}</el-text>
                                </template>
                            </el-table-column>
                            <el-table-column prop="unit" label="单位" width="60" />
                            <el-table-column prop="locationCode" label="库位" />
                            <!-- <el-table-column label="操作" width="80" align="center">
                                <template #default="scope">
                                    <el-button type="text" size="small" @click="quickPick(scope.row)"
                                        :disabled="!scanForm.palletCode">
                                        æ‹£é€‰
                                    </el-button>
                                </template>
                            </el-table-column> -->
                        </el-table>
                        <div class="table-footer">
                            <el-descriptions :column="2" size="small">
                                <el-descriptions-item label="总条数">
                                    <el-text type="info">{{ unpickedTotal }}</el-text>
                                </el-descriptions-item>
                                <el-descriptions-item label="总数量">
                                    <el-text type="warning">{{ unpickedQuantity }}</el-text>
                                </el-descriptions-item>
                            </el-descriptions>
                        </div>
                    </el-card>
                </el-col>
                <!-- å·²æ‹£é€‰åˆ—表 -->
                <el-col :span="12">
                    <el-card class="table-card" shadow="never">
                        <template #header>
                            <div class="card-header">
                                <span class="card-title">
                                    <i class="el-icon-circle-check"></i>
                                    å·²æ‹£é€‰åˆ—表
                                </span>
                                <el-badge :value="pickedCount" class="badge-item" type="success">
                                    <el-button size="small" @click="refreshPickedTable" icon="el-icon-refresh">
                                        åˆ·æ–°
                                    </el-button>
                                </el-badge>
                            </div>
                        </template>
                        <el-table ref="pickedTable" :data="pickedData" height="400" stripe>
                            <el-table-column type="index" label="序号" width="60" align="center" />
                            <el-table-column prop="materielCode" label="物料编码" width="120" />
                            <el-table-column prop="materielName" label="物料名称" show-overflow-tooltip />
                            <el-table-column prop="batchNo" label="批次号" width="100" />
                            <el-table-column prop="changeQuantity" label="拣选数量" width="80" align="right">
                                <template #default="scope">
                                    <el-text type="success">{{ 0 - scope.row.changeQuantity }}</el-text>
                                </template>
                            </el-table-column>
                            <el-table-column prop="changeQuantity" label="原库存量" width="80" align="right">
                                <template #default="scope">
                                    <el-text type="success">{{ scope.row.beforeQuantity }}</el-text>
                                </template>
                            </el-table-column>
                            <el-table-column prop="changeQuantity" label="拣选后库存量" width="80" align="right">
                                <template #default="scope">
                                    <el-text type="success">{{ scope.row.afterQuantity }}</el-text>
                                </template>
                            </el-table-column>
                            <el-table-column prop="unit" label="单位" width="60" />
                            <el-table-column prop="palletCode" label="托盘码" width="100" />
                            <el-table-column prop="createDate" label="拣选时间" width="160" />
                            <el-table-column prop="originalBarcode" label="原物料码" width="160" />
                            <el-table-column prop="newBarcode" label="新物料码" width="160" />
                            <!-- <el-table-column label="操作" width="80" align="center">
                                <template #default="scope">
                                    <el-button type="text" size="small" @click="undoPick(scope.row)">
                                        æ’¤é”€
                                    </el-button>
                                </template>
                            </el-table-column> -->
                        </el-table>
                        <div class="table-footer">
                            <el-descriptions :column="2" size="small">
                                <el-descriptions-item label="总条数">
                                    <el-text type="info">{{ pickedTotal }}</el-text>
                                </el-descriptions-item>
                                <el-descriptions-item label="总数量">
                                    <el-text type="success">{{ pickedQuantity }}</el-text>
                                </el-descriptions-item>
                            </el-descriptions>
                        </div>
                    </el-card>
                </el-col>
            </el-row>
        </div>
        <print-view ref="printView" @parentcall="parentcall"></print-view>
        <!-- ç¡®è®¤å¯¹è¯æ¡† -->
        <el-dialog v-model="confirmDialogVisible" title="操作确认" width="400px" :before-close="handleDialogClose">
            <div class="confirm-content">
                <p>{{ confirmMessage }}</p>
            </div>
            <template #footer>
                <span class="dialog-footer">
                    <el-button @click="confirmDialogVisible = false">取消</el-button>
                    <el-button type="primary" @click="executeConfirm" :loading="executeLoading">
                        ç¡®å®š
                    </el-button>
                </span>
            </template>
        </el-dialog>
    </div>
</template>
<script>
import printView from "@/extension/outbound/extend/printView.vue"
export default {
    components: { printView },
    name: 'OutPicking',
    data() {
        return {
            orderNo: '',
            orderInfo: null,
            scanForm: {
                palletCode: '',
                materialBarcode: ''
            },
            scanRules: {
                palletCode: [
                    { required: true, message: '请扫描托盘码', trigger: 'blur' }
                ],
                materialBarcode: [
                    { required: true, message: '请扫描物料条码', trigger: 'blur' }
                ]
            },
            unpickedData: [],
            pickedData: [],
            unpickedCount: 0,
            unpickedTotal: 0,
            unpickedQuantity: 0,
            pickedCount: 0,
            pickedTotal: 0,
            pickedQuantity: 0,
            confirmLoading: false,
            confirmDialogVisible: false,
            confirmMessage: '',
            currentAction: null,
            executeLoading: false
        }
    },
    computed: {
        canConfirm() {
            return this.scanForm.palletCode && this.scanForm.materialBarcode
        }
    },
    mounted() {
        this.initPage()
    },
    methods: {
        initPage() {
            // ä»Žè·¯ç”±å‚数获取订单号
            this.orderNo = this.$route.query.orderNo || ''
            if (!this.orderNo) {
                this.$message.error('订单号不能为空')
                this.$router.back()
                return
            }
            // è‡ªåŠ¨èšç„¦åˆ°æ‰˜ç›˜ç è¾“å…¥æ¡†
            this.$nextTick(() => {
                if (this.$refs.palletInput) {
                    this.$refs.palletInput.focus()
                }
            })
        },
        loadPalletData() {
            if (!this.scanForm.palletCode) {
                this.unpickedData = []
                return
            }
            try {
                this.loadUnpickedData();
                this.loadPickedData();
            } catch (error) {
                console.error('加载托盘数据失败:', error)
                this.unpickedData = []
            }
        },
        loadUnpickedData() {
            try {
                this.http.post(`/api/Outbound/QueryPickingTasks?orderNo=${this.orderNo}&palletCode=${this.scanForm.palletCode}`, {}).then(response => {
                    if (response.status) {
                        if (response.data.length > 0) {
                            this.unpickedData = response.data
                            this.calculateUnpickedStats()
                            // è‡ªåŠ¨èšç„¦åˆ°ç‰©æ–™æ¡ç è¾“å…¥æ¡†
                            this.$nextTick(() => {
                                if (this.$refs.materialInput) {
                                    this.$refs.materialInput.focus()
                                }
                            })
                        } else {
                            this.$message.warning('该托盘无未拣选任务')
                            this.unpickedData = []
                        }
                    } else {
                        this.$message.error(response.message || '获取托盘数据失败')
                        this.unpickedData = []
                    }
                }
                )
            } catch (error) {
                console.error('加载未拣选数据失败:', error)
            }
        },
        loadPickedData() {
            try {
                this.http.post(`/api/Outbound/QueryPickedList?orderNo=${this.orderNo}&palletCode=${this.scanForm.palletCode}`, {}).then(response => {
                    if (response.status) {
                        if (response.data.length > 0) {
                            this.pickedData = response.data
                            this.calculatePickedStats()
                        } else {
                            this.pickedData = []
                        }
                    } else {
                        this.$message.error(response.message || '获取托盘数据失败')
                        this.pickedData = []
                    }
                }
                )
            } catch (error) {
                console.error('加载已拣选数据失败:', error)
            }
        },
        // è®¡ç®—未拣选
        calculateUnpickedStats() {
            // æœªæ‹£é€‰æ¡ç›®æ•°é‡
            this.unpickedCount = this.unpickedData.length
            // è®¡ç®—未拣选的总数量(分拣数量 - å·²åˆ†æ‹£æ•°é‡ï¼‰
            this.unpickedQuantity = this.unpickedData.reduce((sum, item) => {
                const assignQty = item.assignQuantity || 0
                const sortedQty = item.sortedQuantity || 0
                const remainingQty = Math.max(0, assignQty - sortedQty)
                return sum + remainingQty
            }, 0)
            // æ€»æ¡ç›®æ•°ï¼ˆä¸Žæ¡ç›®æ•°é‡ç›¸åŒï¼Œè¿™é‡Œä¿ç•™å˜é‡ä¸€è‡´æ€§ï¼‰
            this.unpickedTotal = this.unpickedCount
        },
        // è®¡ç®—已拣选
        calculatePickedStats() {
            // å·²æ‹£é€‰æ¡ç›®æ•°é‡
            this.pickedCount = this.pickedData.length
            // è®¡ç®—已拣选的总数量
            this.pickedQuantity = 0 - this.pickedData.reduce((sum, item) => {
                return (sum + item.changeQuantity)
            }, 0)
            // æ€»æ¡ç›®æ•°ï¼ˆä¸Žæ¡ç›®æ•°é‡ç›¸åŒï¼‰
            this.pickedTotal = this.pickedCount
        },
        handlePalletScan() {
            if (this.scanForm.palletCode) {
                // this.$message.success(`托盘码: ${this.scanForm.palletCode}`)
                this.loadPalletData()
            }
        },
        handleMaterialScan() {
            if (!this.scanForm.palletCode) {
                this.$message.warning('请先扫描托盘码')
                this.$refs.palletInput.focus()
                return
            }
            if (!this.scanForm.materialBarcode) {
                this.$message.warning('请扫描物料条码')
                return
            }
            // è‡ªåŠ¨æ‰§è¡Œç¡®è®¤æ‹£é€‰
            this.handleConfirmPick()
        },
        handleConfirmPick() {
            if (!this.scanForm.palletCode || !this.scanForm.materialBarcode) {
                this.$message.warning('请先扫描托盘码和物料条码')
                return
            }
            this.confirmLoading = true
            try {
                this.http.post('/api/Outbound/CompleteOutboundWithBarcode', {
                    orderNo: this.orderNo,
                    palletCode: this.scanForm.palletCode,
                    barcode: this.scanForm.materialBarcode,
                    operator: this.getUserName()
                }).then(response => {
                    if (response.status) {
                        if (response.data.scannedDetail.isUnpacked && response.data.scannedDetail.materialCodes.length > 0) {
                            this.$refs.printView.open(response.data.scannedDetail.materialCodes);
                        }
                        this.$message.success('拣选确认成功')
                        this.resetMaterialBarcode()
                        // this.loadUnpickedData()
                        // this.loadPickedData()
                        this.loadPalletData()
                    } else {
                        this.$message.error(response.message || '拣选确认失败')
                    }
                })
            } catch (error) {
                console.error('拣选确认失败:', error)
                this.$message.error('拣选确认失败')
            } finally {
                this.confirmLoading = false
            }
        },
        handleEmptyBox() {
            if (!this.scanForm.palletCode) {
                this.$message.warning('请先扫描托盘码')
                return
            }
            this.confirmMessage = `确定要取空托盘 ${this.scanForm.palletCode} å—?`
            this.currentAction = 'emptyBox'
            this.confirmDialogVisible = true
        },
        handleReturnToWarehouse() {
            if (!this.scanForm.palletCode) {
                this.$message.warning('请先扫描托盘码')
                return
            }
            this.confirmMessage = `确定要将托盘 ${this.scanForm.palletCode} å›žåº“吗?`
            this.currentAction = 'returnToWarehouse'
            this.confirmDialogVisible = true
        },
        executeConfirm() {
            this.executeLoading = true
            try {
                let apiUrl = ''
                let params = {
                    orderNo: this.orderNo,
                    palletCode: this.scanForm.palletCode
                }
                if (this.currentAction === 'emptyBox') {
                    apiUrl = '/api/Outbound/EmptyBox'
                } else if (this.currentAction === 'returnToWarehouse') {
                    apiUrl = '/api/Outbound/ReturnToWarehouse'
                }
                this.http.post(apiUrl, params).then(response => {
                    if (response.status) {
                        this.$message.success('操作成功')
                        this.confirmDialogVisible = false
                        this.resetForm()
                        // this.loadUnpickedData()
                        // this.loadPickedData()
                    } else {
                        this.$message.error(response.message || '操作失败')
                    }
                })
            } catch (error) {
                console.error('操作失败:', error)
                this.$message.error('操作失败')
            } finally {
                this.executeLoading = false
            }
        },
        handleDialogClose() {
            if (!this.executeLoading) {
                this.confirmDialogVisible = false
            }
        },
        quickPick(row) {
            this.scanForm.materialBarcode = row.materielCode
            this.handleConfirmPick()
        },
        undoPick(row) {
            try {
                this.$confirm('确定要撤销这条拣选记录吗?', '提示', {
                    type: 'warning'
                })
                const response = this.http.post('/api/Outbound/UndoPicking', {
                    id: row.id
                }).then(response => {
                    if (response.status) {
                        this.$message.success('撤销成功')
                        this.loadUnpickedData()
                        this.loadPickedData()
                    } else {
                        this.$message.error(response.message || '撤销失败')
                    }
                })
            } catch (error) {
                if (error !== 'cancel') {
                    console.error('撤销失败:', error)
                    this.$message.error('撤销失败')
                }
            }
        },
        // handleUnpickedRowClick(row) {
        //     // ç‚¹å‡»æœªæ‹£é€‰è¡Œæ—¶è‡ªåŠ¨å¡«å……ç‰©æ–™æ¡ç 
        //     this.scanForm.materialBarcode = row.materielCode
        // },
        refreshUnpickedTable() {
            if (this.scanForm.palletCode) {
                this.loadPalletData()
            }
        },
        refreshPickedTable() {
            this.loadPickedData()
        },
        resetMaterialBarcode() {
            this.scanForm.materialBarcode = ''
            this.$nextTick(() => {
                if (this.$refs.materialInput) {
                    this.$refs.materialInput.focus()
                }
            })
        },
        resetForm() {
            this.scanForm.palletCode = ''
            this.scanForm.materialBarcode = ''
            this.unpickedData = []
            this.$nextTick(() => {
                if (this.$refs.palletInput) {
                    this.$refs.palletInput.focus()
                }
            })
        },
        getUserName() {
            // å°è¯•从 Vuex store èŽ·å–ç”¨æˆ·å
            if (this.$store && this.$store.state && this.$store.state.userInfo) {
                return this.$store.state.userInfo.userName || this.$store.state.userInfo.username || '未登录用户'
            }
            // å°è¯•从 localStorage èŽ·å–
            try {
                const userInfo = localStorage.getItem('user')
                if (userInfo) {
                    const user = JSON.parse(userInfo)
                    return user.userName || user.username || '未登录用户'
                }
            } catch (error) {
                console.error('获取用户信息失败:', error)
            }
            return '未登录用户'
        },
        getStatusType(status) {
            const statusMap = {
                0: 'info',    // å¾…处理
                10: 'warning', // è¿›è¡Œä¸­
                20: 'primary', // æ‹£é€‰ä¸­
                30: 'success', // å·²å®Œæˆ
                40: 'danger'   // å¼‚常
            }
            return statusMap[status] || 'info'
        }
    }
}
</script>
<style scoped>
.picking-container {
    padding: 20px;
    background-color: #f5f5f5;
    min-height: 100vh;
}
/* è®¢å•信息卡片 */
.order-info-card {
    margin-bottom: 20px;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
    color: white;
}
.order-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.order-title {
    display: flex;
    align-items: center;
    font-size: 18px;
    font-weight: bold;
}
.order-title i {
    margin-right: 10px;
    font-size: 24px;
}
.order-label {
    margin-left: 10px;
    opacity: 0.9;
}
.order-value {
    font-size: 20px;
    font-weight: bold;
    letter-spacing: 1px;
}
/* æ‰«ç åŒºåŸŸ */
.scan-section-card {
    margin-bottom: 20px;
}
.scan-section {
    padding: 10px 0;
}
.scan-alert {
    margin-bottom: 20px;
}
.scan-form {
    margin-top: 20px;
}
.button-form-item {
    margin-bottom: 0;
}
.button-form-item .el-form-item__content {
    line-height: normal;
}
.action-buttons {
    display: flex;
    gap: 8px;
    width: 100%;
    height: 40px;
}
.action-buttons .el-button {
    flex: 1;
    height: 40px;
    font-weight: bold;
    border-radius: 6px;
    margin: 0;
}
/* è¡¨æ ¼åŒºåŸŸ */
.tables-section {
    margin-top: 20px;
}
.table-card {
    height: 600px;
    display: flex;
    flex-direction: column;
}
.card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
}
.card-title {
    display: flex;
    align-items: center;
    font-weight: bold;
    font-size: 16px;
}
.card-title i {
    margin-right: 8px;
    color: #409EFF;
}
.badge-item {
    margin-left: 10px;
}
.table-footer {
    margin-top: 10px;
    padding-top: 10px;
    border-top: 1px solid #ebeef5;
}
/* è¡¨æ ¼è¡Œæ ·å¼ */
.el-table tbody tr:hover>td {
    background-color: #f0f9ff !important;
}
.el-table tbody tr.current-row>td {
    background-color: #e1f3ff !important;
}
/* å¯¹è¯æ¡†æ ·å¼ */
.confirm-content {
    padding: 20px 0;
    text-align: center;
}
.confirm-content p {
    font-size: 16px;
    color: #606266;
}
.dialog-footer {
    text-align: center;
}
/* å“åº”式设计 */
@media (max-width: 1200px) {
    .action-buttons .el-button {
        font-size: 14px;
    }
}
/* Element UI ç»„件样式覆盖 */
::v-deep .el-card__header {
    padding: 18px 20px;
    border-bottom: 1px solid #ebeef5;
}
::v-deep .el-input__inner {
    border-radius: 6px;
}
::v-deep .el-button--primary {
    background: linear-gradient(135deg, #409EFF 0%, #3a8ee6 100%);
    border: none;
}
::v-deep .el-button--warning {
    background: linear-gradient(135deg, #E6A23C 0%, #d9971a 100%);
    border: none;
}
::v-deep .el-button--success {
    background: linear-gradient(135deg, #67C23A 0%, #5daf34 100%);
    border: none;
}
/* å›¾æ ‡æ ·å¼ */
.el-icon-document,
.el-icon-box,
.el-icon-s-grid,
.el-icon-check,
.el-icon-delete,
.el-icon-refresh-left,
.el-icon-time,
.el-icon-circle-check {
    font-size: 18px;
}
/* æè¿°åˆ—表样式 */
::v-deep .el-descriptions__label {
    font-weight: bold;
    color: #909399;
}
::v-deep .el-descriptions__content {
    color: #606266;
}
</style>
ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/outbound/outPicking.vue
@@ -21,7 +21,13 @@
            <div class="scan-section">
                <el-alert title="请使用扫码枪扫描,支持回车自动确认" type="info" :closable="false" show-icon class="scan-alert">
                    <template #default>
                        <span>1. è¯·å…ˆæ‰«ææ‰˜ç›˜ç  â†’ 2. å†æ‰«æç‰©æ–™æ¡ç </span>
                        <div>
                            <div>1. è¯·å…ˆæ‰«ææ‰˜ç›˜ç  â†’ 2. å†æ‰«æç‰©æ–™æ ‡ç­¾ç </div>
                            <div style="margin-top: 8px; font-size: 13px; color: #666;">
                                <i class="el-icon-info" style="color: #409EFF;"></i>
                                æ”¯æŒæ‰«æ‰˜ç›˜ç æ•´ç®±å‡ºåº“:扫描托盘码后可直接进行整箱物料拣选,无需再扫描物料标签码。
                            </div>
                        </div>
                    </template>
                </el-alert>
@@ -71,6 +77,41 @@
                        </el-col>
                    </el-row>
                </el-form>
                <!-- åˆ†æ‹£ç»Ÿè®¡ä¿¡æ¯ -->
                <div class="picking-stats" v-if="scanForm.palletCode && unpickedData.length > 0">
                    <el-divider content-position="left">
                        <span style="color: #409EFF; font-size: 14px;">
                            <i class="el-icon-data-analysis"></i> æ‰˜ç›˜åˆ†æ‹£ç»Ÿè®¡
                        </span>
                    </el-divider>
                    <div class="stats-container">
                        <div class="stat-item">
                            <el-tag type="primary" size="medium" effect="dark">
                                <i class="el-icon-s-order"></i>
                                åˆ†æ‹£æ€»æ•°ï¼š<b>{{ calculateTotalAssignQuantity() }}</b>
                            </el-tag>
                        </div>
                        <div class="stat-item">
                            <el-tag type="success" size="medium" effect="dark">
                                <i class="el-icon-circle-check"></i>
                                å·²åˆ†æ‹£ï¼š<b>{{ calculateTotalSortedQuantity() }}</b>
                            </el-tag>
                        </div>
                        <div class="stat-item">
                            <el-tag type="warning" size="medium" effect="dark">
                                <i class="el-icon-time"></i>
                                æœªåˆ†æ‹£ï¼š<b>{{ calculateTotalUnsortedQuantity() }}</b>
                            </el-tag>
                        </div>
                        <div class="stat-item">
                            <el-tag type="success" size="medium" effect="dark">
                                <i class="el-icon-box"></i>
                                æ˜¯å¦æ•´å‡ºï¼š{{ hasWholeOut() ? '是' : '否' }}
                            </el-tag>
                        </div>
                    </div>
                </div>
            </div>
        </el-card>
@@ -198,6 +239,53 @@
                    </el-card>
                </el-col>
            </el-row>
            <!-- æ‰˜ç›˜ç‰©æ–™åº“存信息 -->
            <!-- <div class="pallet-inventory" v-if="scanForm.palletCode && unpickedData.length > 0">
                <el-divider content-position="left">
                    <span style="color: #67C23A; font-size: 14px;">
                        <i class="el-icon-goods"></i> æ‰˜ç›˜ç‰©æ–™åº“存信息
                    </span>
                </el-divider>
                <div class="inventory-container">
                    <el-table :data="unpickedData" size="small" :show-header="true" :border="true" stripe
                        highlight-current-row max-height="200" class="inventory-table">
                        <el-table-column type="index" label="序号" width="50" align="center" />
                        <el-table-column prop="materielCode" label="物料编码" width="100" show-overflow-tooltip />
                        <el-table-column prop="materielName" label="物料名称" width="120" show-overflow-tooltip />
                        <el-table-column prop="batchNo" label="批次号" width="90" />
                        <el-table-column label="当前库存" width="80" align="right">
                            <template #default="scope">
                                <el-text type="primary" tag="b">{{ scope.row.currentStock || 0 }}</el-text>
                            </template>
                        </el-table-column>
                        <el-table-column label="分拣数量" width="80" align="right">
                            <template #default="scope">
                                <el-text type="warning">{{ scope.row.assignQuantity }}</el-text>
                            </template>
                        </el-table-column>
                        <el-table-column label="已分拣" width="70" align="right">
                            <template #default="scope">
                                <el-text type="success">{{ scope.row.sortedQuantity || 0 }}</el-text>
                            </template>
                        </el-table-column>
                        <el-table-column label="剩余库存" width="80" align="right">
                            <template #default="scope">
                                <el-text type="info">{{ calculateRemainingStock(scope.row) }}</el-text>
                            </template>
                        </el-table-column>
                        <el-table-column prop="unit" label="单位" width="100" align="center" />
                        <el-table-column prop="locationCode" label="库位" width="150" />
                        <el-table-column label="状态" width="80" align="center">
                            <template #default="scope">
                                <el-tag :type="getStockStatusType(scope.row)" size="mini">
                                    {{ getStockStatusText(scope.row) }}
                                </el-tag>
                            </template>
                        </el-table-column>
                    </el-table>
                </div>
            </div> -->
        </div>
        <print-view ref="printView" @parentcall="parentcall"></print-view>
@@ -253,7 +341,8 @@
            confirmDialogVisible: false,
            confirmMessage: '',
            currentAction: null,
            executeLoading: false
            executeLoading: false,
            matMixed: true
        }
    },
    computed: {
@@ -300,8 +389,9 @@
            try {
                this.http.post(`/api/Outbound/QueryPickingTasks?orderNo=${this.orderNo}&palletCode=${this.scanForm.palletCode}`, {}).then(response => {
                    if (response.status) {
                        if (response.data.length > 0) {
                            this.unpickedData = response.data
                        if (response.data.outStockLockInfos.length > 0) {
                            this.unpickedData = response.data.outStockLockInfos;
                            this.matMixed = response.data.isMatMixed;
                            this.calculateUnpickedStats()
                            // è‡ªåŠ¨èšç„¦åˆ°ç‰©æ–™æ¡ç è¾“å…¥æ¡†
@@ -589,6 +679,71 @@
            return '未登录用户'
        },
        // è®¡ç®—分拣总数
        calculateTotalAssignQuantity() {
            return this.unpickedData.reduce((sum, item) => {
                return sum + (item.assignQuantity || 0)
            }, 0)
        },
        // è®¡ç®—已分拣总数
        calculateTotalSortedQuantity() {
            return this.unpickedData.reduce((sum, item) => {
                return sum + (item.sortedQuantity || 0)
            }, 0)
        },
        // è®¡ç®—未分拣总数
        calculateTotalUnsortedQuantity() {
            return this.unpickedData.reduce((sum, item) => {
                const assignQty = item.assignQuantity || 0
                const sortedQty = item.sortedQuantity || 0
                return sum + Math.max(0, assignQty - sortedQty)
            }, 0)
        },
        // æ£€æŸ¥æ˜¯å¦åŒ…含整出
        hasWholeOut() {
            return this.unpickedData.some(item => item.assignQuantity === item.originalQuantity) && this.matMixed;
        },
        // è®¡ç®—剩余库存
        calculateRemainingStock(row) {
            const currentStock = row.currentStock || 0
            const assignQty = row.assignQuantity || 0
            return Math.max(0, currentStock - assignQty)
        },
        // èŽ·å–åº“å­˜çŠ¶æ€ç±»åž‹
        getStockStatusType(row) {
            const currentStock = row.currentStock || 0
            const assignQty = row.assignQuantity || 0
            const sortedQty = row.sortedQuantity || 0
            if (sortedQty >= assignQty) {
                return 'success' // å·²å®Œæˆ
            } else if (currentStock < assignQty) {
                return 'danger' // åº“存不足
            } else {
                return 'warning' // è¿›è¡Œä¸­
            }
        },
        // èŽ·å–åº“å­˜çŠ¶æ€æ–‡æœ¬
        getStockStatusText(row) {
            const currentStock = row.currentStock || 0
            const assignQty = row.assignQuantity || 0
            const sortedQty = row.sortedQuantity || 0
            if (sortedQty >= assignQty) {
                return '已完成'
            } else if (currentStock < assignQty) {
                return '库存不足'
            } else {
                return '分拣中'
            }
        },
        getStatusType(status) {
            const statusMap = {
                0: 'info',    // å¾…处理
@@ -802,4 +957,118 @@
::v-deep .el-descriptions__content {
    color: #606266;
}
/* è¡¨æ ¼å¢žå¼ºæ ·å¼ */
::v-deep .el-table th {
    background-color: #fafafa;
    font-weight: 600;
    color: #303133;
}
::v-deep .el-table .el-text {
    font-weight: 500;
}
/* æ ‡ç­¾æ ·å¼å¢žå¼º */
::v-deep .el-tag--small {
    font-weight: 500;
}
/* æç¤ºä¿¡æ¯æ ·å¼å¢žå¼º */
.scan-alert ::v-deep .el-alert__content {
    width: 100%;
}
.scan-alert ::v-deep .el-alert__description {
    margin-top: 8px;
}
/* åˆ†æ‹£ç»Ÿè®¡ä¿¡æ¯æ ·å¼ */
.picking-stats {
    margin-top: 20px;
    padding: 0 10px;
}
.stats-container {
    display: flex;
    flex-wrap: wrap;
    gap: 12px;
    align-items: center;
}
.stat-item {
    display: inline-flex;
    align-items: center;
}
.stat-item .el-tag {
    display: flex;
    align-items: center;
    padding: 6px 12px;
    font-size: 13px;
    border-radius: 20px;
}
.stat-item .el-tag i {
    margin-right: 6px;
    font-size: 14px;
}
.stat-item b {
    margin-left: 4px;
    font-size: 14px;
}
/* åˆ†å‰²çº¿æ ·å¼ */
::v-deep .el-divider__text {
    background-color: #f5f5f5;
    padding: 0 20px;
}
/* æ‰˜ç›˜åº“存信息样式 */
.pallet-inventory {
    margin-top: 20px;
    padding: 0 10px;
}
.inventory-container {
    margin-top: 10px;
}
.inventory-table {
    width: 100%;
}
.inventory-table ::v-deep .el-table__header {
    background-color: #f0f9ff;
}
.inventory-table ::v-deep .el-table__header th {
    background-color: #e1f3ff;
    color: #1f2937;
    font-weight: 600;
    font-size: 12px;
    padding: 8px 0;
}
.inventory-table ::v-deep .el-table__body td {
    padding: 6px 0;
    font-size: 12px;
}
.inventory-table ::v-deep .el-table__row {
    cursor: pointer;
}
.inventory-table ::v-deep .el-table__row:hover {
    background-color: #f0f9ff;
}
/* åº“存表格中的标签样式 */
.inventory-table ::v-deep .el-tag--mini {
    font-size: 11px;
    padding: 1px 6px;
    height: 18px;
    line-height: 16px;
}
</style>
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/CopilotIndices/17.14.1204.46620/CodeChunks.db
Binary files differ
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/.vs/WIDESEA_WMSServer/CopilotIndices/17.14.1204.46620/SemanticSymbols.db
Binary files differ
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_BasicService/BasicService.cs
@@ -109,7 +109,7 @@
                throw new Exception($"转换后单位不能为空");
            }
            decimal ratio = 0;
            decimal ratio = 1;
            if (fromUnit.Trim().ToLower() == toUnit.Trim().ToLower())
            {
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_BasicService/MESOperation/FeedbackMesService.cs
@@ -14,6 +14,7 @@
using WIDESEA_DTO.ReturnMES;
using WIDESEA_IBasicService;
using WIDESEA_Model.Models;
using static HslCommunication.Profinet.Knx.KnxCode;
namespace WIDESEA_BasicService.MESOperation
{
@@ -23,27 +24,189 @@
        private readonly HttpClientHelper _httpClientHelper;
        private readonly IRepository<Dt_OutboundOrder> _outboundOrderRepository;
        private readonly IBasicService _basicService;
        private readonly IRepository<Dt_AllocateOrder> _allocateRepository;
        public FeedbackMesService(IRepository<Dt_MesReturnRecord> BaseDal, IUnitOfWorkManage unitOfWorkManage, HttpClientHelper httpClientHelper, IRepository<Dt_OutboundOrder> outboundOrderRepository, IBasicService basicService) : base(BaseDal)
        public FeedbackMesService(IRepository<Dt_MesReturnRecord> BaseDal, IUnitOfWorkManage unitOfWorkManage, HttpClientHelper httpClientHelper, IRepository<Dt_OutboundOrder> outboundOrderRepository, IBasicService basicService, IRepository<Dt_AllocateOrder> allocateRepository) : base(BaseDal)
        {
            _unitOfWorkManage = unitOfWorkManage;
            _httpClientHelper = httpClientHelper;
            _outboundOrderRepository = outboundOrderRepository;
            _basicService = basicService;
            _allocateRepository = allocateRepository;
        }
        public void MaterialOutboundFeedback(string orderNo)
        public WebResponseContent OutboundFeedback(string orderNo)
        {
            try
            {
                Dt_OutboundOrder outboundOrder = _outboundOrderRepository.Db.Queryable<Dt_OutboundOrder>().Where(x => x.OrderNo == orderNo).Includes(x => x.Details).First();
                if (outboundOrder == null)
                {
                    // todo è®°å½•日志:未找到对应的出库单
                    return;
                    return WebResponseContent.Instance.Error($"未找到对应的出库单信息");
                }
                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>();
                if (outboundOrder.OrderType == 0)
                {
                    MaterialOutboundReturnDTO? returnDTO = BuildOutboundFeedbackData(outboundOrder);
                    if (returnDTO == null)
                    {
                        return WebResponseContent.Instance.Error($"构建回调对象失败");
                    }
                    string apiUrl = "";
                    returnDTO.ReqCode = reqCode;
                    returnDTO.ReqTime = reqTime;
                    requestData = returnDTO.Serialize();
                    lineNos = returnDTO.Details.Select(x => x.LineNo).ToList();
                    httpResponseResult = _httpClientHelper.Post<MesResponseDTO>(apiUrl, requestData);
                    httpResponseResult.ApiUrl = apiUrl;
                }
                else
                {
                    Dt_AllocateOrder allocateOrder = _allocateRepository.QueryFirst(x => x.OrderNo == outboundOrder.OrderNo);
                    if (allocateOrder == null)
                    {
                        return WebResponseContent.Instance.Error($"未找到对应的调拨单");
                    }
                    AllocationReturnDTO? returnDTO = BuildAllocationFeedbackData(outboundOrder, allocateOrder.FromWarehouse, allocateOrder.ToWarehouse);
                    if (returnDTO == null)
                    {
                        return WebResponseContent.Instance.Error($"构建回调对象失败");
                    }
                    string apiUrl = "";
                    returnDTO.ReqCode = reqCode;
                    returnDTO.ReqTime = reqTime;
                    requestData = returnDTO.Serialize();
                    lineNos = returnDTO.Details.Select(x => x.LineNo).ToList();
                    httpResponseResult = _httpClientHelper.Post<MesResponseDTO>(apiUrl, requestData);
                    httpResponseResult.ApiUrl = apiUrl;
                }
                bool isSuccess = httpResponseResult.IsSuccess && httpResponseResult.Data != null && 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}";
                    }
                }
                Dt_MesReturnRecord mesReturnRecord = new Dt_MesReturnRecord()
                {
                    ApiUrl = httpResponseResult.ApiUrl,
                    InterfaceType = 1,
                    OrderId = outboundOrder.Id,
                    OrderNo = outboundOrder.OrderNo,
                    OrderType = outboundOrder.OrderType,
                    RequestCode = reqCode,
                    RequestData = requestData,
                    FailureReason = message,
                    LastReturnTime = DateTime.Now,
                    HttpStatusCode = httpResponseResult.StatusCode.ObjToInt(),
                    ResponseData = httpResponseResult.Content,
                    ReturnType = 0,
                    ReturnCount = 1,
                    ReturnStatus = httpResponseResult.IsSuccess ? 1 : 2,
                    SuccessTime = httpResponseResult.IsSuccess ? DateTime.Now : null
                };
                _unitOfWorkManage.BeginTran();
                _unitOfWorkManage.Db.Insertable(mesReturnRecord).ExecuteCommand();
                if (isSuccess)
                {
                    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 = "";
                    });
                    _outboundOrderRepository.Db.Updateable(outboundOrderDetails).ExecuteCommand();
                }
                _unitOfWorkManage.CommitTran();
                WebResponseContent responseContent = new WebResponseContent();
                responseContent.Status = isSuccess;
                responseContent.Message = message;
                return responseContent;
            }
            catch(Exception ex)
            {
                return WebResponseContent.Instance.Error(ex.Message);
            }
        }
        public AllocationReturnDTO? BuildAllocationFeedbackData(Dt_OutboundOrder outboundOrder, string fromWarehouse, string toWarehouse)
        {
            try
            {
                List<Dt_OutboundOrderDetail> details = outboundOrder.Details;
                List<AllocationDetail> returnDetails = new List<AllocationDetail>();
                foreach (var detail in details)
                {
                    List<Barcodes>? barcodes = JsonConvert.DeserializeObject<List<Barcodes>>(detail.ReturnJsonData);
                    if (barcodes != null && barcodes.Any())
                    {
                        UnitConvertResultDTO currentResult = _basicService.UnitQuantityConvert(detail.MaterielCode, detail.Unit, detail.BarcodeUnit, detail.CurrentDeliveryQty);
                        UnitConvertResultDTO totalResult = _basicService.UnitQuantityConvert(detail.MaterielCode, detail.Unit, detail.BarcodeUnit, detail.OrderQuantity);
                        returnDetails.Add(new AllocationDetail
                        {
                            Barcodes = barcodes,
                            BatchNo = detail.BatchNo,
                            LineNo = detail.lineNo,
                            MaterialCode = detail.MaterielCode,
                            Qty = totalResult.ToQuantity,
                            WarehouseCode = detail.WarehouseCode,
                            Unit = detail.BarcodeUnit
                        });
                    }
                }
                AllocationReturnDTO outboundReturnDTO = new AllocationReturnDTO()
                {
                    Business_type = outboundOrder.BusinessType,
                    Details = returnDetails,
                    FactoryArea = outboundOrder.FactoryArea,
                    OperationType = 1,
                    OrderNo = outboundOrder.OrderNo,
                    FromWarehouse = fromWarehouse,
                    ToWarehouse = toWarehouse
                };
                return outboundReturnDTO;
            }
            catch (Exception ex)
            {
                return null;
            }
        }
        public void MaterialOutboundFeedback(Dt_OutboundOrder outboundOrder)
        {
            try
            {
                MaterialOutboundReturnDTO? returnDTO = BuildOutboundFeedbackData(outboundOrder);
                if (returnDTO != null)
                {
@@ -112,7 +275,6 @@
            {
                throw new Exception(ex.Message);
            }
        }
        public MaterialOutboundReturnDTO? BuildOutboundFeedbackData(Dt_OutboundOrder outboundOrder)
@@ -125,22 +287,25 @@
                foreach (var detail in details)
                {
                    List<Barcodes>? barcodes = JsonConvert.DeserializeObject<List<Barcodes>>(detail.ReturnJsonData);
                    if (barcodes != null && barcodes.Any())
                    if (!string.IsNullOrWhiteSpace(detail.ReturnJsonData))
                    {
                        UnitConvertResultDTO currentResult = _basicService.UnitQuantityConvert(detail.MaterielCode, detail.Unit, detail.BarcodeUnit, detail.CurrentDeliveryQty);
                        UnitConvertResultDTO totalResult = _basicService.UnitQuantityConvert(detail.MaterielCode, detail.Unit, detail.BarcodeUnit, detail.OrderQuantity);
                        returnDetails.Add(new MaterialOutboundDetail
                        List<Barcodes>? barcodes = JsonConvert.DeserializeObject<List<Barcodes>>(detail.ReturnJsonData);
                        if (barcodes != null && barcodes.Any())
                        {
                            Barcodes = barcodes,
                            CurrentDeliveryQty = currentResult.ToQuantity,
                            LineNo = detail.lineNo,
                            MaterialCode = detail.MaterielCode,
                            Qty = totalResult.ToQuantity,
                            WarehouseCode = detail.WarehouseCode,
                            Unit = detail.BarcodeUnit
                        });
                            UnitConvertResultDTO currentResult = _basicService.UnitQuantityConvert(detail.MaterielCode, detail.Unit, detail.BarcodeUnit, detail.CurrentDeliveryQty);
                            UnitConvertResultDTO totalResult = _basicService.UnitQuantityConvert(detail.MaterielCode, detail.Unit, detail.BarcodeUnit, detail.OrderQuantity);
                            returnDetails.Add(new MaterialOutboundDetail
                            {
                                Barcodes = barcodes,
                                CurrentDeliveryQty = currentResult.ToQuantity,
                                LineNo = detail.lineNo,
                                MaterialCode = detail.MaterielCode,
                                Qty = totalResult.ToQuantity,
                                WarehouseCode = detail.WarehouseCode,
                                Unit = detail.BarcodeUnit
                            });
                        }
                    }
                }
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Core/Middlewares/ApiLogMiddleware.cs
@@ -93,7 +93,7 @@
                    ms.Position = 0;
                    await ms.CopyToAsync(originalBody);
                    if (!ignoreUrls.Any(x => context.Request.Path.Value?.Contains(x) ?? false))
                    if (!ignoreUrls.Any(x => context.Request.Path.Value?.ToLower().Contains(x.ToLower()) ?? false))
                    {
                        Logger.Add(requestParam, responseParam);
                    }
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Core/Seed/DBSeed.cs
@@ -138,33 +138,33 @@
                        }
                        #endregion
                    }
                    else
                    {
                        List<string> columnNames = dbContext.Db.DbMaintenance.GetColumnInfosByTableName(t.Name, false).Select(x => x.DbColumnName).ToList();
                        if (t.GetProperties().FirstOrDefault(x => !columnNames.Contains(x.Name)) != null)
                        {
                            bool isChange = true;
                            List<PropertyInfo> propertyInfos = t.GetProperties().Where(x => !columnNames.Contains(x.Name)).ToList();
                            for (int i = 0; i < propertyInfos.Count; i++)
                            {
                                PropertyInfo propertyInfo = propertyInfos[i];
                                SugarColumn? sugarColumn = propertyInfo.GetCustomAttribute<SugarColumn>();
                                if (sugarColumn != null)
                                {
                                    if (!sugarColumn.IsIgnore)
                                    {
                                        if (!sugarColumn.IsNullable)
                                        {
                                            isChange = false;
                                            break;
                                        }
                                    }
                                }
                            }
                            if (isChange)
                                dbContext.Db.CodeFirst.InitTables(t);
                        }
                    }
                    //else
                    //{
                    //    List<string> columnNames = dbContext.Db.DbMaintenance.GetColumnInfosByTableName(t.Name, false).Select(x => x.DbColumnName).ToList();
                    //    if (t.GetProperties().FirstOrDefault(x => !columnNames.Contains(x.Name)) != null)
                    //    {
                    //        bool isChange = true;
                    //        List<PropertyInfo> propertyInfos = t.GetProperties().Where(x => !columnNames.Contains(x.Name)).ToList();
                    //        for (int i = 0; i < propertyInfos.Count; i++)
                    //        {
                    //            PropertyInfo propertyInfo = propertyInfos[i];
                    //            SugarColumn? sugarColumn = propertyInfo.GetCustomAttribute<SugarColumn>();
                    //            if (sugarColumn != null)
                    //            {
                    //                if (!sugarColumn.IsIgnore)
                    //                {
                    //                    if (!sugarColumn.IsNullable)
                    //                    {
                    //                        isChange = false;
                    //                        break;
                    //                    }
                    //                }
                    //            }
                    //        }
                    //        if (isChange)
                    //            dbContext.Db.CodeFirst.InitTables(t);
                    //    }
                    //}
                });
                ConsoleHelper.WriteSuccessLine($"Tables Created Successfully!");
                Console.WriteLine();
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Core/Util/HttpClientHelper.cs
@@ -39,7 +39,7 @@
                SetRequestHeaders(request, config?.Headers);
                return await client.SendAsync(request);
            }, config, $"POST {url}").Result;
            httpResponseResult.ApiUrl = url;
            return httpResponseResult;
        }
@@ -52,6 +52,7 @@
                return await client.SendAsync(request);
            }, config, $"GET {url}").Result;
            httpResponseResult.ApiUrl = url;
            return httpResponseResult;
        }
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Core/Util/HttpResponseResult.cs
@@ -46,6 +46,11 @@
        /// å¼‚常信息
        /// </summary>
        public Exception Exception { get; set; }
        /// <summary>
        ///
        /// </summary>
        public string ApiUrl { get; set; }
    }
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_DTO/Base/UnitConvertResultDTO.cs
@@ -39,7 +39,7 @@
        /// <summary>
        /// 
        /// </summary>
        public decimal UnitRatio { get; set; }
        public decimal UnitRatio { get; set; } = 1;
        /// <summary>
        /// 
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_DTO/ReturnMES/AllocationReturnDTO.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,92 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WIDESEA_DTO.ReturnMES
{
    /// <summary>
    ///
    /// </summary>
    public class AllocationReturnDTO : BaseReturnDTO
    {
        /// <summary>
        ///
        /// </summary>
        public string OrderNo { get; set; }
        /// <summary>
        /// åŽ‚åŒº
        /// </summary>
        public string FactoryArea { get; set; }
        /// <summary>
        /// ä¸šåŠ¡ç±»åž‹
        /// </summary>
        public string Business_type { get; set; }
        /// <summary>
        /// 1新增2修改3删除
        /// </summary>
        public int OperationType { get; set; }
        /// <summary>
        ///
        /// </summary>
        public string FromWarehouse { get; set; }
        /// <summary>
        ///
        /// </summary>
        public string ToWarehouse { get; set; }
        /// <summary>
        /// æ˜Žç»†
        /// </summary>
        public List<AllocationDetail> Details { get; set; }
    }
    /// <summary>
    /// æ˜Žç»†
    /// </summary>
    public class AllocationDetail
    {
        /// <summary>
        /// ç‰©æ–™ç¼–码
        /// </summary>
        public string MaterialCode { get; set; }
        /// <summary>
        /// è¡Œå·
        /// </summary>
        public string LineNo { get; set; }
        /// <summary>
        /// æ•°é‡
        /// </summary>
        public decimal Qty { get; set; }
        /// <summary>
        ///
        /// </summary>
        public string BatchNo { get; set; }
        /// <summary>
        /// ä»“库
        /// </summary>
        public string WarehouseCode { get; set; }
        /// <summary>
        /// å•位
        /// </summary>
        public string Unit { get; set; }
        /// <summary>
        /// æ¡ç ä¿¡æ¯
        /// </summary>
        public List<Barcodes> Barcodes { get; set; }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_DTO/ReturnMES/FeedbackMesResult.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,15 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace WIDESEA_DTO.ReturnMES
{
    /// <summary>
    ///
    /// </summary>
    public class FeedbackMesResult
    {
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_IBasicService/MESOperation/IFeedbackMesService.cs
@@ -9,6 +9,6 @@
{
    public interface IFeedbackMesService : IDependency
    {
        void MaterialOutboundFeedback(string orderNo);
        WebResponseContent OutboundFeedback(string orderNo);
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_OutboundOrderDetail.cs
@@ -90,28 +90,28 @@
        /// æŒªæ–™æ•°é‡
        /// é»˜è®¤å€¼:
        ///</summary>
        [SugarColumn(ColumnName = "MoveQty", ColumnDescription = "挪料数量", IsNullable = true)]
        [SugarColumn(ColumnDescription = "挪料数量", IsNullable = true)]
        public decimal MoveQty { get; set; }
        /// <summary>
        /// ä¾›åº”商编号
        /// é»˜è®¤å€¼:
        ///</summary>
        [SugarColumn(ColumnName = "supplyCode", ColumnDescription = "供应商编号", IsNullable = true)]
        [SugarColumn(ColumnDescription = "供应商编号", IsNullable = true)]
        public string? SupplyCode { get; set; }
        /// <summary>
        /// æ•°é‡
        /// é»˜è®¤å€¼:
        ///</summary>
        [SugarColumn(ColumnName = "barcodeQty", ColumnDescription = "数量", DefaultValue = "0", IsNullable = true)]
        [SugarColumn(ColumnDescription = "数量", DefaultValue = "0", IsNullable = true)]
        public decimal BarcodeQty { get; set; }
        /// <summary>
        /// å•位
        /// é»˜è®¤å€¼:
        ///</summary>
        [SugarColumn(ColumnName = "barcodeUnit", ColumnDescription = "单位", IsNullable = true)]
        [SugarColumn(ColumnDescription = "单位", IsNullable = true)]
        public string BarcodeUnit { get; set; } = null!;
@@ -119,13 +119,13 @@
        ///  
        /// é»˜è®¤å€¼:
        ///</summary>
        [SugarColumn(ColumnName = "barcodemoveQty", ColumnDescription = "数量", DefaultValue = "0", IsNullable = true)]
        [SugarColumn(ColumnDescription = "数量", DefaultValue = "0", IsNullable = true)]
        public decimal BarcodeMoveQty { get; set; }
        /// <summary>
        /// ä»“库
        /// é»˜è®¤å€¼:
        ///</summary>
        [SugarColumn(ColumnName = "warehouseCode", ColumnDescription = "仓库", IsNullable = true)]
        [SugarColumn(ColumnDescription = "仓库", IsNullable = true)]
        public string? WarehouseCode { get; set; }
        /// <summary>
@@ -142,7 +142,7 @@
        [SugarColumn(IsIgnore = true)]
        public decimal NeedOutQuantity => OrderQuantity - MoveQty;
        [SugarColumn(IsNullable = true, ColumnDescription = "")]
        [SugarColumn(IsNullable = true, ColumnDescription = "", IsIgnore = true)]
        public decimal PickedQty { get; set; }
        [SugarColumn(IsNullable = true, ColumnDescription = "")]
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderDetailService.cs
@@ -642,8 +642,6 @@
                            }
                        }
                    }
                }
            }
            return new PageGridData<Dt_OutboundOrderDetail>();
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundQueryService.cs
@@ -18,8 +18,19 @@
        {
            try
            {
                Dt_StockInfo stockInfo = _stockInfoRepository.Db.Queryable<Dt_StockInfo>().Where(x => x.PalletCode == palletCode).Includes(x => x.Details).First();
                bool isMatMixed = stockInfo.Details.GroupBy(x => new
                {
                    x.MaterielCode,
                    x.MaterielName,
                    x.BatchNo,
                    x.SupplyCode,
                    x.WarehouseCode
                }).Count() > 1;
                List<Dt_OutStockLockInfo> outStockLockInfos = _outboundLockInfoRepository.QueryData(x => x.PalletCode == palletCode && x.OrderNo == orderNo);
                return WebResponseContent.Instance.OK(data: outStockLockInfos);
                return WebResponseContent.Instance.OK(data: new { outStockLockInfos, stockInfo, isMatMixed });
            }
            catch (Exception ex)
            {
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundService.cs
@@ -138,14 +138,18 @@
                    pickedDetails.AddRange(materielPickedDetails.PickedDetails);
                    decimal allallocatedQuantity = materielCalc.UnallocatedQuantity;
                    // æ›´æ–°å‡ºåº“单明细(增加锁定数量,不增加已出数量)
                    foreach (var detail in materielCalc.Details)
                    {
                        if (allallocatedQuantity <= 0) break;
                        decimal lockQuantity = (detail.OrderQuantity - detail.OverOutQuantity);
                        if (lockQuantity < materielCalc.UnallocatedQuantity)
                        {
                            detail.LockQuantity += lockQuantity; // å¢žåŠ é”å®šæ•°é‡ ä¸æ›´æ–° OverOutQuantity å’Œ OrderDetailStatus,因为还没有实际出库
                            outboundOrderDetails.Add(detail);
                            materielCalc.UnallocatedQuantity -= lockQuantity;
                        }
                        else
                        {
@@ -267,6 +271,18 @@
                        return result;
                    }
                    decimal inputQuantity = request.OutboundQuantity.Value;
                    List<Dt_OutboundOrderDetail> outboundOrderDetails = new List<Dt_OutboundOrderDetail>();
                    foreach (var item in selectedDetails)
                    {
                        inputQuantity -= (item.OrderQuantity - item.MoveQty - item.LockQuantity);
                        outboundOrderDetails.Add(item);
                        if (inputQuantity <= 0)
                        {
                            break;
                        }
                    }
                    result.MaterielCalculations = new List<MaterielOutboundCalculationDTO>()
                    {
                        new MaterielOutboundCalculationDTO
@@ -281,9 +297,11 @@
                            AssignedQuantity = lockQuantity,
                            UnallocatedQuantity = request.OutboundQuantity.Value,
                            MovedQuantity = moveQuantity,
                            Details = selectedDetails
                            Details = outboundOrderDetails
                        }
                    };
                    outboundOrder.Details = outboundOrderDetails;
                }
                result.CanOutbound = true;
@@ -790,7 +808,7 @@
        }
        #endregion
        #region æ‹£é€‰
        /// <summary>
        /// å‡ºåº“完成处理(扫描条码扣减库存)
        /// </summary>
@@ -885,12 +903,12 @@
                    return WebResponseContent.Instance.Error($"无法出库,条码:{request.Barcode},库存:{stockDetail.StockQuantity},已出库:{totalAllocatedQuantity},分配量:{lockInfo.AssignQuantity},明细剩余:{detailRemainingQuantity}");
                }
                if (actualOutboundQuantity + lockInfo.SortedQuantity > lockInfo.AssignQuantity)
                {
                    response.Success = false;
                    response.Message = $"无法出库,条码:{request.Barcode},库存:{stockDetail.StockQuantity},出库量{actualOutboundQuantity + lockInfo.SortedQuantity}大于分配量{lockInfo.AssignQuantity}";
                    return WebResponseContent.Instance.Error($"无法出库,条码:{request.Barcode},库存:{stockDetail.StockQuantity},出库量{actualOutboundQuantity + lockInfo.SortedQuantity}大于分配量{lockInfo.AssignQuantity}");
                }
                //if (actualOutboundQuantity + lockInfo.SortedQuantity > lockInfo.AssignQuantity)
                //{
                //    response.Success = false;
                //    response.Message = $"无法出库,条码:{request.Barcode},库存:{stockDetail.StockQuantity},出库量{actualOutboundQuantity + lockInfo.SortedQuantity}大于分配量{lockInfo.AssignQuantity}";
                //    return WebResponseContent.Instance.Error($"无法出库,条码:{request.Barcode},库存:{stockDetail.StockQuantity},出库量{actualOutboundQuantity + lockInfo.SortedQuantity}大于分配量{lockInfo.AssignQuantity}");
                //}
                // 8. åˆ¤æ–­æ˜¯å¦éœ€è¦æ‹†åŒ…(当出库数量小于库存数量时需要拆包)
                bool isUnpacked = actualOutboundQuantity < stockDetail.StockQuantity;
@@ -930,6 +948,8 @@
                        //    item.OverOutQuantity = item.OrderQuantity - item.MoveQty;
                        //}
                        decimal barcodeQuantity = allocatedQuantity;
                        if (item.LockQuantity - item.OverOutQuantity >= allocatedQuantity)
                        {
                            item.OverOutQuantity += allocatedQuantity;
@@ -938,6 +958,7 @@
                        }
                        else
                        {
                            barcodeQuantity = item.LockQuantity - item.OverOutQuantity;
                            allocatedQuantity -= (item.LockQuantity - item.OverOutQuantity);
                            item.OverOutQuantity = item.LockQuantity;
                            item.CurrentDeliveryQty = item.LockQuantity;
@@ -949,7 +970,7 @@
                        Barcodes barcodes = new Barcodes
                        {
                            Barcode = request.Barcode,
                            Qty = actualOutboundQuantity,
                            Qty = barcodeQuantity,
                            SupplyCode = stockDetail?.SupplyCode ?? "",
                            BatchNo = stockDetail?.BatchNo ?? "",
                            Unit = stockDetail?.Unit ?? ""
@@ -1012,6 +1033,7 @@
                    if (CheckOutboundOrderCompleted(request.OrderNo))
                    {
                        UpdateOutboundOrderStatus(request.OrderNo, OutOrderStatusEnum.出库完成.ObjToInt());
                        //todo: å›žä¼ MES
                    }
@@ -1325,5 +1347,7 @@
            // æ£€æŸ¥æ‰€æœ‰æ˜Žç»†çš„已出数量是否都等于单据数量
            return details.All(x => x.OverOutQuantity >= x.OrderQuantity - x.MoveQty);
        }
        #endregion
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Basic/MesFeedbackController.cs
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using WIDESEA_Core;
using WIDESEA_IBasicService;
namespace WIDESEA_WMSServer.Controllers.Basic
{
    [Route("api/[controller]")]
    [ApiController]
    public class MesFeedbackController : ControllerBase
    {
        private readonly IFeedbackMesService _feedbackMesService;
        public MesFeedbackController(IFeedbackMesService feedbackMesService)
        {
            _feedbackMesService = feedbackMesService;
        }
        [HttpPost, HttpGet, Route("OutboundFeedback")]
        public WebResponseContent OutboundFeedback(string orderNo)
        {
            return _feedbackMesService.OutboundFeedback(orderNo);
        }
    }
}
ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Outbound/OutboundController.cs
@@ -40,17 +40,22 @@
            }
        }
        object lockObj = new object();
        [HttpPost, Route("CompleteOutboundWithBarcode"), AllowAnonymous]
        public WebResponseContent CompleteOutboundWithBarcode([FromBody] OutboundCompleteRequestDTO request)
        {
            try
            {
                if (!ModelState.IsValid)
                    return WebResponseContent.Instance.Error(string.Join("; ", ModelState.Values
                        .SelectMany(v => v.Errors)
                        .Select(e => e.ErrorMessage)));
                lock (lockObj)
                {
                    if (!ModelState.IsValid)
                        return WebResponseContent.Instance.Error(string.Join("; ", ModelState.Values
                            .SelectMany(v => v.Errors)
                            .Select(e => e.ErrorMessage)));
                return _outboundService.CompleteOutboundWithBarcode(request);
                    return _outboundService.CompleteOutboundWithBarcode(request);
                }
            }
            catch (Exception ex)
            {