ÏîÄ¿´úÂë/WIDESEA_WMSClient/config/buttons.js
@@ -180,6 +180,16 @@ onClick: function () { } }, { name: "èæåºå ¥åº", icon: 'el-icon-setting', class: '', value: 'NoStockOut', type: 'success', onClick: function () { } }, ] export default buttons ÏîÄ¿´úÂë/WIDESEA_WMSClient/package.json
@@ -16,6 +16,7 @@ "core-js": "^3.6.5", "echarts": "^5.0.2", "element-plus": "^2.2.14", "element-ui": "^2.15.14", "jsbarcode": "^3.11.6", "less": "^4.1.1", "qrcode": "^1.5.4", ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/outbound/extend/NoStockOut.vue
@@ -5,29 +5,85 @@ :lazy="true" width="65%" :padding="20" title="æ åºååºåº" title="èæåºå ¥åº" class="custom-vol-box" > <div> <!-- 忮鿩åºåï¼å¯è¾å ¥æç´¢ï¼ --> <el-form :inline="true" :model="orderForm" style="margin-bottom: 20px; align-items: flex-end;"> <el-form-item label="åºåºåæ®:" name="outboundOrderId"> <el-select v-model="orderForm.outboundOrderId" placeholder="è¯·éæ©ææ«æåºåºåæ®å·" clearable style="width: 220px; margin-right: 10px;" @change="handleOrderChange" @visible-change="handleOutboundVisibleChange" :loading="loading" filterable :filter-method="filterOutboundOrders" @input="handleOutboundScanInput" ref="outboundSelectRef" > <el-option v-for="order in filteredOutboundOrders" :key="order.id" :label="order.orderNo" :value="order.id" ></el-option> </el-select> </el-form-item> <el-form-item label="éè´åæ®:" name="purchaseOrderId"> <el-select v-model="orderForm.purchaseOrderId" placeholder="è¯·éæ©ææ«æéè´åæ®å·" clearable style="width: 220px; margin-right: 10px;" @change="handleOrderChange" @visible-change="handlePurchaseVisibleChange" :loading="loading" filterable :filter-method="filterPurchaseOrders" @input="handlePurchaseScanInput" ref="purchaseSelectRef" > <el-option v-for="order in filteredPurchaseOrders" :key="order.id" :label="order.orderNo" :value="order.id" ></el-option> </el-select> </el-form-item> </el-form> <!-- 䏿¹è¾å ¥æ¡ --> <el-form :inline="true" :model="formData" ref="formData" style="margin-bottom: 20px; align-items: flex-end;"> <el-form :inline="true" :model="formData" ref="formRef" style="margin-bottom: 20px; align-items: flex-end;"> <el-form-item label="æ«ææ¡ç :" style="width: 80%" prop="barcode" name="barcode" :rules="[{ required: true, message: 'è¯·æ«ææè¾å ¥æ¡ç ', trigger: 'blur' }]" > <el-input ref="barcodeInput" ref="barcodeInputRef" v-model="formData.barcode" placeholder="è¯·ä½¿ç¨æ«ç æªæ«ææ¡ç ï¼ææå¨è¾å ¥" @keyup.enter="handleScan" autofocus class="custom-input" :disabled="!orderForm.outboundOrderId || !orderForm.purchaseOrderId" ></el-input> </el-form-item> <el-form-item> <el-button type="primary" size="small" @click="handleScan" class="custom-button"> <i class="el-icon-search"></i> ç¡®è®¤æ«æ <el-button type="primary" size="small" @click="handleScan" class="custom-button" :disabled="!orderForm.outboundOrderId || !orderForm.purchaseOrderId || loading" > <Search /> ç¡®è®¤æ«æ </el-button> </el-form-item> </el-form> @@ -37,27 +93,23 @@ <el-card shadow="hover" style="margin-bottom: 10px; border: none;" class="custom-card"> <div class="card-header"> <span class="header-title">å·²æ«ææ¡ç å表ï¼å ±{{ scannedBarcodes.length }}æ¡ï¼</span> <el-button class="clear-all-btn" @click="clearAll" :disabled="scannedBarcodes.length === 0" >æ¸ ç©ºææ</el-button> </div> <div class="card-body"> <el-scrollbar height="400px" class="custom-scrollbar"> <!-- ä½¿ç¨ transition-group å 裹以å®ç°å¨ç» --> <transition-group name="barcode-item-transition"> <div class="barcode-item" v-for="(barcode, index) in scannedBarcodes" :key="barcode" :data-index="index"> <span class="barcode-text">{{ index + 1 }}. {{ barcode }}</span> <div class="barcode-item" v-for="(item, index) in scannedBarcodes" :key="item.barcode" :data-index="index"> <span class="barcode-text">{{ index + 1 }}. {{ item.barcode }}</span> <el-button class="delete-btn" @click="removeItem(index)" >å é¤</el-button> @click="removeItem(index, item.barcode)" icon="Delete" circle :disabled="loading" ></el-button> </div> </transition-group> <div class="empty-tip" v-if="scannedBarcodes.length === 0"> <i class="el-icon-information"></i> <span>ææ æ«æè®°å½ï¼è¯·æ«ææ¡ç </span> <span>ææ æ«æè®°å½ï¼è¯·å éæ©åæ®åæ«ææ¡ç </span> </div> </el-scrollbar> </div> @@ -67,10 +119,16 @@ <template #footer> <div class="footer-actions"> <el-button type="primary" size="small" @click="submit" :disabled="scannedBarcodes.length === 0" class="submit-btn"> <i class="el-icon-check"></i> æäº¤åºåº <el-button type="primary" size="small" @click="submit" :disabled="scannedBarcodes.length === 0 || !orderForm.outboundOrderId || !orderForm.purchaseOrderId || loading" class="submit-btn" > <Check /> æäº¤åºåº </el-button> <el-button type="text" size="small" @click="showDetailBox = false" class="cancel-btn"> <el-button type="text" size="small" @click="showDetailBox = false" class="cancel-btn" :disabled="loading"> åæ¶ </el-button> </div> @@ -79,99 +137,307 @@ </div> </template> <script> <script setup> import { ref, reactive, onMounted, nextTick } from 'vue'; import { ElMessage } from 'element-plus'; import VolBox from "@/components/basic/VolBox.vue"; import http from '@/api/http'; export default { components: { VolBox }, data() { return { showDetailBox: false, formData: { barcode: "", }, scannedBarcodes: [], }; }, methods: { open() { this.showDetailBox = true; this.scannedBarcodes = []; this.formData.barcode = ""; this.$nextTick(() => { this.$refs.barcodeInput.focus(); }); }, // ååºå¼æ°æ® const showDetailBox = ref(false); const orderForm = reactive({ outboundOrderId: "", purchaseOrderId: "" }); const formData = reactive({ barcode: "", }); const scannedBarcodes = ref([]); const outboundOrders = ref([]); const purchaseOrders = ref([]); const filteredOutboundOrders = ref([]); const filteredPurchaseOrders = ref([]); const loading = ref(false); handleScan() { const barcode = this.formData.barcode.trim(); if (!barcode) { this.$refs.barcodeInput.focus(); return; } if (this.scannedBarcodes.includes(barcode)) { this.$message.warning(`æ¡ç ${barcode} å·²æ«æè¿ï¼è¯·å¿é夿«æ`); this.formData.barcode = ""; this.$refs.barcodeInput.focus(); return; } // 模æ¿å¼ç¨ const formRef = ref(null); const barcodeInputRef = ref(null); const outboundSelectRef = ref(null); // æ°å¢ï¼åºåºåselectçref const purchaseSelectRef = ref(null); // æ°å¢ï¼éè´åselectçref this.scannedBarcodes.push(barcode); this.formData.barcode = ""; // ç¨äºé²æ¢è¾å ¥äºä»¶åchangeäºä»¶å²çªçé const isProcessingScan = ref(false); this.$nextTick(() => { this.$refs.barcodeInput.focus(); }); }, // ç»ä»¶æè½½æ¶åå§åè¿æ»¤åçå表 onMounted(() => { filteredOutboundOrders.value = [...outboundOrders.value]; filteredPurchaseOrders.value = [...purchaseOrders.value]; }); removeItem(index) { this.scannedBarcodes.splice(index, 1); }, clearAll() { this.$confirm("ç¡®å®è¦æ¸ ç©ºæææ«æè®°å½åï¼", "æç¤º", { confirmButtonText: "ç¡®å®", cancelButtonText: "åæ¶", type: "warning", }).then(() => { this.scannedBarcodes = []; }).catch(() => { this.$refs.barcodeInput.focus(); }); }, submit() { if (this.scannedBarcodes.length === 0) { this.$message.warning("è¯·å æ«æè³å°ä¸æ¡æ¡ç "); this.$refs.barcodeInput.focus(); return; } const params = { barcodes: this.scannedBarcodes, }; this.http .post("/api/OutboundOrder/NoStockOut", params, "æ°æ®å¤çä¸...") .then((res) => { if (!res.status) { this.$message.error(res.message); this.$refs.barcodeInput.focus(); return; } this.$message.success("åºåºæå"); this.showDetailBox = false; this.$emit("parentCall", ($vue) => { $vue.refresh(); }); }) .catch((err) => { this.$message.error(`请æ±å¤±è´¥ï¼${err.message || "æªç¥é误"}`); this.$refs.barcodeInput.focus(); }); }, }, // æå¼å¼¹çª const open = () => { showDetailBox.value = true; scannedBarcodes.value = []; formData.barcode = ""; orderForm.outboundOrderId = ""; orderForm.purchaseOrderId = ""; nextTick(() => { barcodeInputRef.value?.focus(); }); }; // å è½½åºåºåæ®å表 const loadOutboundOrders = async () => { if (outboundOrders.value.length > 0) return; try { loading.value = true; const res = await http.get("/api/OutboundPicking/GetAvailablePickingOrders"); if (res.code === 0) { outboundOrders.value = res.data.map(orderNo => ({ id: orderNo, orderNo: orderNo })); filteredOutboundOrders.value = [...outboundOrders.value]; } else { ElMessage.error("å è½½åºåºåæ®å¤±è´¥ï¼" + (res.message || 'æªç¥é误')); } } catch (error) { ElMessage.error("å è½½åºåºåæ®å¼å¸¸ï¼" + error.message); } finally { loading.value = false; } }; // å è½½éè´åæ®å表 const loadPurchaseOrders = async () => { if (purchaseOrders.value.length > 0) return; try { loading.value = true; const res = await http.get("/api/OutboundPicking/GetAvailablePurchaseOrders"); if (res.status === true) { purchaseOrders.value = res.data.map(orderNo => ({ id: orderNo, orderNo: orderNo })); filteredPurchaseOrders.value = [...purchaseOrders.value]; } else { ElMessage.error("å è½½éè´åæ®å¤±è´¥ï¼" + (res.message || 'æªç¥é误')); } } catch (error) { ElMessage.error("å è½½éè´åæ®å¼å¸¸ï¼" + error.message); } finally { loading.value = false; } }; // åºåºåæ®è¿æ»¤æ¹æ³ const filterOutboundOrders = (value) => { if (!value) { filteredOutboundOrders.value = [...outboundOrders.value]; } else { const lowerValue = value.toLowerCase(); filteredOutboundOrders.value = outboundOrders.value.filter(order => order.orderNo.toLowerCase().includes(lowerValue) ); } }; // éè´åæ®è¿æ»¤æ¹æ³ const filterPurchaseOrders = (value) => { if (!value) { filteredPurchaseOrders.value = [...purchaseOrders.value]; } else { const lowerValue = value.toLowerCase(); filteredPurchaseOrders.value = purchaseOrders.value.filter(order => order.orderNo.toLowerCase().includes(lowerValue) ); } }; // åºåºåæ®ä¸ææ¡æ¾ç¤º/éèæ¶è§¦å const handleOutboundVisibleChange = (visible) => { if (visible) { loadOutboundOrders(); } else { // å½ä¸ææ¡å ³éæ¶ï¼å¦ææ¯æ«ææä½å¯¼è´çï¼æ¸ 空è¾å ¥æ¡ if (isProcessingScan.value) { nextTick(() => { outboundSelectRef.value?.clearInput(); isProcessingScan.value = false; }); } } }; // éè´åæ®ä¸ææ¡æ¾ç¤º/éèæ¶è§¦å const handlePurchaseVisibleChange = (visible) => { if (visible) { loadPurchaseOrders(); } else { // å½ä¸ææ¡å ³éæ¶ï¼å¦ææ¯æ«ææä½å¯¼è´çï¼æ¸ 空è¾å ¥æ¡ if (isProcessingScan.value) { nextTick(() => { purchaseSelectRef.value?.clearInput(); isProcessingScan.value = false; }); } } }; // 忮鿩ååæ¶è§¦å const handleOrderChange = () => { // å¦ææ¯æå¨éæ©ï¼å䏿¸ 空è¾å ¥æ¡ isProcessingScan.value = false; scannedBarcodes.value = []; nextTick(() => { barcodeInputRef.value?.focus(); }); }; /** * å¤çåºåºåæ«æè¾å ¥ * @param {string} scanText - æ«ææªè¾å ¥çææ¬ */ const handleOutboundScanInput = async (scanText) => { // é¿å å¤ç空å符串æç±changeäºä»¶è§¦åçå é¨æä½ if (!scanText || isProcessingScan.value) return; // æ¥æ¾å®å ¨å¹é ç订å const matchedOrder = outboundOrders.value.find(order => order.orderNo === scanText); if (matchedOrder) { isProcessingScan.value = true; // æ 记为æ£å¨å¤çæ«æ // æå¨èµå¼ orderForm.outboundOrderId = matchedOrder.id; ElMessage.success(`æåéæ©åºåºåï¼${matchedOrder.orderNo}`); // å»¶è¿èç¦å°ä¸ä¸ä¸ªè¾å ¥æ¡ï¼ç¡®ä¿èµå¼å®æ setTimeout(() => { purchaseSelectRef.value?.focus(); }, 100); } // å¦ææ²¡æå¹é 项ï¼ä¸åä»»ä½äºï¼è®©el-selectä¿æè¿æ»¤ç¶æï¼ç¨æ·å¯ä»¥æå¨éæ© }; /** * å¤çéè´åæ«æè¾å ¥ * @param {string} scanText - æ«ææªè¾å ¥çææ¬ */ const handlePurchaseScanInput = async (scanText) => { if (!scanText || isProcessingScan.value) return; const matchedOrder = purchaseOrders.value.find(order => order.orderNo === scanText); if (matchedOrder) { isProcessingScan.value = true; // æ 记为æ£å¨å¤çæ«æ orderForm.purchaseOrderId = matchedOrder.id; ElMessage.success(`æåéæ©éè´åï¼${matchedOrder.orderNo}`); setTimeout(() => { barcodeInputRef.value?.focus(); }, 100); } }; // æ«ææ¡ç å¤ç const handleScan = async () => { if (!formRef.value) return; await formRef.value.validateField('barcode'); const barcode = formData.barcode.trim(); if (scannedBarcodes.value.some(item => item.barcode === barcode)) { ElMessage.warning(`æ¡ç ${barcode} å·²æ«æè¿ï¼è¯·å¿é夿«æ`); formData.barcode = ""; nextTick(() => barcodeInputRef.value?.focus()); return; } try { loading.value = true; const res = await http.post("/api/OutboundPicking/BarcodeValidate", { outOder: orderForm.outboundOrderId, inOder: orderForm.purchaseOrderId, barCode: barcode }); if (res.status === true) { scannedBarcodes.value.push({ barcode }); ElMessage.success("æ«ææå"); formData.barcode = ""; } else { ElMessage.error("æ«æå¤±è´¥ï¼" + (res.message || 'éªè¯å¤±è´¥')); } } catch (error) { ElMessage.error("æ«æéªè¯å¼å¸¸ï¼" + error.message); } finally { loading.value = false; nextTick(() => barcodeInputRef.value?.focus()); } }; // ç§»é¤åæ¡æ«æè®°å½ const removeItem = async (index, barcode) => { try { loading.value = true; const res = await http.post("/api/OutboundPicking/DeleteBarcode", { outOder: orderForm.outboundOrderId, inOder: orderForm.purchaseOrderId, barCode: barcode }); if (res.status === true) { scannedBarcodes.value.splice(index, 1); ElMessage.success("å 餿å"); } else { ElMessage.error("å é¤å¤±è´¥ï¼" + (res.message || 'å é¤å¤±è´¥')); } } catch (error) { ElMessage.error("å 餿¡ç å¼å¸¸ï¼" + error.message); } finally { loading.value = false; } }; // æäº¤åºåº const submit = async () => { if (scannedBarcodes.value.length === 0) { ElMessage.warning("è¯·å æ«æè³å°ä¸æ¡æ¡ç "); return; } const barcodes = scannedBarcodes.value.map(item => item.barcode); try { loading.value = true; const res = await http.post("/api/OutboundPicking/NoStockOutSubmit", { OutOderSubmit: orderForm.outboundOrderId, InOderSubmit: orderForm.purchaseOrderId, BarCodeSubmit: barcodes }); if (res.status === true) { ElMessage.success("åºåºæäº¤æå"); showDetailBox.value = false; } else { ElMessage.error("åºåºæäº¤å¤±è´¥ï¼" + (res.message || 'æäº¤å¤±è´¥')); } } catch (error) { ElMessage.error("åºåºæäº¤å¼å¸¸ï¼" + error.message); } finally { loading.value = false; } }; // æ´é²ç»ç¶ç»ä»¶çæ¹æ³ defineExpose({ open }); </script> <style scoped> @@ -219,31 +485,22 @@ font-size: 15px; color: #333; } .clear-all-btn { color: #f56c6c; font-size: 13px; transition: color 0.2s; } .clear-all-btn:hover { color: #e53e3e; background: rgba(245, 108, 108, 0.1); } .card-body { padding: 0; } /* èªå®ä¹æ»å¨æ¡ */ .custom-scrollbar ::v-deep .el-scrollbar__thumb { .custom-scrollbar :deep(.el-scrollbar__thumb) { background: rgba(0, 0, 0, 0.2); border-radius: 4px; width: 4px; } .custom-scrollbar ::v-deep .el-scrollbar__bar:hover .el-scrollbar__thumb { .custom-scrollbar :deep(.el-scrollbar__bar:hover .el-scrollbar__thumb) { background: rgba(0, 0, 0, 0.3); width: 6px; } .custom-scrollbar ::v-deep .el-scrollbar__wrap { .custom-scrollbar :deep(.el-scrollbar__wrap) { overflow-x: hidden; } @@ -260,7 +517,7 @@ } /* ä¸ºå¥æ°è¡æ·»å 轻微çèæ¯è²ï¼å¢å¼ºå¯è¯»æ§ */ .barcode-item:nth-child(odd) { background-color: #e1e1e1; background-color: #f9f9f9; } .barcode-text { flex: 1; @@ -274,16 +531,15 @@ .delete-btn { color: #ea1919; font-size: 20px; font-size: 16px; transition: all 0.2s; transform: scale(0.8); opacity: 0.7; } .barcode-item:hover .delete-btn { opacity: 1; transform: scale(1); } .delete-btn:hover { color: #f56c6c !important; /* ä½¿ç¨ !important è¦ç Element UI é»è®¤æ ·å¼ */ color: #f56c6c !important; background: rgba(245, 108, 108, 0.1); } @@ -296,22 +552,22 @@ flex-direction: column; align-items: center; justify-content: center; gap: 15px; } .empty-tip i { font-size: 40px; margin-bottom: 15px; color: #dcdfe6; } /* èªå®ä¹è¾å ¥æ¡ */ .custom-input ::v-deep .el-input__inner { .custom-input :deep(.el-input__inner) { border-radius: 6px; border-color: #e4e7ed; transition: all 0.3s; height: 36px; line-height: 36px; } .custom-input ::v-deep .el-input__inner:focus { .custom-input :deep(.el-input__inner:focus) { border-color: #409eff; box-shadow: 0 0 0 3px rgba(64, 158, 255, 0.1); } @@ -355,7 +611,7 @@ </style> <style> /* ... (å ¨å±æ ·å¼é¨åä¿æä¸å) ... */ /* å ¨å±æ ·å¼é¨åä¿æä¸å */ .text-button:hover { background-color: #f0f9eb !important; } ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/outbound/extend/outOrderDetail.vue
@@ -217,13 +217,6 @@ icon: "el-icon-s-grid", }, { prop: "NoStockOut", title: "æ åºååºåº", type: "icon", width: 100, icon: "el-icon-setting", }, { prop: "viewDetail", title: "åºåºè¯¦ç»", type: "icon", ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/outbound/outboundOrder.js
@@ -5,10 +5,11 @@ import { ElDialog, ElForm, ElFormItem, ElInput, ElButton, ElMessage, ElSelect, ElOption } from 'element-plus'; import gridBody from './extend/outOrderDetail.vue' import gridHeader from './extend/NoStockOut.vue' let extension = { components: { //æ¥è¯¢ç颿©å±ç»ä»¶ gridHeader: '', gridHeader: gridHeader, gridBody: gridBody, gridFooter: '', //æ°å»ºãç¼è¾å¼¹åºæ¡æ©å±ç»ä»¶ @@ -326,6 +327,13 @@ this.$refs.gridBody.open(row); } }); let TaskHandCompletedBtn = this.buttons.find(x => x.value == 'NoStockOut'); if (TaskHandCompletedBtn) { TaskHandCompletedBtn.onClick = function () { this.$refs.gridHeader.open(); } } }, onInited() { //æ¡æ¶åå§åé ç½®å @@ -335,15 +343,16 @@ searchBefore(param) { //ç颿¥è¯¢å,å¯ä»¥ç»param.wheresæ·»å æ¥è¯¢åæ° //è¿åfalseï¼åä¸ä¼æ§è¡æ¥è¯¢ let wheres = [{ 'name': 'orderType', 'value': '0', 'displayType': 'text'}]; param.wheres.push(...wheres); return true; let wheres = [{ 'name': 'orderType', 'value': '0', 'displayType': 'text' }]; param.wheres.push(...wheres); return true; return true; }, ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_IOutboundService/IOutboundPickingService.cs
@@ -25,6 +25,27 @@ Task<WebResponseContent> ConfirmPicking(string orderNo, string palletCode, string barcode); Task<WebResponseContent> ReturnRemaining(string orderNo, string palletCode, string reason); /// <summary> /// åæ®æ¥æ¾ /// </summary> /// <returns></returns> WebResponseContent GetAvailablePurchaseOrders(); WebResponseContent GetAvailablePickingOrders(); /// <summary> /// æ«ç éªè¯ /// </summary> WebResponseContent BarcodeValidate(NoStockOutModel noStockOut); /// <summary> /// æ¡ç å é¤ /// </summary> /// <param name="noStockOut"></param> /// <returns></returns> WebResponseContent DeleteBarcode(NoStockOutModel noStockOut); WebResponseContent NoStockOutSubmit(NoStockOutSubmit noStockOutSubmit); Task<WebResponseContent> RemoveEmptyPallet(string orderNo, string palletCode); } } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Model/Models/Inbound/Dt_InboundOrderDetail.cs
@@ -140,5 +140,10 @@ /// </summary> [SugarColumn(IsNullable = true, ColumnDescription = "夿³¨")] public string Remark { get; set; } /// <summary> /// èæåºå ¥åºæ°é /// </summary> public decimal NoStockOutQty { get; set; } } } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/Dt_OutboundOrderDetail.cs
@@ -149,5 +149,11 @@ public decimal AllocatedQuantity { get; set; } public string BatchAllocateStatus { get; set; } /// <summary> /// èæåºå ¥åºæ°é /// </summary> public decimal NoStockOutQty { get; set; } } } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Model/Models/Outbound/NoStockOutModel.cs
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,46 @@ using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace WIDESEA_Model.Models { /// <summary> /// èæåºå ¥åºå®ä½ç±» /// </summary> public class NoStockOutModel { /// <summary> /// å ¥åºå /// </summary> public string inOder { get; set; } /// <summary> /// åºåºå /// </summary> public string outOder { get; set; } /// <summary> /// æ¡ç /// </summary> public string barCode { get; set; } } /// <summary> /// æäº¤åºåºå®ä½ç±» /// </summary> public class NoStockOutSubmit { /// <summary> /// æäº¤å ¥åºå /// </summary> public string InOderSubmit { get; set; } /// <summary> /// æäº¤åºåºå /// </summary> public string OutOderSubmit { get; set; } /// <summary> /// æäº¤æ¡ç éå /// </summary> public List<string> BarCodeSubmit { get; set; } } } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs
@@ -26,6 +26,7 @@ using WIDESEA_DTO.Outbound; using WIDESEA_IAllocateService; using WIDESEA_IBasicService; using WIDESEA_IInboundService; using WIDESEA_IOutboundService; using WIDESEA_IStockService; using WIDESEA_Model.Models; @@ -53,6 +54,9 @@ private readonly IInvokeMESService _invokeMESService; private readonly IDailySequenceService _dailySequenceService; private readonly IAllocateService _allocateService; private readonly IRepository<Dt_InboundOrder> _inboundOrderRepository; private readonly IInboundOrderDetailService _inboundOrderDetailService; private readonly ILogger<OutboundPickingService> _logger; @@ -73,7 +77,7 @@ public OutboundPickingService(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_InboundOrder> inboundOrderRepository,IInboundOrderDetailService inboundOrderDetailService) : base(BaseDal) { _unitOfWorkManage = unitOfWorkManage; _stockInfoService = stockInfoService; @@ -90,6 +94,8 @@ _invokeMESService = invokeMESService; _dailySequenceService = dailySequenceService; _allocateService = allocateService; _inboundOrderRepository = inboundOrderRepository; _inboundOrderDetailService = inboundOrderDetailService; } @@ -2395,9 +2401,212 @@ return WebResponseContent.Instance.OK("æ£é确认æå", new { SplitResults = new List<SplitResult>() }); } #region èæåºå ¥åº public WebResponseContent GetAvailablePurchaseOrders() { List<Dt_InboundOrder> InOders = _inboundOrderRepository.QueryData().Where(x => x.OrderStatus != InOrderStatusEnum.å ¥åºå®æ.ObjToInt()).ToList(); List<string> InOderCodes = InOders.Select(x => x.UpperOrderNo).ToList(); return WebResponseContent.Instance.OK("æå",data: InOderCodes); } public WebResponseContent GetAvailablePickingOrders() { List<Dt_OutboundOrder> outOders = _outboundOrderService.Db.Queryable<Dt_OutboundOrder>().Where(x => x.OrderStatus != OutOrderStatusEnum.åºåºå®æ.ObjToInt()).ToList(); List<string> outOderCodes = outOders.Select(x => x.UpperOrderNo).ToList(); return WebResponseContent.Instance.OK("æå", data: outOderCodes); } public WebResponseContent BarcodeValidate(NoStockOutModel noStockOut) { try { Dt_InboundOrder inboundOrder = Db.Queryable<Dt_InboundOrder>().Where(x => x.UpperOrderNo == noStockOut.inOder && x.OrderStatus != InOrderStatusEnum.å ¥åºå®æ.ObjToInt()).Includes(x => x.Details).First(); if(inboundOrder == null) { return WebResponseContent.Instance.Error($"æªæ¾å°éè´åï¼{noStockOut.inOder}"); } var matchedDetail = inboundOrder.Details.FirstOrDefault(detail => detail.Barcode == noStockOut.barCode && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt()); if (matchedDetail == null) { return WebResponseContent.Instance.Error($"å¨éè´å {noStockOut.inOder} 䏿ªæ¾å°æ¡ç 为 {noStockOut.barCode} çæç»ã"); } matchedDetail.NoStockOutQty = 0; Dt_OutboundOrder outboundOrder = Db.Queryable<Dt_OutboundOrder>().Where(x => x.UpperOrderNo == noStockOut.outOder && x.OrderStatus != OutOrderStatusEnum.åºåºå®æ.ObjToInt()).Includes(x => x.Details).First(); if (outboundOrder == null) { return WebResponseContent.Instance.Error($"æªæ¾å°åºåºåï¼{noStockOut.inOder}"); } var matchedCode = outboundOrder.Details.FirstOrDefault(detail => detail.MaterielCode == matchedDetail.MaterielCode && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt()); if (matchedCode == null) { return WebResponseContent.Instance.Error($"å¨åºåºåçç©æç¼ç 䏿ªæ¾å°ä¸éè´åä¸ç{matchedDetail.MaterielCode} 对åºçç©æã"); } matchedCode.NoStockOutQty = 0; //å©ä½å ¥åºæ°éå³èæåºå ¥åºå©ä½å¯åºæ°é decimal outQuantity = matchedDetail.OrderQuantity - matchedDetail.ReceiptQuantity; if(outQuantity == 0) { return WebResponseContent.Instance.Error($"该éè´åä¸çæ¡ç 对åºçå¯åºæ°é为0"); } if (matchedCode.OrderQuantity < outQuantity) { return WebResponseContent.Instance.Error($"该éè´åä¸çæ¡ç 对åºçå¯åºæ°éè¶ åºåºåºååºåºæ°é{matchedDetail.OrderQuantity - matchedCode.OrderQuantity},䏿»¡è¶³æ´å åºåº"); } //åæ®åºåºé宿°é matchedDetail.NoStockOutQty += outQuantity; matchedCode.NoStockOutQty += outQuantity; if ((matchedCode.LockQuantity + matchedCode.NoStockOutQty) > matchedCode.OrderQuantity) { return WebResponseContent.Instance.Error($"åºåºåæç»æ°é溢åº{matchedCode.LockQuantity - matchedCode.OrderQuantity}"); } matchedDetail.OrderDetailStatus = OrderDetailStatusEnum.Inbounding.ObjToInt(); matchedCode.OrderDetailStatus = OrderDetailStatusEnum.Outbound.ObjToInt(); _unitOfWorkManage.BeginTran(); _inboundOrderDetailService.UpdateData(matchedDetail); _outboundOrderDetailService.UpdateData(matchedCode); _unitOfWorkManage.CommitTran(); return WebResponseContent.Instance.OK(); } catch(Exception ex) { _unitOfWorkManage.RollbackTran(); return WebResponseContent.Instance.Error(ex.Message); } } public WebResponseContent DeleteBarcode(NoStockOutModel noStockOut) { try { Dt_InboundOrder inboundOrder = Db.Queryable<Dt_InboundOrder>().Where(x => x.UpperOrderNo == noStockOut.inOder && x.OrderStatus != InOrderStatusEnum.å ¥åºå®æ.ObjToInt()).Includes(x => x.Details).First(); if (inboundOrder == null) { return WebResponseContent.Instance.Error($"æªæ¾å°éè´åï¼{noStockOut.inOder}"); } var matchedDetail = inboundOrder.Details.FirstOrDefault(detail => detail.Barcode == noStockOut.barCode && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt()); if (matchedDetail == null) { return WebResponseContent.Instance.Error($"å¨éè´å {noStockOut.inOder} 䏿ªæ¾å°æ¡ç 为 {noStockOut.barCode} çæç»ã"); } matchedDetail.NoStockOutQty = 0; Dt_OutboundOrder outboundOrder = Db.Queryable<Dt_OutboundOrder>().Where(x => x.UpperOrderNo == noStockOut.outOder && x.OrderStatus != OutOrderStatusEnum.åºåºå®æ.ObjToInt()).Includes(x => x.Details).First(); if (outboundOrder == null) { return WebResponseContent.Instance.Error($"æªæ¾å°åºåºåï¼{noStockOut.inOder}"); } var matchedCode = outboundOrder.Details.FirstOrDefault(detail => detail.MaterielCode == matchedDetail.MaterielCode && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt()); if (matchedCode == null) { return WebResponseContent.Instance.Error($"å¨åºåºåçç©æç¼ç 䏿ªæ¾å°ä¸éè´åä¸ç{matchedDetail.MaterielCode} 对åºçç©æã"); } matchedCode.NoStockOutQty = 0; _unitOfWorkManage.BeginTran(); _inboundOrderDetailService.UpdateData(matchedDetail); _outboundOrderDetailService.UpdateData(matchedCode); _unitOfWorkManage.CommitTran(); return WebResponseContent.Instance.OK(); } catch(Exception ex) { _unitOfWorkManage.RollbackTran(); return WebResponseContent.Instance.Error(ex.Message); } } public WebResponseContent NoStockOutSubmit(NoStockOutSubmit noStockOutSubmit) { try { Dt_InboundOrder inboundOrder = Db.Queryable<Dt_InboundOrder>().Where(x => x.UpperOrderNo == noStockOutSubmit.InOderSubmit && x.OrderStatus != InOrderStatusEnum.å ¥åºå®æ.ObjToInt()).Includes(x => x.Details).First(); if (inboundOrder == null) { return WebResponseContent.Instance.Error($"æªæ¾å°éè´åï¼{noStockOutSubmit.InOderSubmit}"); } Dt_OutboundOrder outboundOrder = Db.Queryable<Dt_OutboundOrder>().Where(x => x.UpperOrderNo == noStockOutSubmit.OutOderSubmit && x.OrderStatus != OutOrderStatusEnum.åºåºå®æ.ObjToInt()).Includes(x => x.Details).First(); if (outboundOrder == null) { return WebResponseContent.Instance.Error($"æªæ¾å°åºåºåï¼{noStockOutSubmit.OutOderSubmit}"); } List<Dt_InboundOrderDetail> inboundOrderDetails = new List<Dt_InboundOrderDetail>(); List<Dt_OutboundOrderDetail> outboundOrderDetails = new List<Dt_OutboundOrderDetail>(); foreach (var BarCode in noStockOutSubmit.BarCodeSubmit) { var inboundOrderDetail = inboundOrder.Details.FirstOrDefault(detail => detail.Barcode == BarCode && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt()); if(inboundOrderDetail == null) { return WebResponseContent.Instance.Error($"å¨éè´å {noStockOutSubmit.InOderSubmit} 䏿ªæ¾å°æ¡ç 为 {BarCode} çæç»ã"); } var outboundOrderDetail = outboundOrder.Details.FirstOrDefault(detail => detail.MaterielCode == inboundOrderDetail.MaterielCode && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt()); if (outboundOrderDetail == null) { return WebResponseContent.Instance.Error($"å¨åºåºåçç©æç¼ç 䏿ªæ¾å°ä¸éè´åä¸ç{inboundOrderDetail.MaterielCode} 对åºçç©æã"); } inboundOrderDetail.ReceiptQuantity += inboundOrderDetail.NoStockOutQty; inboundOrderDetail.OverInQuantity = inboundOrderDetail.ReceiptQuantity; inboundOrderDetail.OrderDetailStatus = OrderDetailStatusEnum.Over.ObjToInt(); inboundOrderDetails.Add(inboundOrderDetail); outboundOrderDetail.LockQuantity += outboundOrderDetail.NoStockOutQty; outboundOrderDetail.OverOutQuantity = outboundOrderDetail.LockQuantity; if(outboundOrderDetail.OrderQuantity == outboundOrderDetail.OverOutQuantity) { outboundOrderDetail.OrderDetailStatus = OrderDetailStatusEnum.Over.ObjToInt(); } outboundOrderDetails.Add(outboundOrderDetail); } //å¤æå ¥åºåæ®æç»æ¯å¦å ¨é¨æ¯å®æç¶æ bool inoderOver = inboundOrder.Details.Count() == inboundOrder.Details.Select(x => x.OrderDetailStatus == OrderDetailStatusEnum.Over.ObjToInt()).Count(); if (inoderOver) { inboundOrder.OrderStatus = InOrderStatusEnum.å ¥åºå®æ.ObjToInt(); } //夿åºåºåæ®æç»æ¯å¦å ¨é¨æ¯å®æç¶æ bool outOderOver = outboundOrder.Details.Count() == outboundOrder.Details.Select(x => x.OrderDetailStatus == OrderDetailStatusEnum.Over.ObjToInt()).Count(); if (outOderOver) { outboundOrder.OrderStatus = OutOrderStatusEnum.åºåºå®æ.ObjToInt(); } //æ°æ®å¤ç _unitOfWorkManage.BeginTran(); _inboundOrderDetailService.UpdateData(inboundOrderDetails); _outboundOrderDetailService.UpdateData(outboundOrderDetails); _inboundOrderRepository.UpdateData(inboundOrder); _outboundOrderService.UpdateData(outboundOrder); _unitOfWorkManage.CommitTran(); return WebResponseContent.Instance.OK(); } catch(Exception ex) { _unitOfWorkManage.RollbackTran(); return WebResponseContent.Instance.Error(ex.Message); } } #endregion #endregion } #region æ¯æç±»å®ä¹ public class ValidationResult<T> ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/WIDESEA_OutboundService.csproj
@@ -10,6 +10,7 @@ <ProjectReference Include="..\WIDESEA_BasicService\WIDESEA_BasicService.csproj" /> <ProjectReference Include="..\WIDESEA_IAllocateService\WIDESEA_IAllocateService.csproj" /> <ProjectReference Include="..\WIDESEA_IBasicService\WIDESEA_IBasicService.csproj" /> <ProjectReference Include="..\WIDESEA_IInboundService\WIDESEA_IInboundService.csproj" /> <ProjectReference Include="..\WIDESEA_IOutboundService\WIDESEA_IOutboundService.csproj" /> <ProjectReference Include="..\WIDESEA_IRecordService\WIDESEA_IRecordService.csproj" /> <ProjectReference Include="..\WIDESEA_IStockService\WIDESEA_IStockService.csproj" /> ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Outbound/OutboundPickingController.cs
@@ -1,4 +1,5 @@ using Autofac.Core; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using WIDESEA_Core; @@ -113,5 +114,35 @@ { return await Service.CancelPicking(dto.OrderNo,dto.PalletCode,dto.Barcode); } [HttpPost,HttpGet, Route("GetAvailablePurchaseOrders"),AllowAnonymous] public WebResponseContent GetAvailablePurchaseOrders() { return Service.GetAvailablePurchaseOrders(); } [HttpPost, HttpGet, Route("GetAvailablePickingOrders"),AllowAnonymous] public WebResponseContent GetAvailablePickingOrders() { return Service.GetAvailablePickingOrders(); } [HttpPost, HttpGet, Route("BarcodeValidate"), AllowAnonymous] public WebResponseContent BarcodeValidate([FromBody] NoStockOutModel noStockOut) { return Service.BarcodeValidate(noStockOut); } [HttpPost, HttpGet, Route("DeleteBarcode"), AllowAnonymous] public WebResponseContent DeleteBarcode([FromBody] NoStockOutModel noStockOut) { return Service.DeleteBarcode(noStockOut); } [HttpPost, HttpGet, Route("NoStockOutSubmit"), AllowAnonymous] public WebResponseContent NoStockOutSubmit([FromBody] NoStockOutSubmit noStockOutSubmit) { return Service.NoStockOutSubmit(noStockOutSubmit); } } }