ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/outbound/extend/outOrderDetail.vue
@@ -642,18 +642,18 @@ } catch (err) { return; } console.log(this.selection); // 4. æé 请æ±åæ°ï¼æ°å¢å°æ°åæ®µï¼ const keys = this.selection.map((item) => item.id); const requestParams = { taskIds: keys, orderDetailId: keys[0], // åæ¹åºåºä» æ¯æåæ¡æç» outboundPlatform: formData.selectedPlatform, // åºåºç«å° outboundDecimal: formData.outboundDecimal // æ°å¢ï¼å°æ°åæ®µä¼ ç»å端 batchQuantity: formData.outboundDecimal // æ°å¢ï¼å°æ°åæ®µä¼ ç»å端 }; // 5. è°ç¨åºåºæ¥å£ this.http .post("api/Task/ ", requestParams, "æ°æ®å¤çä¸") .post("api/Task/GenerateOutboundBatchTasks", requestParams, "æ°æ®å¤çä¸") .then((x) => { if (!x.status) return ElMessage.error(x.message); ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/outbound/BatchPickingConfirm.vue
@@ -4,29 +4,8 @@ <el-page-header @back="goBack"> <template #content> <span class="title">åºåºæ£é确认 - {{ this.$route.query.orderNo }}</span> <el-tag v-if="currentBatchNo" type="success" style="margin-left: 10px;"> å½åæ¹æ¬¡: {{ currentBatchNo }} </el-tag> </template> </el-page-header> </div> <!-- æ¹æ¬¡æä½åºå --> <div class="batch-operations"> <el-card> <div class="batch-actions"> <el-button type="primary" @click="openBatchAllocateDialog">åæ¹åé </el-button> <el-select v-model="selectedBatchNo" placeholder="éæ©æ¹æ¬¡" @change="onBatchChange" style="width: 200px; margin-left: 10px;"> <el-option v-for="batch in batchList" :key="batch.batchNo" :label="`${batch.batchNo} (${batch.batchStatusText})`" :value="batch.batchNo"> </el-option> </el-select> <el-button type="info" @click="refreshBatchList">å·æ°æ¹æ¬¡</el-button> </div> </el-card> </div> <!-- æ«ç åºå --> @@ -52,21 +31,6 @@ <el-button type="info" @click="openRevertSplitDialog">æ¤éæå </el-button> <el-button type="info" @click="handleEmptyPallet">å空箱</el-button> <el-button type="primary" @click="openBatchReturnDialog">ååº</el-button> </div> </el-card> </div> <!-- æ¹æ¬¡æ±æ»ä¿¡æ¯ --> <div class="batch-summary-area" v-if="currentBatchNo"> <el-card> <div class="batch-summary-info"> <el-tag type="info">æ¹æ¬¡å·: {{ batchSummary.batchNo }}</el-tag> <el-tag :type="getBatchStatusType(batchSummary.batchStatus)"> {{ batchSummary.batchStatusText }} </el-tag> <el-tag type="warning">åé æ°é: {{ batchSummary.batchQuantity }}</el-tag> <el-tag type="success">宿æ°é: {{ batchSummary.completedQuantity }}</el-tag> <el-tag type="danger">å©ä½æ°é: {{ batchSummary.remainingQuantity }}</el-tag> </div> </el-card> </div> @@ -127,52 +91,6 @@ </el-row> </div> <!-- åæ¹åé å¼¹çª --> <div v-if="showBatchAllocateDialog" class="custom-dialog-overlay"> <div class="custom-dialog-wrapper"> <div class="custom-dialog"> <div class="custom-dialog-header"> <h3>åæ¹åé åºå</h3> <el-button type="text" @click="closeBatchAllocateDialog" class="close-button">Ã</el-button> </div> <div class="custom-dialog-body"> <el-form :model="batchAllocateForm" :rules="batchAllocateFormRules" ref="batchAllocateFormRef" label-width="100px"> <el-form-item label="订åç¼å·"> <el-input v-model="batchAllocateForm.orderNo" disabled></el-input> </el-form-item> <el-form-item label="ç©ææç»" prop="orderDetailId"> <el-select v-model="batchAllocateForm.orderDetailId" placeholder="éæ©ç©ææç»" style="width: 100%"> <el-option v-for="detail in allocatableDetails" :key="detail.id" :label="`${detail.materielCode} - éæ±:${detail.needOutQuantity} å·²åé :${detail.allocatedQuantity} å¯åé :${detail.availableQuantity}`" :value="detail.id"> </el-option> </el-select> </el-form-item> <el-form-item label="åé æ°é" prop="batchQuantity"> <el-input-number v-model="batchAllocateForm.batchQuantity" :min="0.01" :precision="2" :step="1" style="width: 100%" placeholder="è¾å ¥åé æ°é"> </el-input-number> </el-form-item> <el-form-item label="å¯åé æ°é"> <el-input :value="getAvailableQuantity()" disabled></el-input> </el-form-item> </el-form> </div> <div class="custom-dialog-footer"> <el-button @click="closeBatchAllocateDialog">åæ¶</el-button> <el-button type="primary" @click="handleBatchAllocate" :loading="batchAllocateLoading">确认åé </el-button> </div> </div> </div> </div> <!-- æå å¼¹çª --> <div v-if="showCustomSplitDialog" class="custom-dialog-overlay"> <div class="custom-dialog-wrapper"> @@ -186,20 +104,28 @@ <el-form-item label="订åç¼å·"> <el-input v-model="splitForm.orderNo" disabled></el-input> </el-form-item> <el-form-item label="æ¹æ¬¡ç¼å·"> <el-input v-model="splitForm.batchNo" disabled></el-input> </el-form-item> <el-form-item label="æçç¼å·"> <el-input v-model="splitForm.palletCode" disabled></el-input> </el-form-item> <el-form-item label="åæ¡ç " prop="originalBarcode"> <el-input v-model="splitForm.originalBarcode" placeholder="æ«æåæ¡ç " @keyup.enter.native="onSplitBarcodeScan" @change="onSplitBarcodeScan" clearable> </el-input> <div style="display: flex; align-items: center; gap: 10px;"> <el-input v-model="splitForm.originalBarcode" placeholder="æ«æåæ¡ç " @keyup.enter.native="onSplitBarcodeScan" @change="onSplitBarcodeScan" clearable style="flex: 1;"> </el-input> <!-- æ°å¢ï¼æ¥çæå é¾æé® --> <el-button type="primary" @click="viewSplitChainFromSplit(splitForm.originalBarcode)" :disabled="!splitForm.originalBarcode" :loading="splitChainLoading"> æ¥çæå é¾ </el-button> </div> </el-form-item> <el-form-item label="ç©æç¼ç "> <el-input v-model="splitForm.materielCode" disabled></el-input> @@ -238,15 +164,46 @@ <div class="custom-dialog-body"> <el-form :model="revertSplitForm" :rules="revertSplitFormRules" ref="revertSplitFormRef" label-width="100px"> <el-form-item label="æ°æ¡ç " prop="newBarcode"> <el-input v-model="revertSplitForm.newBarcode" placeholder="æ«ææ°æ¡ç " @keyup.enter.native="onRevertSplitBarcodeScan" @change="onRevertSplitBarcodeScan" clearable> </el-input> <div style="display: flex; align-items: center; gap: 10px;"> <el-input v-model="revertSplitForm.newBarcode" placeholder="æ«ææ°æ¡ç " @keyup.enter.native="onRevertSplitBarcodeScan" @change="onRevertSplitBarcodeScan" clearable style="flex: 1;"> </el-input> <!-- æ°å¢ï¼æ¥çæå é¾æé® --> <el-button type="primary" @click="viewSplitChain(revertSplitForm.newBarcode)" :disabled="!revertSplitForm.newBarcode"> æ¥çæå é¾ </el-button> </div> </el-form-item> </el-form> <!-- æ°å¢ï¼æå é¾ç®è¦ä¿¡æ¯æ¾ç¤º --> <div v-if="splitChainInfo.splitChain && splitChainInfo.splitChain.length > 0" style="margin-top: 15px; padding: 10px; background: #f0f9ff; border-radius: 4px;"> <div style="font-size: 14px; color: #606266;"> <div>æå é¾ä¿¡æ¯: å ± {{ splitChainInfo.totalSplitTimes }} 次æå </div> <div style="margin-top: 5px;"> <el-tag v-for="item in splitChainInfo.splitChain.slice(0, 3)" :key="item.newBarcode" :type="item.isReverted ? 'success' : 'primary'" size="small" style="margin-right: 5px;"> {{ item.newBarcode }} ({{ item.splitQuantity }}) </el-tag> <span v-if="splitChainInfo.splitChain.length > 3" style="color: #909399;"> ç {{ splitChainInfo.splitChain.length }} 个æ¡ç </span> </div> </div> </div> </div> <div class="custom-dialog-footer"> <el-button @click="closeRevertSplitDialog">åæ¶</el-button> @@ -256,12 +213,136 @@ </div> </div> <!-- æå é¾ä¿¡æ¯å¼¹çª --> <div v-if="showSplitChainDialog" class="custom-dialog-overlay"> <div class="custom-dialog-wrapper"> <div class="custom-dialog" style="width: 750px;"> <div class="custom-dialog-header"> <h3>æå é¾ä¿¡æ¯</h3> <el-button type="text" @click="closeSplitChainDialog" class="close-button">Ã</el-button> </div> <div class="custom-dialog-body"> <!-- æ°å¢ï¼æå é¾è¯´æ --> <div style="margin-bottom: 15px; padding: 10px; background: #f0f9ff; border-radius: 4px;"> <div style="display: flex; justify-content: space-between; align-items: center;"> <div> <div style="font-weight: bold; color: #303133;">æå é¾è¯´æ</div> <div style="font-size: 12px; color: #606266; margin-top: 5px;"> å½åæ¾ç¤ºçæ¯ä» <el-tag type="primary" size="small">{{ splitChainInfo.originalBarcode }}</el-tag> å¼å§çæå é¾ <br>å ± {{ splitChainInfo.totalSplitTimes }} 次æå æä½ï¼æ¶å {{ splitChainInfo.splitChain.length }} 个æ¡ç </div> </div> <el-button type="primary" size="small" @click="findRootChain(splitChainInfo.originalBarcode)" v-if="splitChainInfo.chainType !== 'root'"> æ¥æ¾å®æ´æå é¾ </el-button> </div> </div> <div style="margin-bottom: 15px;"> <el-tag type="info">æ»æå æ¬¡æ°: {{ splitChainInfo.totalSplitTimes }}</el-tag> <el-tag type="warning" style="margin-left: 10px;"> åå§æ¡ç : {{ splitChainInfo.originalBarcode }} </el-tag> <el-tag :type="splitChainInfo.chainType === 'root' ? 'success' : 'warning'" style="margin-left: 10px;"> {{ splitChainInfo.chainType === 'root' ? '宿´é¾' : '忝é¾' }} </el-tag> </div> <el-table :data="splitChainInfo.splitChain" border height="300"> <el-table-column type="index" label="åºå·" width="60" align="center"></el-table-column> <el-table-column prop="splitTime" label="æå æ¶é´" width="160"> <template #default="scope"> {{ formatDateTime(scope.row.splitTime) }} </template> </el-table-column> <el-table-column prop="originalBarcode" label="åæ¡ç " width="140"> <template #default="scope"> <el-tag :type="scope.row.originalBarcode === splitChainInfo.rootBarcode ? 'success' : 'primary'" size="small"> {{ scope.row.originalBarcode }} </el-tag> </template> </el-table-column> <el-table-column prop="newBarcode" label="æ°æ¡ç " width="140"> <template #default="scope"> <el-tag :type="scope.row.isReverted ? 'info' : 'warning'" size="small"> {{ scope.row.newBarcode }} </el-tag> </template> </el-table-column> <el-table-column prop="splitQuantity" label="æå æ°é" width="100" align="right"> <template #default="scope"> {{ scope.row.splitQuantity.toFixed(2) }} </template> </el-table-column> <el-table-column prop="operator" label="æä½å" width="100"></el-table-column> <el-table-column prop="isReverted" label="ç¶æ" width="80" align="center"> <template #default="scope"> <el-tag :type="scope.row.isReverted ? 'success' : 'danger'" size="small"> {{ scope.row.isReverted ? 'å·²æ¤é' : 'ææ' }} </el-tag> </template> </el-table-column> <el-table-column label="æä½" width="120" align="center"> <template #default="scope"> <el-button v-if="!scope.row.isReverted" type="danger" size="mini" @click="cancelSingleSplit(scope.row.newBarcode)" :disabled="hasPicked(scope.row.newBarcode)"> åæ¶ </el-button> <span v-else style="color: #909399;">å·²æ¤é</span> </template> </el-table-column> </el-table> <!-- æ¹éæä½åºå --> <div style="margin-top: 15px; padding: 10px; background: #f5f7fa; border-radius: 4px;"> <div style="display: flex; justify-content: space-between; align-items: center;"> <div> <span style="font-size: 14px; color: #606266;"> æ¹éæä½: å¯ä»¥åæ¶æ´ä¸ªæå 龿鿩å个æå è®°å½åæ¶ </span> <div style="font-size: 12px; color: #909399; margin-top: 5px;"> 宿´æå é¾å å«ä»æåå§æ¡ç å¼å§çæææå æä½ </div> </div> <div> <el-button type="danger" @click="cancelWholeSplitChain" :disabled="!canCancelWholeChain" :loading="revertSplitLoading"> åæ¶æ´ä¸ªæå é¾ </el-button> </div> </div> </div> </div> <div class="custom-dialog-footer"> <el-button @click="closeSplitChainDialog">å ³é</el-button> </div> </div> </div> </div> <!-- æ¹éååºå¼¹çª --> <div v-if="showBatchReturnDialog" class="custom-dialog-overlay"> <div class="custom-dialog-wrapper"> <div class="custom-dialog"> <div class="custom-dialog-header"> <h3>æ¹æ¬¡ååº</h3> <h3>æçååº</h3> <el-button type="text" @click="closeBatchReturnDialog" class="close-button">Ã</el-button> </div> <div class="custom-dialog-body"> @@ -269,8 +350,8 @@ <el-form-item label="订åç¼å·"> <el-input v-model="batchReturnForm.orderNo" disabled></el-input> </el-form-item> <el-form-item label="æ¹æ¬¡ç¼å·"> <el-input v-model="batchReturnForm.batchNo" disabled></el-input> <el-form-item label="æçç¼å·"> <el-input v-model="batchReturnForm.palletCode" disabled></el-input> </el-form-item> <el-form-item label="æªæ£éæ°é"> <el-input v-model="batchReturnForm.unpickedQuantity" disabled></el-input> @@ -335,36 +416,12 @@ name: 'BatchOutboundPicking', components: {printView}, data() { // éªè¯è§åå®ä¹... const validateBatchQuantity = (rule, value, callback) => { if (value === null || value === undefined || value === '') { callback(new Error('请è¾å ¥åé æ°é')); } else if (value <= 0) { callback(new Error('åé æ°éå¿ é¡»å¤§äº0')); } else { callback(); } }; const validateOrderDetailId = (rule, value, callback) => { if (!value) { callback(new Error('è¯·éæ©ç©ææç»')); } else { callback(); } }; return { scanData: { orderNo: '', palletCode: '', barcode: '', batchNo: '' barcode: '' }, currentBatchNo: '', // å½åæ¹æ¬¡å· batchList: [], // æ¹æ¬¡å表 selectedBatchNo: '', // éä¸çæ¹æ¬¡å· batchSummary: {}, // æ¹æ¬¡æ±æ»ä¿¡æ¯ unpickedList: [], pickedList: [], selectedPickedRows: [], @@ -376,30 +433,22 @@ palletStatus: 'æªç¥', // å¼¹çªç¶æ showBatchAllocateDialog: false, showCustomSplitDialog: false, showRevertSplitDialog: false, showBatchReturnDialog: false, showEmptyPalletDialog: false, showSplitChainDialog: false, // æ°å¢ï¼æå é¾ä¿¡æ¯å¼¹çª // å è½½ç¶æ batchAllocateLoading: false, splitLoading: false, revertSplitLoading: false, batchReturnLoading: false, emptypalletOutLoading: false, splitChainLoading: false, // æ°å¢ï¼æå é¾å è½½ç¶æ // è¡¨åæ°æ® batchAllocateForm: { orderNo: '', orderDetailId: '', batchQuantity: 0 }, allocatableDetails: [], // å¯åé ç订åæç» splitForm: { orderNo: '', batchNo: '', palletCode: '', originalBarcode: '', materielCode: '', @@ -413,7 +462,7 @@ batchReturnForm: { orderNo: '', batchNo: '', palletCode: '', unpickedCount: 0, unpickedQuantity: 0 }, @@ -423,25 +472,52 @@ palletCode: '' }, // æ°å¢ï¼æå é¾ç¸å ³æ°æ® splitChainInfo: { originalBarcode: '', totalSplitTimes: 0, splitChain: [] }, // éªè¯è§å batchAllocateFormRules: { orderDetailId: [ { required: true, validator: validateOrderDetailId, trigger: 'change' } splitFormRules: { originalBarcode: [ { required: true, message: '请è¾å ¥åæ¡ç ', trigger: 'blur' } ], batchQuantity: [ { required: true, validator: validateBatchQuantity, trigger: 'blur' } splitQuantity: [ { required: true, message: '请è¾å ¥æå æ°é', trigger: 'blur' }, { type: 'number', min: 0.01, message: 'æå æ°éå¿ é¡»å¤§äº0', trigger: 'blur' } ] }, // å ¶ä»éªè¯è§å... revertSplitFormRules: { newBarcode: [ { required: true, message: '请è¾å ¥æ°æ¡ç ', trigger: 'blur' } ] }, emptypalletOutFormRules: { palletCode: [ { required: true, message: '请è¾å ¥æçç ', trigger: 'blur' } ] }, isProcessing: false } }, computed: { // æ¯å¦å¯ä»¥åæ¶æ´ä¸ªæå é¾ canCancelWholeChain() { return this.splitChainInfo.splitChain && this.splitChainInfo.splitChain.some(item => !item.isReverted); } }, mounted() { if (this.$route.query.orderNo) { this.scanData.orderNo = this.$route.query.orderNo; this.batchAllocateForm.orderNo = this.$route.query.orderNo; this.loadBatchList(); this.splitForm.orderNo = this.$route.query.orderNo; this.batchReturnForm.orderNo = this.$route.query.orderNo; this.emptypalletOutForm.orderNo = this.$route.query.orderNo; } this.$nextTick(() => { this.$refs.palletInput.focus(); @@ -450,150 +526,6 @@ methods: { goBack(){ this.$router.back() }, // æ¹æ¬¡ç¸å ³æ¹æ³ async loadBatchList() { try { const res = await http.post('/api/BatchOutbound/order-batch-list', { orderNo: this.scanData.orderNo }); if (res.status) { this.batchList = res.data || []; if (this.batchList.length > 0) { this.selectedBatchNo = this.batchList[0].batchNo; this.currentBatchNo = this.selectedBatchNo; this.scanData.batchNo = this.selectedBatchNo; this.loadBatchData(); } } } catch (error) { this.$message.error('å è½½æ¹æ¬¡å表失败'); } }, async refreshBatchList() { await this.loadBatchList(); this.$message.success('æ¹æ¬¡åè¡¨å·²å·æ°'); }, onBatchChange(batchNo) { this.currentBatchNo = batchNo; this.scanData.batchNo = batchNo; this.loadBatchData(); }, async loadBatchData() { if (!this.currentBatchNo) return; await this.loadBatchSummary(); await this.loadUnpickedList(); await this.loadPickedList(); }, async loadBatchSummary() { try { const res = await http.post('/api/BatchOutbound/batch-summary', { orderNo: this.scanData.orderNo, batchNo: this.currentBatchNo }); if (res.status) { this.batchSummary = res.data || {}; } } catch (error) { this.$message.error('å è½½æ¹æ¬¡æ±æ»å¤±è´¥'); } }, async loadUnpickedList() { try { const res = await http.post('/api/BatchOutbound/batch-unpicked-list', { orderNo: this.scanData.orderNo, batchNo: this.currentBatchNo }); this.unpickedList = res.data || []; this.summary.unpickedCount = this.unpickedList.length; this.summary.unpickedQuantity = this.unpickedList.reduce((sum, item) => sum + (item.remainQuantity || 0), 0); } catch (error) { this.$message.error('å è½½æªæ£éå表失败'); } }, async loadPickedList() { try { const res = await http.post('/api/BatchOutbound/batch-picked-list', { orderNo: this.scanData.orderNo, batchNo: this.currentBatchNo }); this.pickedList = res.data || []; this.summary.pickedCount = this.pickedList.length; } catch (error) { this.$message.error('å 载已æ£éå表失败'); } }, getBatchStatusType(status) { const statusMap = { 0: 'info', // åé ä¸ 1: 'warning', // æ§è¡ä¸ 2: 'success', // 已宿 3: 'danger' // å·²ååº }; return statusMap[status] || 'info'; }, // åæ¹åé ç¸å ³æ¹æ³ async openBatchAllocateDialog() { this.showBatchAllocateDialog = true; await this.loadAllocatableDetails(); this.batchAllocateForm.orderDetailId = ''; this.batchAllocateForm.batchQuantity = 0; }, async loadAllocatableDetails() { try { const res = await http.post('/api/BatchOutbound/allocatable-order-details', { orderNo: this.scanData.orderNo }); if (res.status) { this.allocatableDetails = res.data || []; } } catch (error) { this.$message.error('å è½½å¯åé æç»å¤±è´¥'); } }, getAvailableQuantity() { const detail = this.allocatableDetails.find(d => d.id === this.batchAllocateForm.orderDetailId); return detail ? detail.availableQuantity : 0; }, async handleBatchAllocate() { if (this.$refs.batchAllocateFormRef) { this.$refs.batchAllocateFormRef.validate(async (valid) => { if (valid) { this.batchAllocateLoading = true; try { const res = await http.post('/api/BatchOutbound/batch-allocate-stock', this.batchAllocateForm); if (res.status) { this.$message.success('åæ¹åé æå'); this.showBatchAllocateDialog = false; await this.loadBatchList(); // å·æ°æ¹æ¬¡å表 } else { this.$message.error(res.message || 'åæ¹åé 失败'); } } catch (error) { this.$message.error('åæ¹åé 失败'); } finally { this.batchAllocateLoading = false; } } }); } }, closeBatchAllocateDialog() { this.showBatchAllocateDialog = false; }, // 忣ç¸å ³æ¹æ³ @@ -606,19 +538,18 @@ return; } if (!this.currentBatchNo) { this.$message.warning('请å éæ©æ¹æ¬¡'); return; } this.isProcessing = true; try { const res = await http.post('/api/BatchOutbound/confirm-picking', this.scanData); const res = await http.post('/api/OutboundBatchPicking/confirm-picking', { orderNo: this.scanData.orderNo, palletCode: this.scanData.palletCode, barcode: this.scanData.barcode }); if (res.status) { this.$message.success('æ£é确认æå'); this.scanData.barcode = ''; await this.loadBatchData(); await this.loadPalletData(); if(res.data && res.data.splitResults && res.data.splitResults.length>0){ this.$refs.childs.open(res.data.splitResults); } @@ -643,14 +574,9 @@ this.$message.warning('è¯·å æ«ææçç '); return; } if (!this.currentBatchNo) { this.$message.warning('请å éæ©æ¹æ¬¡'); return; } this.showCustomSplitDialog = true; this.resetSplitForm(); this.splitForm.orderNo = this.scanData.orderNo; this.splitForm.batchNo = this.currentBatchNo; this.splitForm.palletCode = this.scanData.palletCode; }, @@ -659,9 +585,9 @@ this.splitForm.originalBarcode = this.splitForm.originalBarcode.replace(/\n/g, '').trim(); try { const res = await http.post('/api/BatchOutbound/split-package-info', { const res = await http.post('/api/OutboundBatchPicking/split-package-info', { orderNo: this.splitForm.orderNo, batchNo: this.splitForm.batchNo, palletCode: this.splitForm.palletCode, barcode: this.splitForm.originalBarcode }); @@ -683,11 +609,16 @@ if (valid) { this.splitLoading = true; try { const res = await http.post('/api/BatchOutbound/manual-split-package', this.splitForm); const res = await http.post('/api/OutboundBatchPicking/split-package', { orderNo: this.splitForm.orderNo, palletCode: this.splitForm.palletCode, originalBarcode: this.splitForm.originalBarcode, splitQuantity: this.splitForm.splitQuantity }); if (res.status) { this.$message.success('æå æå'); this.showCustomSplitDialog = false; await this.loadBatchData(); await this.loadPalletData(); } else { this.$message.error(res.message || 'æå 失败'); } @@ -700,11 +631,28 @@ }); } }, // 卿å å¼¹çªä¸æ¥çæå é¾ async viewSplitChainFromSplit(barcode) { if (!barcode) { this.$message.warning('请å è¾å ¥æ¡ç '); return; } // å å ³éæå å¼¹çª this.closeCustomSplitDialog(); await this.$nextTick(); // ç¶åæå¼æå é¾ä¿¡æ¯å¼¹çª await this.viewSplitChain(barcode); }, // æ¤éæå async onRevertSplitBarcodeScan() { if (!this.revertSplitForm.newBarcode) return; this.revertSplitForm.newBarcode = this.revertSplitForm.newBarcode.replace(/\n/g, '').trim(); // æ°å¢ï¼æ«æåèªå¨æ¾ç¤ºæå é¾ä¿¡æ¯ await this.viewSplitChain(this.revertSplitForm.newBarcode); }, async handleRevertSplit() { @@ -713,15 +661,15 @@ if (valid) { this.revertSplitLoading = true; try { const res = await http.post('/api/BatchOutbound/cancel-split-package', { const res = await http.post('/api/OutboundBatchPicking/cancel-split', { orderNo: this.scanData.orderNo, batchNo: this.currentBatchNo, palletCode: this.scanData.palletCode, newBarcode: this.revertSplitForm.newBarcode }); if (res.status) { this.$message.success('æ¤éæå æå'); this.showRevertSplitDialog = false; await this.loadBatchData(); await this.loadPalletData(); } else { this.$message.error(res.message || 'æ¤éæå 失败'); } @@ -734,16 +682,238 @@ }); } }, // æ¥æ¾å®æ´æå é¾ï¼ä»æ ¹æ¡ç å¼å§ï¼ async findRootChain(currentBarcode) { this.splitChainLoading = true; try { const res = await http.post('/api/OutboundBatchPicking/find-root-split-chain', { orderNo: this.scanData.orderNo, barcode: currentBarcode }); if (res.status) { this.splitChainInfo = res.data; this.$message.success('å·²å è½½å®æ´æå é¾'); } else { this.$message.error(res.message || 'æ¥æ¾å®æ´æå é¾å¤±è´¥'); } } catch (error) { this.$message.error('æ¥æ¾å®æ´æå é¾å¤±è´¥'); } finally { this.splitChainLoading = false; } }, // æå é¾ç¸å ³æ¹æ³ // æ¥çæå é¾ä¿¡æ¯ async viewSplitChain(barcode) { if (!barcode) { this.$message.warning('请å è¾å ¥æ¡ç '); return; } this.splitChainLoading = true; try { const res = await http.post('/api/OutboundBatchPicking/split-package-chain-info', { orderNo: this.scanData.orderNo, barcode: barcode }); if (res.status) { this.splitChainInfo = res.data; // æ¾ç¤ºæç¤ºä¿¡æ¯ï¼åè¯ç¨æ·è¿æ¯ä»ä¹ç±»åçæå é¾ let chainType = "å½åæ¡ç çæå é¾"; if (this.splitChainInfo.chainType === 'root') { chainType = "宿´æå é¾ï¼ä»åå§æ¡ç å¼å§ï¼"; } else if (this.splitChainInfo.chainType === 'branch') { chainType = "忝æå é¾"; } this.$message.info(`å·²å è½½${chainType}ï¼å ±${this.splitChainInfo.totalSplitTimes}次æå `); this.showSplitChainDialog = true; } else { this.$message.error(res.message || 'è·åæå é¾ä¿¡æ¯å¤±è´¥'); } } catch (error) { this.$message.error('è·åæå é¾ä¿¡æ¯å¤±è´¥'); } finally { this.splitChainLoading = false; } }, // å ³éæå é¾ä¿¡æ¯å¼¹çª closeSplitChainDialog() { this.showSplitChainDialog = false; }, // 卿¤éæå å¼¹çªä¸æ¥çæå é¾ async viewSplitChainFromRevert(barcode) { if (!barcode) { this.$message.warning('请å è¾å ¥æ¡ç '); return; } // å å ³éæ¤éæå å¼¹çª this.closeRevertSplitDialog(); await this.$nextTick(); // ç¶åæå¼æå é¾ä¿¡æ¯å¼¹çª await this.viewSplitChain(barcode); }, // å¿«ééæ°æå¼æå é¾å¼¹çª async quickReopenSplitChainDialog(barcode) { if (!barcode) return; this.showSplitChainDialog = true; this.splitChainLoading = true; try { const res = await http.post('/api/OutboundBatchPicking/split-package-chain-info', { orderNo: this.scanData.orderNo, barcode: barcode }); if (res.status) { this.splitChainInfo = res.data; } } catch (error) { console.error('éæ°å è½½æå é¾ä¿¡æ¯å¤±è´¥:', error); } finally { this.splitChainLoading = false; } }, // åæ¶å个æå è®°å½ async cancelSingleSplit(newBarcode) { // å è®°å½å½åä¿¡æ¯ï¼ç¶åå ³éå¼¹çª const originalBarcode = this.splitChainInfo.originalBarcode; this.closeSplitChainDialog(); await this.$nextTick(); try { await this.$confirm( `ç¡®å®è¦åæ¶æ¡ç ${newBarcode} çæå æä½åï¼`, 'åæ¶å个æå ', { confirmButtonText: 'ç¡®å®åæ¶', cancelButtonText: 'åæ³æ³', type: 'warning' } ); this.revertSplitLoading = true; const res = await http.post('/api/OutboundBatchPicking/cancel-split', { orderNo: this.scanData.orderNo, palletCode: this.scanData.palletCode, newBarcode: newBarcode }); if (res.status) { this.$message.success('åæ¶æå æå'); await this.loadPalletData(); // éæ°æå¼å¼¹çªæ¾ç¤ºæ´æ°åçç¶æ await this.viewSplitChain(originalBarcode); } else { this.$message.error(res.message || 'åæ¶æå 失败'); await this.viewSplitChain(originalBarcode); } } catch (error) { if (error === 'cancel') { // ç¨æ·åæ¶åéæ°æå¼å¼¹çª await this.viewSplitChain(originalBarcode); } else { this.$message.error('åæ¶æå 失败'); await this.viewSplitChain(originalBarcode); } } finally { this.revertSplitLoading = false; } }, // åæ¶æ´ä¸ªæå é¾ async cancelWholeSplitChain() { // å è®°å½å½åæå é¾ä¿¡æ¯ï¼ç¶åå ³éå¼¹çª const originalBarcode = this.splitChainInfo.originalBarcode; this.closeSplitChainDialog(); // ç»ä¸ç¹æ¶é´è®©å¼¹çªå®å ¨å ³é await this.$nextTick(); try { // ç°å¨æ¾ç¤ºç¡®è®¤å¯¹è¯æ¡ï¼ç¡®ä¿å®å¨æåé¢ await this.$confirm( `ç¡®å®è¦åæ¶æ´ä¸ªæå é¾åï¼\nè¿å°åæ¶ä»æ¡ç ${originalBarcode} å¼å§çæææå æä½ã`, 'åæ¶æå é¾ç¡®è®¤', { confirmButtonText: 'ç¡®å®åæ¶', cancelButtonText: 'åæ³æ³', type: 'warning', center: true, closeOnClickModal: false } ); // ç¨æ·ç¡®è®¤åæ§è¡åæ¶æä½ this.revertSplitLoading = true; const res = await http.post('/api/OutboundBatchPicking/cancel-split-chain', { orderNo: this.scanData.orderNo, palletCode: this.scanData.palletCode, startBarcode: originalBarcode }); console.log('åæ¶æå é¾ååº:', res); if (res.status) { this.$message.success('åæ¶æå 龿å'); await this.loadPalletData(); // å¯éï¼éæ°æå¼æå é¾ä¿¡æ¯å¼¹çªæ¾ç¤ºæ´æ°åçç¶æ // await this.viewSplitChain(originalBarcode); } else { this.$message.error(res.message || 'åæ¶æå é¾å¤±è´¥'); // 失败åéæ°æå¼å¼¹çª await this.viewSplitChain(originalBarcode); } } catch (error) { // ç¨æ·åæ¶æä½ if (error === 'cancel') { console.log('ç¨æ·åæ¶äºæå 龿ä½'); // ç¨æ·åæ¶åéæ°æå¼å¼¹çª await this.viewSplitChain(originalBarcode); } else { console.error('åæ¶æå é¾é误:', error); this.$message.error('åæ¶æå é¾å¤±è´¥: ' + error.message); // åºéåéæ°æå¼å¼¹çª await this.viewSplitChain(originalBarcode); } } finally { this.revertSplitLoading = false; } }, // æ£æ¥æ¡ç æ¯å¦å·²è¢«åæ£ hasPicked(barcode) { return this.pickedList.some(item => item.currentBarcode === barcode); }, // æ ¼å¼åæ¥ææ¶é´ formatDateTime(dateTime) { if (!dateTime) return ''; const date = new Date(dateTime); return `${date.getFullYear()}-${(date.getMonth() + 1).toString().padStart(2, '0')}-${date.getDate().toString().padStart(2, '0')} ${date.getHours().toString().padStart(2, '0')}:${date.getMinutes().toString().padStart(2, '0')}:${date.getSeconds().toString().padStart(2, '0')}`; }, // ååºç¸å ³æ¹æ³ openBatchReturnDialog() { if (!this.currentBatchNo) { this.$message.warning('请å éæ©æ¹æ¬¡'); if (!this.scanData.palletCode) { this.$message.warning('è¯·å æ«ææçç '); return; } this.showBatchReturnDialog = true; this.batchReturnForm.orderNo = this.scanData.orderNo; this.batchReturnForm.batchNo = this.currentBatchNo; this.batchReturnForm.palletCode = this.scanData.palletCode; this.batchReturnForm.unpickedCount = this.summary.unpickedCount; this.batchReturnForm.unpickedQuantity = this.summary.unpickedQuantity; }, @@ -751,33 +921,42 @@ async handleBatchReturnConfirm() { this.batchReturnLoading = true; try { const res = await http.post('/api/BatchOutbound/batch-return-stock', { const res = await http.post('/api/OutboundBatchPicking/return-stock', { orderNo: this.scanData.orderNo, batchNo: this.currentBatchNo palletCode: this.scanData.palletCode }); if (res.status) { this.$message.success('æ¹æ¬¡ååºæå'); this.$message.success('ååºæå'); this.showBatchReturnDialog = false; await this.loadBatchData(); await this.loadPalletData(); } else { this.$message.error(res.message || 'æ¹æ¬¡ååºå¤±è´¥'); this.$message.error(res.message || 'ååºå¤±è´¥'); } } catch (error) { this.$message.error('æ¹æ¬¡ååºå¤±è´¥'); this.$message.error('ååºå¤±è´¥'); } finally { this.batchReturnLoading = false; } }, // åç©ºç®±æ¹æ³ handleEmptyPallet() { this.showEmptyPalletDialog = true; this.emptypalletOutForm.orderNo = this.scanData.orderNo; this.emptypalletOutForm.palletCode = ''; }, async handleEmptyPalletConfirm() { this.emptypalletOutLoading = true; try { const res = await http.post('/api/BatchOutbound/remove-empty-pallet', this.emptypalletOutForm); const res = await http.post('/api/OutboundBatchPicking/remove-empty-pallet', { orderNo: this.emptypalletOutForm.orderNo, palletCode: this.emptypalletOutForm.palletCode }); if (res.status) { this.$message.success('å走空箱æå'); this.showEmptyPalletDialog = false; await this.loadBatchData(); await this.loadPalletData(); } else { this.$message.error(res.message || 'å走空箱失败'); } @@ -788,32 +967,69 @@ } }, // å ¶ä»åææ¹æ³... // æ°æ®å è½½æ¹æ³ async loadPalletData() { if (!this.scanData.orderNo || !this.scanData.palletCode) return; await this.loadUnpickedList(); await this.loadPickedList(); await this.loadPalletStatus(); }, async loadUnpickedList() { try { const res = await http.post('/api/OutboundBatchPicking/pallet-locks', { orderNo: this.scanData.orderNo, palletCode: this.scanData.palletCode }); if (res.status) { this.unpickedList = res.data || []; this.summary.unpickedCount = this.unpickedList.length; this.summary.unpickedQuantity = this.unpickedList.reduce((sum, item) => sum + (item.remainQuantity || 0), 0); } } catch (error) { this.$message.error('å è½½æªæ£éå表失败'); } }, async loadPickedList() { try { const res = await http.post('/api/OutboundBatchPicking/pallet-picked-list', { orderNo: this.scanData.orderNo, palletCode: this.scanData.palletCode }); if (res.status) { this.pickedList = res.data || []; this.summary.pickedCount = this.pickedList.length; } } catch (error) { this.$message.error('å 载已æ£éå表失败'); } }, async loadPalletStatus() { try { const res = await http.post('/api/OutboundBatchPicking/pallet-status', { orderNo: this.scanData.orderNo, palletCode: this.scanData.palletCode }); if (res.status) { this.palletStatus = res.data.statusText || 'æªç¥'; } } catch (error) { this.palletStatus = 'æªç¥'; } }, // æ«ç ç¸å ³æ¹æ³ onPalletScan() { this.scanData.palletCode = this.scanData.palletCode.replace(/\n/g, '').trim(); if (!this.scanData.palletCode) return; this.loadActiveBatch(); this.loadPalletData(); this.$nextTick(() => { this.$refs.barcodeInput.focus(); }); }, async loadActiveBatch() { try { const res = await http.post('/api/BatchOutbound/active-batch', { orderNo: this.scanData.orderNo, palletCode: this.scanData.palletCode }); if (res.status && res.data) { this.currentBatchNo = res.data.batchNo; this.scanData.batchNo = res.data.batchNo; this.selectedBatchNo = res.data.batchNo; await this.loadBatchData(); } } catch (error) { console.log('è·åæ´»è·æ¹æ¬¡å¤±è´¥ï¼å¯è½æç没æå ³èæ¹æ¬¡'); } }, onBarcodeScan() { @@ -853,9 +1069,8 @@ try { for (const row of this.selectedPickedRows) { try { const res = await http.post('/api/BatchOutbound/cancel-picking', { const res = await http.post('/api/OutboundBatchPicking/cancel-picking', { orderNo: this.scanData.orderNo, batchNo: this.currentBatchNo, palletCode: this.scanData.palletCode, barcode: row.currentBarcode }); @@ -867,7 +1082,7 @@ } } this.$message.success('æ¹é忶宿'); await this.loadBatchData(); await this.loadPalletData(); this.selectedPickedRows = []; } catch (error) { this.$message.error('æ¹éåæ¶æä½å¤±è´¥'); @@ -902,10 +1117,9 @@ this.showBatchReturnDialog = false; }, openEmptyPalletDialog() { this.showEmptyPalletDialog = true; this.emptypalletOutForm.orderNo = this.scanData.orderNo; this.emptypalletOutForm.palletCode = ''; onEmptyPalletScan() { if (!this.emptypalletOutForm.palletCode) return; this.emptypalletOutForm.palletCode = this.emptypalletOutForm.palletCode.replace(/\n/g, '').trim(); }, closeEmptyPalletDialog() { @@ -913,9 +1127,8 @@ this.emptypalletOutForm.palletCode = ''; }, onEmptyPalletScan() { if (!this.emptypalletOutForm.palletCode) return; this.emptypalletOutForm.palletCode = this.emptypalletOutForm.palletCode.replace(/\n/g, '').trim(); parentcall() { // æå°åè° } } }) @@ -924,26 +1137,6 @@ <style scoped> .OutboundPicking-container { padding: 20px; } .batch-operations { margin-bottom: 15px; } .batch-actions { display: flex; align-items: center; gap: 10px; } .batch-summary-area { margin-bottom: 15px; } .batch-summary-info { display: flex; gap: 15px; flex-wrap: wrap; } .scanner-form { @@ -977,6 +1170,18 @@ } /* èªå®ä¹å¼¹çªæ ·å¼ */ :deep(.el-message-box) { z-index: 10010 !important; } :deep(.el-overlay) { z-index: 10009 !important; } :deep(.el-message) { z-index: 10011 !important; } .custom-dialog-overlay { position: fixed; top: 0; @@ -987,12 +1192,12 @@ display: flex; align-items: center; justify-content: center; z-index: 9999; z-index: 2000; /* ä¿æä¸ä¸ªåçç z-index */ } .custom-dialog-wrapper { position: relative; z-index: 10000; z-index: 2001; } .custom-dialog { @@ -1058,7 +1263,14 @@ flex-direction: column; align-items: stretch; } /* ç¡®ä¿ç¡®è®¤å¯¹è¯æ¡å¨æåé¢ */ .el-message-box__wrapper { z-index: 10001 !important; } .el-message { z-index: 10002 !important; } .scanner-form .el-input { width: 100%; } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Common/CommonEnum/PalletTypeEnum.cs
@@ -35,4 +35,18 @@ /// </summary> LargestPallet = 4 } public enum PalletStatusEnum { æªå¼å§ = 0, æ£éä¸ = 1, 已宿 = 2, æ ä»»å¡ = 3 } } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Common/OrderEnum/OutboundOrderEnum.cs
@@ -103,4 +103,6 @@ [Description("å ¶ä»åºåºå")] Other = 235 } } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Common/StockEnum/OutLockStockStatusEnum.cs
@@ -35,6 +35,8 @@ å·²æ£é = 3, å·²ååº = 4, } public enum OutLockStockStatusEnum { [Description("å·²åé ")] @@ -67,6 +69,9 @@ [Description("已鿾")] 已鿾 =9, [Description("å·²åèµ°")] å·²åèµ° =10, [Description("æ¤é")] æ¤é = 99 } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Common/StockEnum/StockStatusEmun.cs
@@ -40,6 +40,9 @@ Lock, } /// <summary> /// åºåç¶æï¼ <br/> /// 1ï¼ç»çæå<br/> @@ -98,7 +101,10 @@ [Description("çç¹åºå宿")] çç¹åºå宿 = 32, [Description("ç»çæ¤é")] [Description("å·²æ¸ ç")] å·²æ¸ ç = 33, [Description("ç»çæ¤é")] ç»çæ¤é = 99, [Description("å ¥åºæ¤é")] ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_DTO/Outbound/BatchOutBoundDto.cs
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,169 @@ using SqlSugar; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text; using System.Threading.Tasks; namespace WIDESEA_DTO.Outbound { public class PalletLockInfoDto { public int Id { get; set; } public string OrderNo { get; set; } public string BatchNo { get; set; } public string MaterielCode { get; set; } public string CurrentBarcode { get; set; } public decimal AssignQuantity { get; set; } public decimal PickedQty { get; set; } public int Status { get; set; } public string LocationCode { get; set; } public string PalletCode { get; set; } public bool CanSplit { get; set; } public bool CanPick { get; set; } } #region 请æ±DTO public class ConfirmPickingRequest { [Required(ErrorMessage = "订åå·ä¸è½ä¸ºç©º")] public string OrderNo { get; set; } [Required(ErrorMessage = "æçå·ä¸è½ä¸ºç©º")] public string PalletCode { get; set; } [Required(ErrorMessage = "æ¡ç ä¸è½ä¸ºç©º")] public string Barcode { get; set; } } public class CancelPickingRequest { [Required(ErrorMessage = "订åå·ä¸è½ä¸ºç©º")] public string OrderNo { get; set; } [Required(ErrorMessage = "æçå·ä¸è½ä¸ºç©º")] public string PalletCode { get; set; } [Required(ErrorMessage = "æ¡ç ä¸è½ä¸ºç©º")] public string Barcode { get; set; } public int PickingHistoryId { get; set; } } public class CancelSplitRequest { [Required(ErrorMessage = "订åå·ä¸è½ä¸ºç©º")] public string OrderNo { get; set; } [Required(ErrorMessage = "æçå·ä¸è½ä¸ºç©º")] public string PalletCode { get; set; } [Required(ErrorMessage = "æ°æ¡ç ä¸è½ä¸ºç©º")] public string NewBarcode { get; set; } } public class ReturnStockRequest { [Required(ErrorMessage = "订åå·ä¸è½ä¸ºç©º")] public string OrderNo { get; set; } [Required(ErrorMessage = "æçå·ä¸è½ä¸ºç©º")] public string PalletCode { get; set; } } public class CancelSplitDto { [Required(ErrorMessage = "订åå·ä¸è½ä¸ºç©º")] public string OrderNo { get; set; } [Required(ErrorMessage = "æçå·ä¸è½ä¸ºç©º")] public string PalletCode { get; set; } [Required(ErrorMessage = "æ°æ¡ç ä¸è½ä¸ºç©º")] public string NewBarcode { get; set; } } public class ReturnStockDto { [Required(ErrorMessage = "订åå·ä¸è½ä¸ºç©º")] public string OrderNo { get; set; } [Required(ErrorMessage = "æçå·ä¸è½ä¸ºç©º")] public string PalletCode { get; set; } } public class PalletLocksDto { [Required(ErrorMessage = "订åå·ä¸è½ä¸ºç©º")] public string OrderNo { get; set; } [Required(ErrorMessage = "æçå·ä¸è½ä¸ºç©º")] public string PalletCode { get; set; } } #endregion #region DTOç±» public class PalletPickedInfoDto { public int Id { get; set; } public string OrderNo { get; set; } public int OrderDetailId { get; set; } public string PalletCode { get; set; } public string Barcode { get; set; } public string MaterielCode { get; set; } public decimal PickedQty { get; set; } public DateTime PickTime { get; set; } public string Operator { get; set; } public string LocationCode { get; set; } } public class PalletStatusDto { public string OrderNo { get; set; } public string PalletCode { get; set; } public int Status { get; set; } public string StatusText { get; set; } public int TotalItems { get; set; } public int CompletedItems { get; set; } public int PendingItems { get; set; } } public class SplitPackageInfoDto { public string OrderNo { get; set; } public string PalletCode { get; set; } public string Barcode { get; set; } public string MaterielCode { get; set; } public decimal RemainQuantity { get; set; } public decimal AssignQuantity { get; set; } public decimal PickedQty { get; set; } } public class EmptyPalletRemovalDto { public string OrderNo { get; set; } public string PalletCode { get; set; } public DateTime RemovalTime { get; set; } public string Operator { get; set; } public int CompletedItemsCount { get; set; } public decimal TotalPickedQuantity { get; set; } } public class RemoveEmptyPalletDto { [Required(ErrorMessage = "订åå·ä¸è½ä¸ºç©º")] public string OrderNo { get; set; } [Required(ErrorMessage = "æçå·ä¸è½ä¸ºç©º")] public string PalletCode { get; set; } } #endregion } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_DTO/Outbound/OutboundOrderGetDTO.cs
@@ -2,6 +2,7 @@ using Newtonsoft.Json; using System; using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -37,8 +38,20 @@ { public int OutStockLockInfoId { get; set; } public string MaterielCode { get; set; } public decimal SplitQuantity { get; set; } public string Operator { get; set; } [Required(ErrorMessage = "订åå·ä¸è½ä¸ºç©º")] public string OrderNo { get; set; } [Required(ErrorMessage = "æçå·ä¸è½ä¸ºç©º")] public string PalletCode { get; set; } [Required(ErrorMessage = "åæ¡ç ä¸è½ä¸ºç©º")] public string OriginalBarcode { get; set; } [Range(0.001, double.MaxValue, ErrorMessage = "æå æ°éå¿ é¡»å¤§äº0")] public decimal SplitQuantity { get; set; } } public class ConfirmPickingDto { @@ -77,10 +90,7 @@ public string OrderNo { get; set; } } public class CancelPickingRequest { public int PickingHistoryId { get; set; } } public class BackToStockRequest { @@ -261,6 +271,29 @@ public string Barcode { get; set; } } public class CancelSplitChainDto { [Required(ErrorMessage = "订åå·ä¸è½ä¸ºç©º")] public string OrderNo { get; set; } [Required(ErrorMessage = "æçå·ä¸è½ä¸ºç©º")] public string PalletCode { get; set; } [Required(ErrorMessage = "èµ·å§æ¡ç ä¸è½ä¸ºç©º")] public string StartBarcode { get; set; } } public class SplitPackageChainInfoRequestDto { [Required(ErrorMessage = "订åå·ä¸è½ä¸ºç©º")] public string OrderNo { get; set; } [Required(ErrorMessage = "æ¡ç ä¸è½ä¸ºç©º")] public string Barcode { get; set; } } public class SplitPackageDto { public string OrderNo { get; set; } @@ -295,4 +328,6 @@ //public decimal SplitQuantity { get; set; } //public decimal RemainQuantity { get; set; } } } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_DTO/Task/WMSTaskDTO.cs
@@ -81,4 +81,15 @@ public int[] taskIds { get; set; } } public class GenerateOutboundBatchTasksDto { public string orderNo { get; set; } public int orderDetailId { get; set; } public decimal batchQuantity { get; set; } public string outboundPlatform { get; set; } } } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_IOutboundService/IOutboundBatchPickingService.cs
@@ -1,5 +1,6 @@ using WIDESEA_Core; using WIDESEA_Core.BaseRepository; using WIDESEA_DTO.Outbound; using WIDESEA_Model.Models; namespace WIDESEA_IOutboundService @@ -11,7 +12,18 @@ Task<WebResponseContent> BatchReturnStock(string orderNo, string palletCode); Task<WebResponseContent> CancelPicking(string orderNo, string palletCode, string barcode); Task<WebResponseContent> CancelSplitPackage(string orderNo, string palletCode, string newBarcode); Task<WebResponseContent> CancelSplitPackageChain(string orderNo, string palletCode, string startBarcode); Task<List<Dt_SplitPackageRecord>> GetSplitPackageChain(string orderNo, string startBarcode); Task<string> FindRootBarcode(string orderNo, string startBarcode); Task<WebResponseContent> GetSplitPackageChainInfo(string orderNo, string barcode); Task<WebResponseContent> ConfirmBatchPicking(string orderNo, string palletCode, string barcode); Task<List<PalletLockInfoDto>> GetPalletLockInfos(string orderNo, string palletCode); Task<List<PalletPickedInfoDto>> GetPalletPickedList(string orderNo, string palletCode); Task<PalletStatusDto> GetPalletStatus(string orderNo, string palletCode); Task<SplitPackageInfoDto> GetSplitPackageInfo(string orderNo, string palletCode, string barcode); Task<WebResponseContent> ManualSplitPackage(string orderNo, string palletCode, string originalBarcode, decimal splitQuantity); Task<WebResponseContent> RemoveEmptyPallet(string orderNo, string palletCode); } } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_ITaskInfoService/ITaskService.cs
@@ -48,6 +48,8 @@ WebResponseContent GenerateOutboundTask(int orderDetailId, List<StockSelectViewDTO> stockSelectViews); Task<WebResponseContent> GenerateOutboundBatchTasksAsync(int orderDetailId, decimal batchQuantity, string outStation); } } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_PickingRecord.cs
@@ -122,6 +122,28 @@ public decimal StockBeforeSplit { get; set; } public decimal AssignBeforeSplit { get; set; } } /// <summary> /// 空箱åèµ°è®°å½è¡¨ /// </summary> public class Dt_EmptyPalletRemoval { [SugarColumn(IsPrimaryKey = true, IsIdentity = true)] public int Id { get; set; } public string OrderNo { get; set; } public string PalletCode { get; set; } public DateTime RemovalTime { get; set; } public string Operator { get; set; } public int CompletedItemsCount { get; set; } public decimal TotalPickedQuantity { get; set; } } } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundBatchPickingService.cs
@@ -6,11 +6,13 @@ using System.Text; using System.Threading.Tasks; using WIDESEA_BasicService; using WIDESEA_Common.CommonEnum; using WIDESEA_Common.OrderEnum; using WIDESEA_Common.StockEnum; using WIDESEA_Core; using WIDESEA_Core.BaseRepository; using WIDESEA_Core.BaseServices; using WIDESEA_DTO.Outbound; using WIDESEA_IAllocateService; using WIDESEA_IBasicService; using WIDESEA_IOutboundService; @@ -18,6 +20,7 @@ using WIDESEA_Model.Models; using WIDESEA_Model.Models.Basic; using WIDESEA_Model.Models.Outbound; using static WIDESEA_OutboundService.OutboundBatchPickingService; namespace WIDESEA_OutboundService { @@ -61,7 +64,7 @@ public OutboundBatchPickingService(IRepository<Dt_PickingRecord> BaseDal, IUnitOfWorkManage unitOfWorkManage, IStockInfoService stockInfoService, IStockService stockService, IOutStockLockInfoService outStockLockInfoService, IStockInfoDetailService stockInfoDetailService, ILocationInfoService locationInfoService, IOutboundOrderDetailService outboundOrderDetailService, ISplitPackageService splitPackageService, IOutboundOrderService outboundOrderService, IRepository<Dt_Task> taskRepository, IESSApiService eSSApiService, ILogger<OutboundPickingService> logger, IInvokeMESService invokeMESService, IDailySequenceService dailySequenceService, IAllocateService allocateService) : base(BaseDal) IRepository<Dt_Task> taskRepository, IESSApiService eSSApiService, ILogger<OutboundPickingService> logger, IInvokeMESService invokeMESService, IDailySequenceService dailySequenceService, IAllocateService allocateService, IRepository<Dt_OutboundBatch> outboundBatchRepository) : base(BaseDal) { _unitOfWorkManage = unitOfWorkManage; _stockInfoService = stockInfoService; @@ -78,9 +81,341 @@ _invokeMESService = invokeMESService; _dailySequenceService = dailySequenceService; _allocateService = allocateService; _outboundBatchRepository = outboundBatchRepository; } // <summary> /// è·åæççéå®ä¿¡æ¯ /// </summary> public async Task<List<PalletLockInfoDto>> GetPalletLockInfos(string orderNo, string palletCode) { var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode) .Select(x => new { x.Id, x.OrderNo, x.BatchNo, x.MaterielCode, x.CurrentBarcode, x.AssignQuantity, x.PickedQty, x.Status, x.LocationCode, x.PalletCode }).ToListAsync(); var lockInfoDtos = lockInfos.Select(x => new PalletLockInfoDto { Id = x.Id, OrderNo = x.OrderNo, BatchNo = x.BatchNo, MaterielCode = x.MaterielCode, CurrentBarcode = x.CurrentBarcode, AssignQuantity = x.AssignQuantity, PickedQty = x.PickedQty, Status = x.Status, LocationCode = x.LocationCode, PalletCode = x.PalletCode, CanSplit = (x.Status == (int)OutLockStockStatusEnum.åºåºä¸ && x.AssignQuantity - x.PickedQty > 0), CanPick = (x.Status == (int)OutLockStockStatusEnum.åºåºä¸ && x.PickedQty < x.AssignQuantity) }).ToList(); return lockInfoDtos; } #region æ¥è¯¢æ¹æ³ /// <summary> /// è·åæççå·²æ£éå表 /// </summary> public async Task<List<PalletPickedInfoDto>> GetPalletPickedList(string orderNo, string palletCode) { var pickedList = await Db.Queryable<Dt_PickingRecord>() .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode && !x.IsCancelled) .Select(x => new PalletPickedInfoDto { Id = x.Id, OrderNo = x.OrderNo, OrderDetailId = x.OrderDetailId, PalletCode = x.PalletCode, Barcode = x.Barcode, MaterielCode = x.MaterielCode, PickedQty = x.PickQuantity, PickTime = x.PickTime, Operator = x.Operator, LocationCode = x.LocationCode }) .ToListAsync(); return pickedList; } /// <summary> /// è·åæçç¶æ /// </summary> public async Task<PalletStatusDto> GetPalletStatus(string orderNo, string palletCode) { // è·åæççéå®ä¿¡æ¯ var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode) .ToListAsync(); if (!lockInfos.Any()) { return new PalletStatusDto { OrderNo = orderNo, PalletCode = palletCode, Status = (int)PalletStatusEnum.æ ä»»å¡, StatusText = "æ ä»»å¡", TotalItems = 0, CompletedItems = 0, PendingItems = 0 }; } var totalItems = lockInfos.Count; var completedItems = lockInfos.Count(x => x.Status == (int)OutLockStockStatusEnum.æ£é宿); var pendingItems = lockInfos.Count(x => x.Status == (int)OutLockStockStatusEnum.åºåºä¸); var status = PalletStatusEnum.æ£éä¸; if (pendingItems == 0 && completedItems > 0) { status = PalletStatusEnum.已宿; } else if (pendingItems > 0 && completedItems == 0) { status = PalletStatusEnum.æªå¼å§; } else if (pendingItems > 0 && completedItems > 0) { status = PalletStatusEnum.æ£éä¸; } return new PalletStatusDto { OrderNo = orderNo, PalletCode = palletCode, Status = (int)status, StatusText = GetPalletStatusText(status), TotalItems = totalItems, CompletedItems = completedItems, PendingItems = pendingItems }; } /// <summary> /// è·åæå ä¿¡æ¯ /// </summary> public async Task<SplitPackageInfoDto> GetSplitPackageInfo(string orderNo, string palletCode, string barcode) { // æ¥æ¾éå®ä¿¡æ¯ var lockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode && x.CurrentBarcode == barcode //&& x.Status == (int)OutLockStockStatusEnum.åºåºä¸ ) .FirstAsync(); if (lockInfo == null) throw new Exception("æªæ¾å°ææçéå®ä¿¡æ¯"); // 计ç®å©ä½å¯ææ°é var remainQuantity = lockInfo.AssignQuantity - lockInfo.PickedQty; return new SplitPackageInfoDto { OrderNo = orderNo, PalletCode = palletCode, Barcode = barcode, MaterielCode = lockInfo.MaterielCode, RemainQuantity = remainQuantity, AssignQuantity = lockInfo.AssignQuantity, PickedQty = lockInfo.PickedQty }; } #endregion #region å走空箱é»è¾ /// <summary> /// å走空箱 - æ¸ ç已宿æ£éçæçæ°æ® /// </summary> public async Task<WebResponseContent> RemoveEmptyPallet(string orderNo, string palletCode) { try { _unitOfWorkManage.BeginTran(); // éªè¯æçæ¯å¦å¯ä»¥åèµ°ï¼å¿ é¡»å ¨é¨å®ææ£éï¼ var validationResult = await ValidateEmptyPalletRemoval(orderNo, palletCode); if (!validationResult.IsValid) return WebResponseContent.Instance.Error(validationResult.ErrorMessage); var completedLocks = validationResult.Data; // æ¸ çéå®è®°å½ï¼æ è®°ä¸ºå·²å®æï¼ await CleanupCompletedLocks(completedLocks); // æ´æ°ç¸å ³è®¢åç¶æ await UpdateOrderStatusAfterPalletRemoval(orderNo); // è®°å½æä½åå² // await RecordEmptyPalletRemoval(orderNo, palletCode, completedLocks); _unitOfWorkManage.CommitTran(); return WebResponseContent.Instance.OK("å走空箱æå"); } catch (Exception ex) { _unitOfWorkManage.RollbackTran(); _logger.LogError($"å走空箱失败 - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}"); return WebResponseContent.Instance.Error($"å走空箱失败ï¼{ex.Message}"); } } /// <summary> /// éªè¯ç©ºç®±åèµ°æ¡ä»¶ /// </summary> private async Task<ValidationResult<List<Dt_OutStockLockInfo>>> ValidateEmptyPalletRemoval(string orderNo, string palletCode) { // è·åæççææéå®è®°å½ var lockInfos = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode) .ToListAsync(); if (!lockInfos.Any()) return ValidationResult<List<Dt_OutStockLockInfo>>.Error("该æç没æéå®è®°å½"); // æ£æ¥æ¯å¦ææªå®æçéå®è®°å½ var unfinishedLocks = lockInfos.Where(x => x.Status == (int)OutLockStockStatusEnum.åºåºä¸ || x.Status == (int)OutLockStockStatusEnum.ååºä¸).ToList(); if (unfinishedLocks.Any()) { var unfinishedCount = unfinishedLocks.Count; var unfinishedQty = unfinishedLocks.Sum(x => x.AssignQuantity - x.PickedQty); return ValidationResult<List<Dt_OutStockLockInfo>>.Error( $"æçè¿æ{unfinishedCount}æ¡æªå®æè®°å½ï¼å©ä½æ°é{unfinishedQty}ï¼ä¸è½å走空箱"); } // è·å已宿çéå®è®°å½ var completedLocks = lockInfos.Where(x => x.Status == (int)OutLockStockStatusEnum.æ£é宿).ToList(); if (!completedLocks.Any()) return ValidationResult<List<Dt_OutStockLockInfo>>.Error("该æç没æå·²å®ææ£éçè®°å½"); return ValidationResult<List<Dt_OutStockLockInfo>>.Success(completedLocks); } /// <summary> /// æ¸ ç已宿çéå®è®°å½ /// </summary> private async Task CleanupCompletedLocks(List<Dt_OutStockLockInfo> completedLocks) { foreach (var lockInfo in completedLocks) { // æ è®°éå®è®°å½ä¸ºå·²åèµ°ï¼å¯ä»¥æ°å¢ç¶ææç´æ¥å é¤ï¼æ ¹æ®ä¸å¡éæ±ï¼ // è¿éæä»¬å°å ¶ç¶ææ´æ°ä¸º"å·²åèµ°"ï¼å¹¶è®°å½åèµ°æ¶é´ lockInfo.Status = (int)OutLockStockStatusEnum.å·²åèµ°; lockInfo.Operator = App.User.UserName; await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); // åæ¶æ¸ ç对åºçåºåè®°å½ç¶æ await CleanupStockInfo(lockInfo); } } /// <summary> /// æ¸ çåºåä¿¡æ¯ /// </summary> private async Task CleanupStockInfo(Dt_OutStockLockInfo lockInfo) { var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() .FirstAsync(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId); if (stockDetail != null) { // 妿åºåå·²ç»åºåºå®æï¼æ è®°ä¸ºå·²æ¸ ç if (stockDetail.Status == (int)StockStatusEmun.åºåºå®æ) { stockDetail.Status = (int)StockStatusEmun.å·²æ¸ ç; await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); } } } /// <summary> /// æ´æ°è®¢åç¶æ /// </summary> private async Task UpdateOrderStatusAfterPalletRemoval(string orderNo) { // æ£æ¥è®¢åæ¯å¦æææçé½å·²å®æ var allLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() .Where(x => x.OrderNo == orderNo) .ToListAsync(); var unfinishedPallets = allLocks .GroupBy(x => x.PalletCode) .Where(g => g.Any(x => x.Status == (int)OutLockStockStatusEnum.åºåºä¸ || x.Status == (int)OutLockStockStatusEnum.ååºä¸)) .ToList(); // å¦ææ²¡ææªå®æçæçï¼æ´æ°è®¢åç¶æä¸ºåºåºå®æ if (!unfinishedPallets.Any()) { await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>() .SetColumns(x => new Dt_OutboundOrder { OrderStatus = (int)OutOrderStatusEnum.åºåºå®æ, }) .Where(x => x.OrderNo == orderNo) .ExecuteCommandAsync(); } } /// <summary> /// è®°å½ç©ºç®±åèµ°åå² /// </summary> private async Task RecordEmptyPalletRemoval(string orderNo, string palletCode, List<Dt_OutStockLockInfo> completedLocks) { var removalRecord = new Dt_EmptyPalletRemoval { OrderNo = orderNo, PalletCode = palletCode, RemovalTime = DateTime.Now, Operator = App.User.UserName, CompletedItemsCount = completedLocks.Count, TotalPickedQuantity = completedLocks.Sum(x => x.PickedQty) }; await Db.Insertable(removalRecord).ExecuteCommandAsync(); } #endregion #region è¾ å©æ¹æ³ private string GetPalletStatusText(PalletStatusEnum status) { return status switch { PalletStatusEnum.æªå¼å§ => "æªå¼å§", PalletStatusEnum.æ£éä¸ => "æ£éä¸", PalletStatusEnum.已宿 => "已宿", PalletStatusEnum.æ ä»»å¡ => "æ ä»»å¡", _ => "æªç¥" }; } #endregion #region 忹忣 /// <summary> @@ -216,8 +551,10 @@ #region åæ¶æå #region åæ¶æå - ä¿®å¤çæ¬ /// <summary> /// åæ¶æå /// åæ¶æå - æ¯æå¤æ¬¡æå çæ åµ /// </summary> public async Task<WebResponseContent> CancelSplitPackage(string orderNo, string palletCode, string newBarcode) { @@ -225,15 +562,29 @@ { _unitOfWorkManage.BeginTran(); // æ¥æ¾æå è®°å½å¹¶éªè¯ // 1. æ¥æ¾æå è®°å½å¹¶éªè¯ var validationResult = await ValidateCancelSplitRequest(orderNo, palletCode, newBarcode); if (!validationResult.IsValid) return WebResponseContent.Instance.Error(validationResult.ErrorMessage); var (splitRecord, newLockInfo, newStockDetail) = validationResult.Data; // æ§è¡åæ¶æå é»è¾ await ExecuteCancelSplitLogic(splitRecord, newLockInfo, newStockDetail); // 2. æ¥æ¾åå§éå®ä¿¡æ¯ var originalLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() .FirstAsync(x => x.Id == splitRecord.OutStockLockInfoId); // 3. æ£æ¥è¯¥æ¡ç æ¯å¦è¢«å次æå var childSplitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>() .Where(x => x.OriginalBarcode == newBarcode && !x.IsReverted) .ToListAsync(); if (childSplitRecords.Any()) { return WebResponseContent.Instance.Error("该æ¡ç å·²è¢«åæ¬¡æå ï¼è¯·å åæ¶åç»çæå æä½"); } // 4. æ§è¡åæ¶æå é»è¾ await ExecuteCancelSplitLogic(splitRecord, originalLockInfo, newLockInfo, newStockDetail); _unitOfWorkManage.CommitTran(); @@ -242,10 +593,353 @@ catch (Exception ex) { _unitOfWorkManage.RollbackTran(); _logger.LogError($"åæ¶æå 失败 - OrderNo: {orderNo}, Barcode: {newBarcode}, Error: {ex.Message}"); _logger.LogError($"åæ¶æå 失败 - OrderNo: {orderNo}, PalletCode: {palletCode}, Barcode: {newBarcode}, Error: {ex.Message}"); return WebResponseContent.Instance.Error($"åæ¶æå 失败ï¼{ex.Message}"); } } /// <summary> /// æ§è¡åæ¶æå é»è¾ - ä¿®å¤çæ¬ /// </summary> private async Task ExecuteCancelSplitLogic(Dt_SplitPackageRecord splitRecord, Dt_OutStockLockInfo originalLockInfo, Dt_OutStockLockInfo newLockInfo, Dt_StockInfoDetail newStockDetail) { // 1. æ¢å¤åéå®ä¿¡æ¯ // 注æï¼è¿ééè¦ç´¯å ï¼è䏿¯ç®åçèµå¼ï¼å 为å¯è½æå¤æ¬¡æå originalLockInfo.AssignQuantity += splitRecord.SplitQty; originalLockInfo.OrderQuantity += splitRecord.SplitQty; // 妿åéå®ä¿¡æ¯çç¶ææ¯æ£é宿ï¼éè¦éæ°è®¾ç½®ä¸ºåºåºä¸ if (originalLockInfo.Status == (int)OutLockStockStatusEnum.æ£é宿) { originalLockInfo.Status = (int)OutLockStockStatusEnum.åºåºä¸; } await _outStockLockInfoService.Db.Updateable(originalLockInfo).ExecuteCommandAsync(); // 2. æ¢å¤ååºåæç» var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() .FirstAsync(x => x.Barcode == splitRecord.OriginalBarcode && x.StockId == splitRecord.StockId); originalStock.StockQuantity += splitRecord.SplitQty; // 妿ååºåç¶ææ¯åºåºå®æï¼éè¦éæ°è®¾ç½®ä¸ºåºåºéå® if (originalStock.Status == (int)StockStatusEmun.åºåºå®æ) { originalStock.Status = (int)StockStatusEmun.åºåºéå®; } await _stockInfoDetailService.Db.Updateable(originalStock).ExecuteCommandAsync(); // 3. å 餿°éå®ä¿¡æ¯ await _outStockLockInfoService.Db.Deleteable<Dt_OutStockLockInfo>() .Where(x => x.Id == newLockInfo.Id) .ExecuteCommandAsync(); // 4. å 餿°åºåæç» await _stockInfoDetailService.Db.Deleteable<Dt_StockInfoDetail>() .Where(x => x.Barcode == newLockInfo.CurrentBarcode) .ExecuteCommandAsync(); // 5. æ è®°æå è®°å½ä¸ºå·²æ¤é splitRecord.IsReverted = true; splitRecord.RevertTime = DateTime.Now; splitRecord.RevertOperator = App.User.UserName; await _splitPackageService.Db.Updateable(splitRecord).ExecuteCommandAsync(); // 6. æ£æ¥å¹¶æ´æ°æ¹æ¬¡å订åç¶æ await CheckAndUpdateBatchStatus(originalLockInfo.BatchNo); await CheckAndUpdateOrderStatus(originalLockInfo.OrderNo); } /// <summary> /// éªè¯åæ¶æå è¯·æ± - å¢å¼ºçæ¬ /// </summary> private async Task<ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>> ValidateCancelSplitRequest( string orderNo, string palletCode, string newBarcode) { // æ¥æ¾æå è®°å½ var splitRecord = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>() .Where(x => x.NewBarcode == newBarcode && x.OrderNo == orderNo && !x.IsReverted) .FirstAsync(); if (splitRecord == null) return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("æªæ¾å°æå è®°å½"); // æ¥æ¾æ°éå®ä¿¡æ¯ var newLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() .Where(x => x.CurrentBarcode == newBarcode && x.PalletCode == palletCode && x.OrderNo == orderNo) .FirstAsync(); if (newLockInfo == null) return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("æªæ¾å°æ°éå®ä¿¡æ¯"); // æ£æ¥æ°æ¡ç æ¯å¦å·²è¢«åæ£ var pickingRecord = await Db.Queryable<Dt_PickingRecord>() .Where(x => x.Barcode == newBarcode && !x.IsCancelled) .FirstAsync(); if (pickingRecord != null) return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("该æ¡ç å·²è¢«åæ£ï¼æ æ³åæ¶æå "); // æ£æ¥æ°æ¡ç æ¯å¦è¢«å次æå var childSplitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>() .Where(x => x.OriginalBarcode == newBarcode && !x.IsReverted) .ToListAsync(); if (childSplitRecords.Any()) { var childBarcodes = string.Join(", ", childSplitRecords.Select(x => x.NewBarcode)); return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error( $"该æ¡ç å·²è¢«åæ¬¡æå ï¼çæçæ°æ¡ç ï¼{childBarcodes}ï¼è¯·å åæ¶åç»æå "); } var newStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() .FirstAsync(x => x.Barcode == newBarcode); return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Success((splitRecord, newLockInfo, newStockDetail)); } #endregion #region æ¹éåæ¶æå é¾ /// <summary> /// æ¹éåæ¶æå é¾ - åæ¶æä¸ªæ¡ç åå ¶ææåç»æå /// </summary> public async Task<WebResponseContent> CancelSplitPackageChain(string orderNo, string palletCode, string startBarcode) { try { _unitOfWorkManage.BeginTran(); // 1. æ¥æ¾ææç¸å ³çæå è®°å½ï¼å½¢ææå é¾ï¼ var splitChain = await GetSplitPackageChain(orderNo, startBarcode); if (!splitChain.Any()) return WebResponseContent.Instance.Error("æªæ¾å°æå è®°å½"); // 2. ææå 顺åºååºåæ¶ï¼ä»ææ°çå¼å§åæ¶ï¼ var reversedChain = splitChain.OrderByDescending(x => x.SplitTime).ToList(); foreach (var splitRecord in reversedChain) { await CancelSingleSplitPackage(splitRecord, palletCode); } _unitOfWorkManage.CommitTran(); return WebResponseContent.Instance.OK($"æååæ¶æå é¾ï¼å ±{reversedChain.Count}次æå æä½"); } catch (Exception ex) { _unitOfWorkManage.RollbackTran(); _logger.LogError($"åæ¶æå é¾å¤±è´¥ - OrderNo: {orderNo}, StartBarcode: {startBarcode}, Error: {ex.Message}"); return WebResponseContent.Instance.Error($"åæ¶æå é¾å¤±è´¥ï¼{ex.Message}"); } } /// <summary> /// è·åæå é¾ - æ¥æ¾æä¸ªæ¡ç çæææå è®°å½ï¼å æ¬åç»æå ï¼ /// </summary> public async Task<List<Dt_SplitPackageRecord>> GetSplitPackageChain(string orderNo, string startBarcode) { var allSplitRecords = new List<Dt_SplitPackageRecord>(); var visitedBarcodes = new HashSet<string>(); // 鲿¢å¾ªç¯å¼ç¨ // 使ç¨éåè¿è¡å¹¿åº¦ä¼å æç´¢ var queue = new Queue<string>(); queue.Enqueue(startBarcode); visitedBarcodes.Add(startBarcode); while (queue.Count > 0) { var currentBarcode = queue.Dequeue(); // æ¥æ¾ä»¥å½åæ¡ç ä¸ºåæ¡ç çæææå è®°å½ var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>() .Where(x => x.OriginalBarcode == currentBarcode && x.OrderNo == orderNo && !x.IsReverted) .ToListAsync(); foreach (var record in splitRecords) { // é¿å éå¤å¤ç if (!visitedBarcodes.Contains(record.NewBarcode)) { allSplitRecords.Add(record); queue.Enqueue(record.NewBarcode); visitedBarcodes.Add(record.NewBarcode); } } } return allSplitRecords; } /// <summary> /// åæ¶å个æå è®°å½ /// </summary> private async Task CancelSingleSplitPackage(Dt_SplitPackageRecord splitRecord, string palletCode) { // æ¥æ¾ç¸å ³æ°æ® var newLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() .Where(x => x.CurrentBarcode == splitRecord.NewBarcode && x.PalletCode == palletCode) .FirstAsync(); var newStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() .FirstAsync(x => x.Barcode == splitRecord.NewBarcode); var originalLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() .FirstAsync(x => x.Id == splitRecord.OutStockLockInfoId); // æ§è¡åæ¶é»è¾ await ExecuteCancelSplitLogic(splitRecord, originalLockInfo, newLockInfo, newStockDetail); } #endregion #region æå ä¿¡æ¯æ¥è¯¢å¢å¼º /// <summary> /// è·åæå é¾ä¿¡æ¯ /// </summary> public async Task<WebResponseContent> GetSplitPackageChainInfo(string orderNo, string barcode) { try { var splitChain = await GetSplitPackageChain(orderNo, barcode); var chainInfo = new SplitPackageChainInfoDto { OriginalBarcode = barcode, TotalSplitTimes = splitChain.Count, SplitChain = splitChain.Select(x => new SplitChainItemDto { SplitTime = x.SplitTime, OriginalBarcode = x.OriginalBarcode, NewBarcode = x.NewBarcode, SplitQuantity = x.SplitQty, Operator = x.Operator, IsReverted = x.IsReverted }).ToList() }; return WebResponseContent.Instance.OK("è·åæå", chainInfo); } catch (Exception ex) { _logger.LogError($"è·åæå é¾ä¿¡æ¯å¤±è´¥ - OrderNo: {orderNo}, Barcode: {barcode}, Error: {ex.Message}"); return WebResponseContent.Instance.Error("è·åæå é¾ä¿¡æ¯å¤±è´¥"); } } /// <summary> /// æ¥æ¾æ ¹æ¡ç /// </summary> public async Task<string> FindRootBarcode(string orderNo, string startBarcode) { var currentBarcode = startBarcode; var visited = new HashSet<string>(); while (!string.IsNullOrEmpty(currentBarcode) && !visited.Contains(currentBarcode)) { visited.Add(currentBarcode); // æ¥æ¾å½åæ¡ç æ¯å¦æ¯ç±å ¶ä»æ¡ç æå èæ¥ var parentRecord = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>() .Where(x => x.NewBarcode == currentBarcode && x.OrderNo == orderNo && !x.IsReverted) .FirstAsync(); if (parentRecord == null) { // 没æç¶çº§æå è®°å½ï¼è¯´æè¿æ¯æ ¹æ¡ç return currentBarcode; } currentBarcode = parentRecord.OriginalBarcode; } // 妿åºç°å¾ªç¯å¼ç¨ï¼è¿åèµ·å§æ¡ç return startBarcode; } #endregion #region æ´æ°æ¹æ¬¡ç¶ææ£æ¥ /// <summary> /// æ£æ¥å¹¶æ´æ°æ¹æ¬¡ç¶æ /// </summary> private async Task CheckAndUpdateBatchStatus(string batchNo) { var batch = await _outboundBatchRepository.Db.Queryable<Dt_OutboundBatch>() .FirstAsync(x => x.BatchNo == batchNo); if (batch != null) { // éæ°è®¡ç®æ¹æ¬¡å®ææ°é var batchLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() .Where(x => x.BatchNo == batchNo) .ToListAsync(); var completedQuantity = batchLocks.Where(x => x.Status == (int)OutLockStockStatusEnum.æ£é宿) .Sum(x => x.PickedQty); batch.CompletedQuantity = completedQuantity; // æ´æ°æ¹æ¬¡ç¶æ if (batch.CompletedQuantity >= batch.BatchQuantity) { batch.BatchStatus = (int)BatchStatusEnum.已宿; } else if (batch.CompletedQuantity > 0) { batch.BatchStatus = (int)BatchStatusEnum.æ§è¡ä¸; } else { batch.BatchStatus = (int)BatchStatusEnum.åé ä¸; } await _outboundBatchRepository.Db.Updateable(batch).ExecuteCommandAsync(); } } #endregion #region DTOç±» public class SplitPackageChainInfoDto { public string OriginalBarcode { get; set; } public string RootBarcode { get; set; } // æ°å¢ï¼æ ¹æ¡ç public int TotalSplitTimes { get; set; } public string ChainType { get; set; } // "root" æ "branch" public List<SplitChainItemDto> SplitChain { get; set; } } public class SplitChainItemDto { public DateTime SplitTime { get; set; } public string OriginalBarcode { get; set; } public string NewBarcode { get; set; } public decimal SplitQuantity { get; set; } public string Operator { get; set; } public bool IsReverted { get; set; } } #endregion #endregion @@ -337,11 +1031,11 @@ $"åºåæ°éä¸è¶³ï¼éè¦ï¼{lockInfo.AssignQuantity}ï¼å®é ï¼{stockDetail.StockQuantity}"); var batch = await _outboundBatchRepository.Db.Queryable<Dt_OutboundBatch>() .FirstAsync(x => x.BatchNo == lockInfo.BatchNo); .FirstAsync(x => x.BatchNo == lockInfo.OutboundBatchNo); return ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>.Success((lockInfo, orderDetail, stockDetail, batch)); } private async Task<ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>> ValidateSplitRequest( string orderNo, string palletCode, string originalBarcode, decimal splitQuantity) { @@ -366,42 +1060,7 @@ return ValidationResult<(Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Success((lockInfo, stockDetail)); } private async Task<ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>> ValidateCancelSplitRequest( string orderNo, string palletCode, string newBarcode) { var splitRecord = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>() .Where(x => x.NewBarcode == newBarcode && x.OrderNo == orderNo && !x.IsReverted) .FirstAsync(); if (splitRecord == null) return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("æªæ¾å°æå è®°å½"); var newLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() .Where(x => x.CurrentBarcode == newBarcode && x.PalletCode == palletCode && x.OrderNo == orderNo) .FirstAsync(); if (newLockInfo == null) return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("æªæ¾å°æ°éå®ä¿¡æ¯"); // æ£æ¥æ°æ¡ç æ¯å¦å·²è¢«åæ£ var pickingRecord = await Db.Queryable<Dt_PickingRecord>() .Where(x => x.Barcode == newBarcode && !x.IsCancelled) .FirstAsync(); if (pickingRecord != null) return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Error("该æ¡ç å·²è¢«åæ£ï¼æ æ³åæ¶æå "); var newStockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() .FirstAsync(x => x.Barcode == newBarcode); return ValidationResult<(Dt_SplitPackageRecord, Dt_OutStockLockInfo, Dt_StockInfoDetail)>.Success((splitRecord, newLockInfo, newStockDetail)); } #endregion #region æ ¸å¿é»è¾æ¹æ³ @@ -479,7 +1138,13 @@ Barcode = newBarcode, Status = (int)StockStatusEmun.åºåºéå®, SupplyCode = stockDetail.SupplyCode, Unit = stockDetail.Unit Unit = stockDetail.Unit, BarcodeQty=stockDetail.BarcodeQty, BarcodeUnit=stockDetail.BarcodeUnit, BusinessType=stockDetail.BusinessType, InboundOrderRowNo=stockDetail.InboundOrderRowNo, }; await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync(); @@ -487,23 +1152,39 @@ stockDetail.StockQuantity -= splitQuantity; await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); // å建æ°éå®ä¿¡æ¯ var newLockInfo = new Dt_OutStockLockInfo { OrderNo = lockInfo.OrderNo, OrderDetailId = lockInfo.OrderDetailId, OutboundBatchNo = lockInfo.OutboundBatchNo, BatchNo = lockInfo.BatchNo, MaterielCode = lockInfo.MaterielCode, MaterielName = lockInfo.MaterielName, StockId = lockInfo.StockId, OrderQuantity = splitQuantity, //OriginalQuantity = quantity, AssignQuantity = splitQuantity, PickedQty = 0, LocationCode = lockInfo.LocationCode, PalletCode = palletCode, PalletCode = lockInfo.PalletCode, TaskNum = lockInfo.TaskNum, Status = (int)OutLockStockStatusEnum.åºåºä¸, Unit = lockInfo.Unit, SupplyCode = lockInfo.SupplyCode, OrderType = lockInfo.OrderType, CurrentBarcode = newBarcode, // OriginalLockQuantity = quantity, IsSplitted = 1, ParentLockId = lockInfo.Id, Operator = App.User.UserName, FactoryArea = lockInfo.FactoryArea, lineNo = lockInfo.lineNo, WarehouseCode = lockInfo.WarehouseCode, BarcodeQty = lockInfo.BarcodeQty, BarcodeUnit = lockInfo.BarcodeUnit, }; await _outStockLockInfoService.Db.Insertable(newLockInfo).ExecuteCommandAsync(); // æ´æ°åéå®ä¿¡æ¯ ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService.cs
@@ -95,7 +95,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) : 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) : base(BaseDal) { _mapper = mapper; _unitOfWorkManage = unitOfWorkManage; @@ -113,6 +113,7 @@ _invokeMESService = invokeMESService; _outStockLockInfoService = outStockLockInfoService; _allocateService = allocateService; _OutboundBatchRepository = outboundBatchRepository; } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_TaskInfoService/TaskService_Outbound.cs
@@ -637,7 +637,7 @@ /// <summary> /// åæ¹åé åºå /// </summary> public async Task<WebResponseContent> BatchAllocateStock(string orderNo, int orderDetailId, decimal batchQuantity, string outStation) public async Task<WebResponseContent> GenerateOutboundBatchTasksAsync(int orderDetailId, decimal batchQuantity, string outStation) { try { @@ -647,7 +647,7 @@ List<Dt_OutStockLockInfo> outStockLockInfos = new List<Dt_OutStockLockInfo>(); List<Dt_LocationInfo> locationInfos = new List<Dt_LocationInfo>(); (List<Dt_Task>, List<Dt_StockInfo>?, List<Dt_OutboundOrderDetail>?, List<Dt_OutStockLockInfo>?, List<Dt_LocationInfo>?) result = await BatchAllocateStockDataHandle(orderNo, orderDetailId, batchQuantity, outStation); (List<Dt_Task>, List<Dt_StockInfo>?, List<Dt_OutboundOrderDetail>?, List<Dt_OutStockLockInfo>?, List<Dt_LocationInfo>?) result = await BatchAllocateStockDataHandle(orderDetailId, batchQuantity, outStation); if (result.Item2 != null && result.Item2.Count > 0) { @@ -676,7 +676,7 @@ catch (Exception ex) { _unitOfWorkManage.RollbackTran(); _logger.LogError($"åæ¹åé åºå失败 - OrderNo: {orderNo}, OrderDetailId: {orderDetailId}, Quantity: {batchQuantity}, Error: {ex.Message}"); _logger.LogError($"åæ¹åé åºå失败 - OrderDetailId: {orderDetailId}, Quantity: {batchQuantity}, Error: {ex.Message}"); return WebResponseContent.Instance.Error($"åæ¹åé 失败ï¼{ex.Message}"); } } @@ -685,7 +685,7 @@ /// åæ¹åé åºåæ°æ®å¤ç /// </summary> public async Task<(List<Dt_Task>, List<Dt_StockInfo>?, List<Dt_OutboundOrderDetail>?, List<Dt_OutStockLockInfo>?, List<Dt_LocationInfo>?)> BatchAllocateStockDataHandle(string orderNo, int orderDetailId, decimal batchQuantity, string outStation) BatchAllocateStockDataHandle( int orderDetailId, decimal batchQuantity, string outStation) { List<Dt_Task> tasks = new List<Dt_Task>(); @@ -697,7 +697,11 @@ { throw new Exception("æªæ¾å°åºåºåæç»ä¿¡æ¯"); } var outboundOrder = await _outboundOrderService.Db.Queryable<Dt_OutboundOrder>().FirstAsync(x => x.Id == outboundOrderDetail.OrderId); if(outboundOrder == null) { throw new Exception("æªæ¾å°åºåºåä¿¡æ¯"); } // éªè¯è®¢åæç»ç¶æ if (outboundOrderDetail.OrderDetailStatus > OrderDetailStatusEnum.New.ObjToInt() && outboundOrderDetail.OrderDetailStatus != OrderDetailStatusEnum.AssignOverPartial.ObjToInt()) @@ -732,9 +736,8 @@ if (allocateResult.Item1 != null && allocateResult.Item1.Count > 0) { // åå»ºåæ¹è®°å½ await CreateBatchRecord(orderNo, orderDetailId, batchQuantity, batchNo); Dt_OutboundOrder outboundOrder = await _outboundOrderService.Repository.QueryFirstAsync(x => x.Id == outboundOrderDetail.OrderId); await CreateBatchRecord(outboundOrder.OrderNo, orderDetailId, batchQuantity, batchNo); TaskTypeEnum typeEnum = outboundOrder.OrderType switch { (int)OutOrderTypeEnum.Issue => TaskTypeEnum.Outbound, ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Outbound/OutboundBatchPickingController.cs
@@ -1,7 +1,10 @@ using Microsoft.AspNetCore.Mvc; using WIDESEA_Core; using WIDESEA_Core.BaseController; using WIDESEA_DTO.Outbound; using WIDESEA_IOutboundService; using WIDESEA_Model.Models; using static WIDESEA_OutboundService.OutboundBatchPickingService; namespace WIDESEA_WMSServer.Controllers.Outbound { @@ -12,10 +15,203 @@ { private readonly ISplitPackageService _splitPackageService; private readonly IOutStockLockInfoService _outStockLockInfoService; public OutboundBatchPickingController(IOutboundBatchPickingService service, ISplitPackageService splitPackageService, IOutStockLockInfoService outStockLockInfoService) : base(service) private readonly IOutboundBatchPickingService _outboundBatchPickingService; private readonly ILogger<OutboundBatchPickingController> _logger; public OutboundBatchPickingController(IOutboundBatchPickingService service, ISplitPackageService splitPackageService, IOutStockLockInfoService outStockLockInfoService, IOutboundBatchPickingService outboundBatchPickingService, ILogger<OutboundBatchPickingController> logger) : base(service) { _splitPackageService = splitPackageService; _outStockLockInfoService = outStockLockInfoService; _outboundBatchPickingService = outboundBatchPickingService; _logger = logger; } /// <summary> /// åæ£ç¡®è®¤ /// </summary> [HttpPost("confirm-picking")] public async Task<WebResponseContent> ConfirmPicking([FromBody] ConfirmPickingDto dto) { return await _outboundBatchPickingService.ConfirmBatchPicking(dto.OrderNo, dto.PalletCode, dto.Barcode); } /// <summary> /// 忶忣 /// </summary> [HttpPost("cancel-picking")] public async Task<WebResponseContent> CancelPicking([FromBody] CancelPickingDto dto) { return await _outboundBatchPickingService.CancelPicking(dto.OrderNo, dto.PalletCode, dto.Barcode); } /// <summary> /// åæ¶æå é¾ /// </summary> [HttpPost("cancel-split-chain")] public async Task<WebResponseContent> CancelSplitChain([FromBody] CancelSplitChainDto dto) { return await _outboundBatchPickingService.CancelSplitPackageChain(dto.OrderNo, dto.PalletCode, dto.StartBarcode); } /// <summary> /// è·åæå é¾ä¿¡æ¯ /// </summary> [HttpPost("split-package-chain-info")] public async Task<WebResponseContent> GetSplitPackageChainInfo([FromBody] SplitPackageChainInfoRequestDto dto) { return await _outboundBatchPickingService.GetSplitPackageChainInfo(dto.OrderNo, dto.Barcode); } /// <summary> /// æ¥æ¾å®æ´æå é¾ï¼ä»æ ¹æ¡ç å¼å§ï¼ /// </summary> [HttpPost("find-root-split-chain")] public async Task<WebResponseContent> FindRootSplitChain([FromBody] SplitPackageChainInfoRequestDto dto) { try { // æ¥æ¾æ ¹æ¡ç var rootBarcode = await _outboundBatchPickingService. FindRootBarcode(dto.OrderNo, dto.Barcode); // è·å宿´æå é¾ var splitChain = await _outboundBatchPickingService.GetSplitPackageChain(dto.OrderNo, rootBarcode); var chainInfo = new SplitPackageChainInfoDto { OriginalBarcode = rootBarcode, RootBarcode = rootBarcode, TotalSplitTimes = splitChain.Count, ChainType = "root", SplitChain = splitChain.Select(x => new SplitChainItemDto { SplitTime = x.SplitTime, OriginalBarcode = x.OriginalBarcode, NewBarcode = x.NewBarcode, SplitQuantity = x.SplitQty, Operator = x.Operator, IsReverted = x.IsReverted }).ToList() }; return WebResponseContent.Instance.OK("è·åæå", chainInfo); } catch (Exception ex) { _logger.LogError($"æ¥æ¾å®æ´æå é¾å¤±è´¥ - OrderNo: {dto.OrderNo}, Barcode: {dto.Barcode}, Error: {ex.Message}"); return WebResponseContent.Instance.Error("æ¥æ¾å®æ´æå é¾å¤±è´¥"); } } /// <summary> /// æå¨æå /// </summary> [HttpPost("split-package")] public async Task<WebResponseContent> SplitPackage([FromBody] SplitPackageDto dto) { return await _outboundBatchPickingService.ManualSplitPackage(dto.OrderNo, dto.PalletCode, dto.OriginalBarcode, dto.SplitQuantity); } /// <summary> /// åæ¶æå /// </summary> [HttpPost("cancel-split")] public async Task<WebResponseContent> CancelSplit([FromBody] CancelSplitDto dto) { return await _outboundBatchPickingService.CancelSplitPackage(dto.OrderNo, dto.PalletCode, dto.NewBarcode); } /// <summary> /// åæ¹ååº /// </summary> [HttpPost("return-stock")] public async Task<WebResponseContent> ReturnStock([FromBody] ReturnStockDto dto) { return await _outboundBatchPickingService.BatchReturnStock(dto.OrderNo, dto.PalletCode); } /// <summary> /// è·åæççéå®ä¿¡æ¯ /// </summary> [HttpPost("pallet-locks")] public async Task<WebResponseContent> GetPalletLocks([FromBody] PalletLocksDto dto) { try { var locks = await _outboundBatchPickingService.GetPalletLockInfos(dto.OrderNo, dto.PalletCode); return WebResponseContent.Instance.OK("è·åæå", locks); } catch (Exception ex) { _logger.LogError(ex, $"è·åæçéå®ä¿¡æ¯å¼å¸¸ - OrderNo: {dto.OrderNo}, PalletCode: {dto.PalletCode}"); return WebResponseContent.Instance.Error("ç³»ç»å¼å¸¸ï¼è¯·ç¨åéè¯" + ex.Message); } } /// <summary> /// è·åå·²æ£éå表 /// </summary> [HttpPost("pallet-picked-list")] public async Task<WebResponseContent> GetPalletPickedList([FromBody] PalletLocksDto dto) { try { var pickedList = await _outboundBatchPickingService.GetPalletPickedList(dto.OrderNo, dto.PalletCode); return WebResponseContent.Instance.OK("è·åæå", pickedList); } catch (Exception ex) { _logger.LogError(ex, $"è·åå·²æ£éå表å¼å¸¸ - OrderNo: {dto.OrderNo}, PalletCode: {dto.PalletCode}"); return WebResponseContent.Instance.Error("ç³»ç»å¼å¸¸ï¼è¯·ç¨åéè¯" + ex.Message); } } /// <summary> /// è·åæçç¶æ /// </summary> [HttpPost("pallet-status")] public async Task<WebResponseContent> GetPalletStatus([FromBody] PalletLocksDto dto) { try { var status = await _outboundBatchPickingService.GetPalletStatus(dto.OrderNo, dto.PalletCode); return WebResponseContent.Instance.OK("è·åæå", status); } catch (Exception ex) { _logger.LogError(ex, $"è·åæçç¶æå¼å¸¸ - OrderNo: {dto.OrderNo}, PalletCode: {dto.PalletCode}"); return WebResponseContent.Instance.Error("ç³»ç»å¼å¸¸ï¼è¯·ç¨åéè¯" + ex.Message); } } /// <summary> /// è·åæå ä¿¡æ¯ /// </summary> [HttpPost("split-package-info")] public async Task<WebResponseContent> GetSplitPackageInfo([FromBody] SplitPackageInfoDto dto) { try { var info = await _outboundBatchPickingService.GetSplitPackageInfo(dto.OrderNo, dto.PalletCode, dto.Barcode); return WebResponseContent.Instance.OK("è·åæå", info); } catch (Exception ex) { _logger.LogError(ex, $"è·åæå ä¿¡æ¯å¼å¸¸ - OrderNo: {dto.OrderNo}, PalletCode: {dto.PalletCode}, Barcode: {dto.Barcode}"); return WebResponseContent.Instance.Error("ç³»ç»å¼å¸¸ï¼è¯·ç¨åéè¯" +ex.Message); } } /// <summary> /// å走空箱 /// </summary> [HttpPost("remove-empty-pallet")] public async Task<WebResponseContent> RemoveEmptyPallet([FromBody] RemoveEmptyPalletDto dto) { try { var result = await _outboundBatchPickingService.RemoveEmptyPallet(dto.OrderNo, dto.PalletCode); return result; } catch (Exception ex) { _logger.LogError(ex, $"å走空箱å¼å¸¸ - OrderNo: {dto.OrderNo}, PalletCode: {dto.PalletCode}"); return WebResponseContent.Instance.Error("ç³»ç»å¼å¸¸ï¼è¯·ç¨åéè¯" +ex.Message); } } } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/TaskInfo/TaskController.cs
@@ -88,5 +88,17 @@ return Service.GenerateOutboundTask(orderDetailId, stockSelectViews); } /// <summary> /// åæ¹çæåºåºä»»å¡ /// </summary> /// <param name="data"></param> /// <returns></returns> [HttpPost, HttpGet, Route("GenerateOutboundBatchTasks"), AllowAnonymous] public async Task<WebResponseContent> GenerateOutboundBatchTasks([FromBody] GenerateOutboundBatchTasksDto data) { return await Service.GenerateOutboundBatchTasksAsync(data.orderDetailId,data.batchQuantity, data.outboundPlatform); } } }