ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/check/recheckOrder.js
@@ -7,7 +7,9 @@ *****************************************************************************************/ //æ¤jsæä»¶æ¯ç¨æ¥èªå®ä¹æ©å±ä¸å¡ä»£ç ï¼å¯ä»¥æ©å±ä¸äºèªå®ä¹é¡µé¢æè éæ°é ç½®çæç代ç import gridBody from './extend/StockSelect.vue' import http from '@/api/http.js' import { h,createVNode, render,reactive,ref } from 'vue'; import { ElDialog , ElForm, ElFormItem, ElInput, ElButton, ElMessage ,ElSelect ,ElOption } from 'element-plus'; // å¼å ¥ElMessageï¼è§£å³æç¤ºæ ååº let extension = { components: { //æ¥è¯¢ç颿©å±ç»ä»¶ @@ -20,7 +22,32 @@ modelFooter: '' }, tableAction: '', //æå®æå¼ 表çæé(è¿éå¡«å表å,é»è®¤ä¸ç¨å¡«å) buttons: { view: [], box: [], detail: [] }, //æ©å±çæé® buttons: { view: [ { name: 'ç»ç', type: 'primary', value: 'ç»ç', onClick: function () { // ä¿®å¤1ï¼ç§»é¤æ ç¨rowåæ°ï¼å æ¥å¿è°è¯ console.log('ç»çæé®è¢«ç¹å»ï¼å¼å§æ ¡éª'); const selectedRows = this.$refs.table.getSelected(); // æ ¡éª1ï¼æ¯å¦éä¸è¡ if (selectedRows.length === 0) { console.log('æ ¡éªä¸éè¿ï¼æªéä¸ä»»ä½åæ®'); ElMessage.warning('è¯·éæ©ä¸æ¡åæ®'); return; } // æ ¡éª2ï¼æ¯å¦éä¸åè¡ if (selectedRows.length > 1) { console.log('æ ¡éªä¸éè¿ï¼éä¸å¤è¡åæ®'); ElMessage.warning('åªè½éæ©ä¸æ¡åæ®'); return; } const targetRow = selectedRows[0]; this.$emit('openPalletDialog', targetRow.orderNo); } },], box: [], detail: [] }, //æ©å±çæé® methods: { //ä¸é¢è¿äºæ¹æ³å¯ä»¥ä¿çä¹å¯ä»¥å é¤ onInit() { //æ¡æ¶åå§åé ç½®åï¼ ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/inbound/Dt_AllocateOrder.js
@@ -431,6 +431,15 @@ searchBefore(param) { //ç颿¥è¯¢å,å¯ä»¥ç»param.wheresæ·»å æ¥è¯¢åæ° //è¿åfalseï¼åä¸ä¼æ§è¡æ¥è¯¢ let wheres = [{ 'name': 'orderType', 'value': '117', 'displayType': 'text'}]; param.wheres.push(...wheres); return true; return true; }, searchAfter(result) { ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/inbound/extend/AllocatedPallet.vue
@@ -510,7 +510,7 @@ this.sumError = ''; try { // è°ç¨å端ç»è®¡æ¥å£ï¼æ¿æ¢ä¸ºä½ çå®é æ¥å£è·¯å¾ï¼ const response = await http.post('/api/InboundOrder/UnPalletQuantity?orderNo='+this.docNo, { const response = await http.post('/api/PickingReturn/UnPalletQuantity?orderNo='+this.docNo, { }); @@ -885,7 +885,7 @@ // APIè¯·æ± - æ¿æ¢ä¸ºå®é çAPIè°ç¨ async fetchMaterialData(barcode) { try { const response = await http.post('/api/InboundOrder/BarcodeMaterielGroup', const response = await http.post('/api/PickingReturn/BarcodeMaterielGroup', { palletCode: this.trayBarcode, orderNo: this.docNo, ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/inbound/extend/PickingRetuenPallet.vue
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,1282 @@ <template> <vol-box v-model="groupPalletVisible" :title="'ç»çæä½ - åæ®å·ï¼' + currentDocNo" :height="1000" :width="1100" :padding="20" :modal="true" @open="handleDialogOpen" @close="handleDialogClose"> <div class="barcode-scanner-container"> <!-- ä»åºéæ© - ç´§åå¸å± --> <div class="location-section compact"> <el-form :model="form" :rules="rules" ref="locationForm" class="compact-form"> <el-form-item label="ä»åº" prop="warehouseType" class="location-select compact-item"> <el-select v-model="form.warehouseType" placeholder="è¯·éæ©ä»åº" clearable filterable @change="handleWarehouseChange" style="width: 100%" :loading="warehouseLoading" size="medium"> <el-option v-for="item in warehouseTypes" :key="item.warehouseType" :label="item.warehouseTypeDesc" :value="item.warehouseType" /> </el-select> </el-form-item> </el-form> </div> <!-- ä»åºåºåéæ© - ç´§åå¸å± --> <div class="location-section compact"> <el-form :model="form" :rules="rules" ref="locationForm" class="compact-form"> <el-form-item label="ä»åºåºå" prop="locationType" class="location-select compact-item"> <el-select v-model="form.locationType" placeholder="请å éæ©ä»åº" clearable filterable @change="handleLocationChange" style="width: 100%" :loading="locationLoading" size="medium"> <el-option v-for="item in locationTypes" :key="item.locationType" :label="item.locationTypeDesc" :value="item.locationType" /> </el-select> </el-form-item> </el-form> </div> <!-- æçä¿¡æ¯æ¾ç¤º - ç´§åå¸å± --> <div class="tray-info compact" v-if="trayBarcode"> <i class="el-icon-s-management"></i> å½åæç®±: {{ trayBarcode }} <span class="location-info" v-if="form.warehouseType"> | ä»åº: {{ currentWarehouseName }} </span> <span class="location-info" v-if="form.locationType"> | ä»åºåºå: {{ currentLocationDesc }} </span> </div> <!-- æ«ç åºå - ç´§åå¸å± --> <div class="input-section compact"> <el-card shadow="hover" class="compact-card"> <div slot="header" class="compact-header"> <span><i class="el-icon-scanner"></i> æ«ç åºå</span> <span class="scan-status"> <span class="scan-indicator"></span> {{ form.locationType && form.warehouseType ? 'æ«ç 就绪' : '请å éæ©ä»åºåä»åºåºå' }} </span> </div> <!-- æçæ¡ç è¾å ¥ --> <div class="input-wrapper custom-input-group compact-input"> <div class="input-label">æç®±ç </div> <el-input ref="trayInput" v-model="trayBarcode" placeholder="è¯·æ«ææè¾å ¥æç®±ç åæå车é®" clearable :disabled="!form.locationType || !form.warehouseType" @keyup.enter.native="handleTraySubmit" @clear="handleTrayClear" @input="handleTrayInput" class="custom-input" size="medium"> <template slot="append"> <el-button @click="handleTraySubmit" type="primary" icon="el-icon-position" :disabled="!form.locationType || !trayBarcode || !form.warehouseType" size="medium"> 确认 </el-button> </template> </el-input> </div> <!-- ç©ææ¡ç è¾å ¥ --> <div class="input-wrapper custom-input-group compact-input"> <div class="input-label">ç©ææ¡ç </div> <el-input ref="barcodeInput" v-model="barcode" placeholder="è¯·æ«ææè¾å ¥ç©ææ¡ç åæå车é®" clearable :disabled="!form.locationType || !trayBarcode || !form.warehouseType" @keyup.enter.native="handleBarcodeSubmit" @clear="handleClear" @input="handleBarcodeInput" class="custom-input" size="medium"> <template slot="append"> <el-button :loading="loading" @click="handleBarcodeSubmit" type="primary" icon="el-icon-search" :disabled="!form.locationType || !trayBarcode || !barcode || !from.warehouseType" size="medium"> {{ loading ? 'æ¥è¯¢ä¸...' : 'æ¥è¯¢' }} </el-button> </template> </el-input> </div> <div class="input-tips compact-tips"> <p>æç¤ºï¼è¯·å éæ©ä»åº â éæ©ä»åºåºå â è¾å ¥æç®±ç â è¾å ¥ç©ææ¡ç </p> <p v-if="!form.warehouseType" class="warning-text">â ï¸ è¯·å éæ©ä»åº</p> <p v-if="!form.locationType && !form.warehouseType" class="warning-text">â ï¸ è¯·å éæ©ä»åºåºå</p> <p v-if="form.warehouseType && form.locationType && !trayBarcode" class="warning-text">â ï¸ è¯·å è¾å ¥æç®±ç </p> </div> </el-card> </div> <!-- å è½½ç¶æ --> <div v-if="loading" class="loading compact"> <el-progress :percentage="100" status="success" :show-text="false" /> <p>æ£å¨æ¥è¯¢ç©æä¿¡æ¯...</p> </div> <!-- é误æç¤º --> <div v-if="error" class="error-message compact"> <el-alert :title="error" type="error" show-icon closable @close="error = ''" /> </div> <!-- ç©æå表 - åºå®é«åº¦å¸¦æ»å¨æ¡ --> <div class="material-list compact"> <el-card shadow="hover" class="compact-card"> <div slot="header" class="compact-header"> <span><i class="el-icon-tickets"></i> ç»çæ°æ®</span> <span class="list-actions"> <el-tag type="primary" size="small">å ± {{ materials.length }} æ¡</el-tag> <el-tag type="primary" size="small">æªç»ç {{ totalStockCount }}</el-tag> <el-tag type="primary" size="small">æªå ¥åºæ°é {{ totalStockSum }}{{ uniqueUnit }}</el-tag> <el-tag v-if="trayBarcode" type="success" size="small">æç: {{ trayBarcode }}</el-tag> <el-tag v-if="form.warehouseType" type="info" size="small">ä»åº: {{ currentWarehouseName }}</el-tag> <el-tag v-if="form.locationType" type="info" size="small">åºå: {{ currentLocationDesc }}</el-tag> </span> </div> <div v-if="materials.length === 0" class="empty-state compact"> <i class="el-icon-document"></i> <p v-if="!form.warehouseType">请å éæ©ä»åº</p> <p v-if="!form.locationType">请å éæ©ä»åºåºå</p> <p v-else-if="!trayBarcode">请å è¾å ¥æç®±æ¡ç </p> <p v-else>ææ ç©ææ°æ®ï¼è¯·æ«ææè¾å ¥ç©ææ¡ç </p> </div> <div class="table-container" v-else> <el-table :data="materials" stripe style="width: 100%" height="100%" size="small"> <el-table-column type="index" label="åºå·" width="60" align="center"></el-table-column> <el-table-column prop="barcode" label="æ¡ç " min-width="140" show-overflow-tooltip></el-table-column> <el-table-column prop="materielCode" label="ç©æç¼ç " min-width="150" show-overflow-tooltip></el-table-column> <el-table-column prop="batchNo" label="æ¹æ¬¡" min-width="150" show-overflow-tooltip></el-table-column> <el-table-column prop="stockQuantity" label="æ°é" min-width="130" align="right"></el-table-column> <el-table-column prop="unit" label="åä½" width="80" align="center"></el-table-column> <el-table-column prop="supplyCode" label="ä¾åºå" min-width="130" show-overflow-tooltip></el-table-column> <el-table-column prop="warehouseType" label="ä»åº" min-width="120" show-overflow-tooltip></el-table-column> </el-table> </div> </el-card> </div> </div> <!-- <div slot="footer" class="dialog-footer"> <el-button @click="handleCancel">åæ¶</el-button> <el-button type="primary" @click="handleConfirm">确认</el-button> </div> --> </vol-box> </template> <script> import http from '@/api/http.js'; import VolBox from '@/components/basic/VolBox.vue'; import VolForm from '@/components/basic/VolForm.vue'; import VolTable from '@/components/basic/VolTable.vue'; import { ElLoading, ElMessage, ElMessageBox } from 'element-plus'; import { ref, onMounted, onUnmounted } from 'vue' import InboundOrder from '../../../views/inbound/inboundOrder.vue'; import { th } from 'element-plus/es/locales.mjs'; export default { name: 'BarcodeScanner', components: { VolBox, VolForm, VolTable }, props: { docNo: { type: String, required: true, default: '' }, visible: { type: Boolean, required: true, default: false } }, data() { return { palletVisible: this.visible, trayBarcode: '', barcode: '', materials: [], loading: false, error: '', debugMode: false, currentFocus: 'warehouse', // æ«ç æªç¸å ³åé scanCode: '', lastKeyTime: null, isManualInput: false, isScanning: false, scanTimer: null, manualInputTimer: null, scanTarget: 'tray', // å½åæ«ç ç®æ : tray æ material // åºåç»è®¡ç¸å ³åé totalStockSum: 0, totalStockCount: 0, uniqueUnit: '', sumLoading: false, sumError: '', // ä»åºç¸å ³åé warehouseTypes: [], warehouseLoading: false, // ä»åºåºåç¸å ³åé locationTypes: [], locationLoading: false, form: { warehouseType: null, locationType: null }, rules: { locationType: [ { validator: this.validateLocationType, trigger: 'change' } ], warehouseType: [ { massage: 'è¯·éæ©ä»åº', trigger: 'change' } ] } } }, computed: { groupPalletVisible: { get() { return this.visible; }, set(newVal) { this.$emit('update:visible', newVal); } }, currentDocNo() { return this.docNo; }, // å½åéæ©çä»åºåç§° currentWarehouseName() { const warehouse = this.warehouseTypes.find(item => item.warehouseType === this.form.warehouseType); return warehouse ? warehouse.warehouseTypeDesc : ''; }, // å½åéæ©çä»åºåºåæè¿° currentLocationDesc() { const location = this.locationTypes.find(item => item.locationType === this.form.locationType) return location ? location.locationTypeDesc : '' } }, watch: { visible(newVal, oldVal) { this.palletVisible = newVal; // å½ä» false å为 true æ¶ï¼è¡¨ç¤ºå¼¹æ¡æå¼ if (newVal === true && oldVal === false) { console.log('å¼¹æ¡æå¼ï¼éç½®æ°æ®'); this.resetData(); this.$nextTick(() => { setTimeout(() => { // this.focusTrayInput(); this.initwarehouseTypes(); // åå§åä»åº this.initLocationTypes(); // åå§åä»åºåºå this.fetchStockStatistics(); // å è½½ç»è®¡æ°æ® }, 300); }); } // å½ä» true å为 false æ¶ï¼è¡¨ç¤ºå¼¹æ¡å ³é if (newVal === false && oldVal === true) { console.log('å¼¹æ¡å ³éï¼éç½®æ°æ®'); this.resetData(); } }, palletVisible(newVal) { this.$emit('update:visible', newVal); }, docNo(newVal) { if (newVal) { this.palletForm = { palletCode: '', barcode: '' }; this.backData = []; this.$refs.palletForm?.reset(); this.fetchStockStatistics(); // åæ®å·åäºï¼å·æ°ç»è®¡ } } }, 'form.warehouseType'(newVal) { if (newVal) { this.form.locationType = null; } else { this.locationTypes = []; } }, mounted() { // æ·»å å ¨å±é®ççå¬ document.addEventListener('keypress', this.handleKeyPress); // 使ç¨setTimeoutç¡®ä¿DOMå®å ¨æ¸²æååèç¦ setTimeout(() => { // this.focusTrayInput(); this.focusLocationSelect(); }, 300); }, beforeDestroy() { // æ¸ çäºä»¶çå¬ document.removeEventListener('keypress', this.handleKeyPress); this.clearAllTimers(); }, methods: { /** * èªå®ä¹ä»åºåºåéªè¯ * å 许å¼ä¸º0ï¼å 为0æ¯åæ³çlocationType */ validateLocationType(rule, value, callback) { // æ£æ¥å¼æ¯å¦ä¸ºnullãundefinedæç©ºå符串ï¼ä½å 许æ°å0 if (!this.form.warehouseType) { callback(new Error('请å éæ©ä»åº')); } else if (value === null || value === undefined || value === '') { callback(new Error('è¯·éæ©ä»åºåºå')); } else { callback(); } }, /** * åå§åä»åºåºåæ°æ® */ async initLocationTypes() { this.locationLoading = true; this.error = ''; try { const response = await http.post('/api/LocationInfo/GetLocationTypes'); if (response.status && Array.isArray(response.data)) { this.locationTypes = response.data; if (this.locationTypes.length === 0) { this.error = 'æªè·åå°ä»åºåºåæ°æ®'; } else { // 妿æé»è®¤åºåï¼å¯ä»¥å¨è¿é设置 // this.form.locationType = this.locationTypes[0].locationType; } } else { this.error = 'è·åä»åºåºåæ°æ®å¤±è´¥'; } } catch (error) { console.error('è·åä»åºåºå失败:', error); this.error = `è·åä»åºåºå失败: ${error.message || 'ç½ç»é误'}`; } finally { this.locationLoading = false; } }, /** * åå§åä»åºæ°æ® */ async initwarehouseTypes() { this.warehouseLoading = true; this.error = ''; try { const response = await http.post('/api/Warehouse/GetwarehouseTypes'); if (response.status && Array.isArray(response.data)) { this.warehouseTypes = response.data; if (this.warehouseTypes.length === 0) { this.error = 'æªè·åå°ä»åºæ°æ®'; } else { // 妿æé»è®¤åºåï¼å¯ä»¥å¨è¿é设置 // this.form.locationType = this.locationTypes[0].locationType; } } else { this.error = 'è·åä»åºæ°æ®å¤±è´¥'; } } catch (error) { console.error('è·åä»åºå¤±è´¥:', error); this.error = `è·åä»åºå¤±è´¥: ${error.message || 'ç½ç»é误'}`; } finally { this.warehouseLoading = false; } }, /** * ä»åºåºååæ´å¤ç */ handleLocationChange(value) { console.log('éæ©ä»åºåºå:', value, 'ç±»å:', typeof value, this.currentLocationDesc); // ç«å³æ¸ é¤éè¯¯ä¿¡æ¯ this.error = ''; // æå¨è§¦å表åéªè¯æ´æ° this.$nextTick(() => { if (this.$refs.locationForm) { // æ¸ é¤è¯¥å段çéªè¯ç¶æï¼ç¶åéæ°éªè¯ this.$refs.locationForm.clearValidate('locationType'); // çæå»¶è¿åéæ°éªè¯ï¼ç¡®ä¿DOMå·²æ´æ° setTimeout(() => { this.$refs.locationForm.validateField('locationType', (errorMsg) => { if (!errorMsg && (value === 0 || value)) { console.log('ä»åºåºåéªè¯éè¿:', value); // åºåéæ©åï¼èªå¨èç¦å°æçè¾å ¥æ¡ this.focusLocationSelect(); } }); }, 100); } }); }, /** * ä»åºåæ´å¤ç */ handleWarehouseChange(value) { console.log('éæ©ä»åº:', value, 'ç±»å:', typeof value, this.currentWarehouseName); // ç«å³æ¸ é¤éè¯¯ä¿¡æ¯ this.error = ''; // æå¨è§¦å表åéªè¯æ´æ° this.$nextTick(() => { if (this.$refs.locationForm) { // æ¸ é¤è¯¥å段çéªè¯ç¶æï¼ç¶åéæ°éªè¯ this.$refs.locationForm.clearValidate('warehouseType'); // çæå»¶è¿åéæ°éªè¯ï¼ç¡®ä¿DOMå·²æ´æ° setTimeout(() => { this.$refs.locationForm.validateField('warehouseType', (errorMsg) => { if (!errorMsg && (value === 0 || value)) { console.log('ä»åºéªè¯éè¿:', value); this.focusLocationSelect(); } }); }, 100); } }); }, async fetchStockStatistics() { // åæ®å·ä¸ºç©ºæ¶ä¸æ¥è¯¢ if (!this.docNo) { this.sumError = 'åæ®å·ä¸ºç©ºï¼æ æ³ç»è®¡'; return; } this.sumLoading = true; this.sumError = ''; try { // è°ç¨å端ç»è®¡æ¥å£ï¼æ¿æ¢ä¸ºä½ çå®é æ¥å£è·¯å¾ï¼ const response = await http.post('/api/PickingReturn/UnPalletQuantity?orderNo=' + this.docNo, { }); // ç»å®æ°æ®ï¼å¹é PalletSumQuantityDTO ç»æï¼ if (response.data) { this.totalStockSum = response.data.stockSumQuantity || 0; // æ»åºåæ°é this.totalStockCount = response.data.stockCount || 0; // æ»åºåè®°å½æ° this.uniqueUnit = response.data.uniqueUnit || ''; // 计éåä½ } } catch (err) { this.sumError = 'ç»è®¡å 载失败'; this.totalStockSum = 0; this.totalStockCount = 0; console.error('åºåç»è®¡æ¥è¯¢å¼å¸¸ï¼', err); } finally { this.sumLoading = false; } }, /** * 表åéªè¯ */ async validateForm() { return new Promise((resolve) => { if (!this.$refs.locationForm) { this.error = 'è¡¨åæªåå§å'; this.$message.warning('请å éæ©ä»åºåºå'); resolve(false); return; } this.$refs.locationForm.validate((valid) => { if (valid) { this.error = ''; resolve(true); } else { // æå¨æ£æ¥locationTypeï¼æ£ç¡®å¤çå¼ä¸º0çæ åµ if (!this.from.warehouseType) { this.error = '请å éæ©ä»åº'; } else if (this.form.locationType === null || this.form.locationType === undefined || this.form.locationType === '') { this.error = '请å éæ©ä»åºåºå'; //this.$message.warning('请å éæ©ä»åºåºå'); } else { // 妿å¼åå¨ï¼å æ¬0ï¼ï¼ä½éªè¯ä¸éè¿ï¼å¯è½æ¯å ¶ä»éªè¯é误 this.error = 'è¯·æ£æ¥è¡¨å填忝妿£ç¡®'; } resolve(false); } }); }); }, focusWarehouseSelect() { if (this.$refs.locationForm) { const selectEl = this.$el.querySelector('.location-select:first-child .el-input__inner'); if (selectEl) { selectEl.focus(); this.currentFocus = 'warehouse'; } } }, // èç¦å°ä»åºåºåéæ© focusLocationSelect() { if (this.$refs.locationForm) { const selectEl = this.$el.querySelector('.location-select:nth-child(2) .el-input__inner'); if (selectEl) { selectEl.focus(); this.currentFocus = 'location'; } } }, // èç¦å°æçè¾å ¥æ¡ focusTrayInput() { if (this.$refs.trayInput && this.$refs.trayInput.$el) { const inputEl = this.$refs.trayInput.$el.querySelector('input'); if (inputEl) { inputEl.focus(); this.currentFocus = 'tray'; this.scanTarget = 'tray'; } } }, // èç¦å°ç©æè¾å ¥æ¡ focusBarcodeInput() { if (this.$refs.barcodeInput && this.$refs.barcodeInput.$el) { const inputEl = this.$refs.barcodeInput.$el.querySelector('input'); if (inputEl) { inputEl.focus(); this.currentFocus = 'material'; this.scanTarget = 'material'; inputEl.select(); console.log('ç©æè¾å ¥æ¡å 容已éä¸'); } } }, // éç½®æææ°æ® resetData() { console.log('éç½®å¼¹æ¡æ°æ®'); this.trayBarcode = ''; this.barcode = ''; this.materials = []; this.loading = false; this.error = ''; this.scanCode = ''; this.lastKeyTime = null; this.isManualInput = false; this.isScanning = false; this.currentFocus = 'warehouse'; this.scanTarget = 'tray'; this.clearAllTimers(); this.totalStockSum = 0; this.totalStockCount = 0; this.sumLoading = false; this.sumError = ''; this.form = { warehouseType: null, locationType: null } this.warehouseTypes = []; this.locationTypes = []; // æ¸ é¤è¡¨åéªè¯ç¶æ this.$nextTick(() => { if (this.$refs.locationForm) { this.$refs.locationForm.clearValidate(); } }); }, // æ¸ é¤ææè®¡æ¶å¨ clearAllTimers() { if (this.manualInputTimer) { clearTimeout(this.manualInputTimer); this.manualInputTimer = null; } if (this.scanTimer) { clearTimeout(this.scanTimer); this.scanTimer = null; } }, // å¼¹æ¡æå¼æ¶éç½®æ°æ® handleDialogOpen() { console.log('å¼¹æ¡æå¼ï¼éç½®æ°æ®'); this.resetData(); // 使ç¨setTimeoutç¡®ä¿DOMå®å ¨æ¸²æååèç¦ this.$nextTick(() => { setTimeout(() => { this.initwarehouseTypes(); this.initLocationTypes(); // åå§åä»åºåºå // ç¡®ä¿è¡¨åå¼ç¨åå¨ååèç¦ if (this.$refs.locationForm) { this.focusWarehouseSelect(); } else { // å¦æè¡¨åå¼ç¨è¿ä¸åå¨ï¼ç¨åéè¯ setTimeout(() => { this.focusWarehouseSelect(); }, 500); } }, 300); }); }, // å¼¹æ¡å ³éæ¶éç½®æ°æ® handleDialogClose() { console.log('å¼¹æ¡å ³éï¼éç½®æ°æ®'); this.resetData(); }, // åæ¶æé® handleCancel() { this.palletVisible = false; }, // 确认æé® async handleConfirm() { if (!await this.validateForm()) return; if (this.materials.length === 0) { this.$message.warning('请è³å°æ·»å ä¸ä¸ªç©æ'); return; } if (!this.trayBarcode) { this.$message.warning('请è¾å ¥æçæ¡ç '); return; } const result = { warehouseType: this.form.warehouseType, warehouseName: this.currentWarehouseName, locationType: this.form.locationType, locationDesc: this.currentLocationDesc, trayBarcode: this.trayBarcode, materials: this.materials, docNo: this.docNo }; // 触åç¶ç»ä»¶ç back-success äºä»¶ this.$emit('back-success', result); this.palletVisible = false; }, // å¤çæçè¾å ¥ handleTrayInput() { // æ 记为æå¨è¾å ¥æ¨¡å¼ this.isManualInput = true; this.isScanning = false; // æ¸ é¤ä¹åç计æ¶å¨ if (this.manualInputTimer) { clearTimeout(this.manualInputTimer); } // 设置计æ¶å¨ï¼å¦æä¸æ®µæ¶é´å 没æè¾å ¥ï¼åé置为æ«ç æ¨¡å¼ this.manualInputTimer = setTimeout(() => { this.isManualInput = false; }, 1000); }, // å¤çç©æè¾å ¥ handleBarcodeInput() { // æ 记为æå¨è¾å ¥æ¨¡å¼ this.isManualInput = true; this.isScanning = false; // æ¸ é¤ä¹åç计æ¶å¨ if (this.manualInputTimer) { clearTimeout(this.manualInputTimer); } // 设置计æ¶å¨ï¼å¦æä¸æ®µæ¶é´å 没æè¾å ¥ï¼åé置为æ«ç æ¨¡å¼ this.manualInputTimer = setTimeout(() => { this.isManualInput = false; }, 1000); }, // å¤çæçæ¡ç æäº¤ async handleTraySubmit() { // å ç´æ¥æ£æ¥locationTypeï¼é¿å 表åéªè¯ç弿¥é®é¢ if (!this.form.warehouseType) { this.error = '请å éæ©ä»åº'; return; } if (!this.form.locationType) { this.error = '请å éæ©ä»åºåºå'; //this.$message.warning('请å éæ©ä»åºåºå'); return; } // ç¶ååè¿è¡å®æ´ç表åéªè¯ if (!await this.validateForm()) return; const currentTrayBarcode = this.trayBarcode.trim(); if (!currentTrayBarcode) { this.error = '请è¾å ¥ææ«ææçæ¡ç '; return; } this.error = ''; // 设置æçæ¡ç åï¼èªå¨èç¦å°ç©æè¾å ¥æ¡ this.focusBarcodeInput(); this.$message({ message: `æçæ¡ç 已设置: ${currentTrayBarcode}`, type: 'success', duration: 2000 }); }, // æ¸ é¤æç clearTray() { this.trayBarcode = ''; this.materials = []; this.focusTrayInput(); this.$message({ message: 'æçæ¡ç å·²æ¸ é¤', type: 'info', duration: 2000 }); }, // æ¸ ç©ºæçè¾å ¥ handleTrayClear() { this.error = ''; }, // æ¸ ç©ºè¾å ¥ handleClear() { this.error = ''; this.scanCode = ''; this.isManualInput = false; this.isScanning = false; }, // å¤çç©ææ¡ç æäº¤ async handleBarcodeSubmit() { if (!await this.validateForm()) return; const currentBarcode = this.barcode.trim(); if (!this.trayBarcode) { this.error = '请å è¾å ¥æçæ¡ç '; this.focusTrayInput(); return; } if (!currentBarcode) { this.error = '请è¾å ¥ææ«æç©ææ¡ç '; return; } this.focusBarcodeInput(); this.error = ''; this.loading = true; try { // è°ç¨APIæ¥è¯¢ç©æä¿¡æ¯ const materialData = await this.fetchMaterialData(currentBarcode); if (!materialData || materialData.length === 0) { return; } // æ£æ¥æ¯å¦å·²åå¨ç¸åç©æç¼ç çè®°å½ const exists = this.materials.some(item => item.barcode === this.trayBarcode ); console.log('API:', materialData) if (exists) { this.$message({ message: '该æ¡ç å·²åå¨å½åæççå表ä¸', type: 'warning', duration: 2000 }); } else { materialData.forEach(item => { // 妿ä¸åå¨ï¼æ·»å æ°ç©æ this.materials.push({ ...item, trayCode: this.trayBarcode, locationType: this.form.locationType, locationDesc: this.currentLocationDesc, scanTime: this.formatTime(new Date()) }); }); this.$message({ message: `æåæ·»å æ¡ç : ${currentBarcode}`, type: 'success', duration: 2000 }); this.fetchStockStatistics(); // æ¸ ç©ºç©æè¾å ¥æ¡å¹¶ä¿æèç¦ this.barcode = ''; this.scanCode = ''; // æ¸ ç©ºæ«ç ç¼å this.isScanning = false; setTimeout(() => { this.focusBarcodeInput(); }, 100); } } catch (err) { this.error = err.message || 'æ¥è¯¢æ¡ç ä¿¡æ¯å¤±è´¥ï¼è¯·éè¯'; this.focusBarcodeInput(); setTimeout(() => { // éä¸è¾å ¥æ¡å çé误å 容ï¼ç¡®ä¿focus宿忧è¡ï¼ const inputEl = this.$refs.barcodeInput?.$el?.querySelector('input'); if (inputEl) inputEl.select(); }, 100); } finally { this.loading = false; } }, // APIè¯·æ± - æ¿æ¢ä¸ºå®é çAPIè°ç¨ async fetchMaterialData(barcode) { try { const response = await http.post('/api/PickingReturn/BarcodeMaterielGroup', { palletCode: this.trayBarcode, orderNo: this.docNo, barcodes: barcode, locationTypeDesc: this.currentLocationDesc, locationType: this.form.locationType, // æ·»å ä»åºåºåä¿¡æ¯ warehouseType: this.form.warehouseType } ); let materialData; if (typeof response.data === 'string') { try { materialData = JSON.parse(response.data); } catch (e) { } } else { // 妿è¿åçæ¯JSON对象ï¼ç´æ¥ä½¿ç¨ materialData = response.data; } if (!response.status) { this.error = response.message || 'æ¥è¯¢æ¡ç ä¿¡æ¯å¤±è´¥ï¼è¯·éè¯'; } return materialData; } catch (error) { console.error('APIè°ç¨å¤±è´¥:', error); } }, // å¤çæ«ç æªè¾å ¥ handleKeyPress(event) { // å¦ææ¯æå¨è¾å ¥æ¨¡å¼ï¼ä¸å¤çæ«ç æªé»è¾ if (this.isManualInput) { return; } const key = event.key; const currentTime = new Date().getTime(); // 忽ç¥ç´æ¥æä¸çå车é®ï¼ç±handleBarcodeSubmitå¤çï¼ if (key === 'Enter') { if (this.scanCode.length > 0) { // 黿¢é»è®¤å车è¡ä¸ºï¼é¿å 表åæäº¤ event.preventDefault(); // æ«ç 宿ï¼èªå¨è§¦åæ¥è¯¢ this.isScanning = false; // æ ¹æ®å½åæ«ç ç®æ 设置ç¸åºçè¾å ¥æ¡å¼ if (this.scanTarget === 'tray') { this.trayBarcode = this.scanCode; this.handleTraySubmit(); } else if (this.scanTarget === 'material') { this.barcode = this.scanCode; this.handleBarcodeSubmit(); } } this.scanCode = ''; this.lastKeyTime = null; return; } // æå»ºæ«ç å 容ï¼å¿«éè¿ç»è¾å ¥è§ä¸ºæ«ç ï¼ if (this.lastKeyTime && currentTime - this.lastKeyTime < 50) { this.scanCode += key; this.isScanning = true; } else { this.scanCode = key; this.isScanning = true; } // 设置计æ¶å¨ï¼å¦æä¸æ®µæ¶é´å 没æè¾å ¥ï¼åéç½®æ«æç¶æ if (this.scanTimer) { clearTimeout(this.scanTimer); } this.scanTimer = setTimeout(() => { this.isScanning = false; }, 100); this.lastKeyTime = currentTime; }, // å é¤ç©æ removeMaterial(index) { this.$confirm('ç¡®å®è¦å é¤è¿æ¡ç©æè®°å½å?', 'æç¤º', { confirmButtonText: 'ç¡®å®', cancelButtonText: 'åæ¶', type: 'warning' }).then(() => { this.materials.splice(index, 1); this.$message({ type: 'success', message: 'å 餿å!' }); this.fetchStockStatistics(); }).catch(() => { // åæ¶å é¤ }); }, // æ¸ ç©ºææç©æ clearAllMaterials() { if (this.materials.length === 0) return; this.$confirm('ç¡®å®è¦æ¸ 空ææç©æè®°å½å?', 'æç¤º', { confirmButtonText: 'ç¡®å®', cancelButtonText: 'åæ¶', type: 'warning' }).then(() => { this.materials = []; this.$message({ type: 'success', message: 'å·²æ¸ ç©ºææè®°å½!' }); }).catch(() => { // åæ¶æ¸ ç©º }); }, // æ ¼å¼åæ¶é´ formatTime(date) { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); const day = String(date.getDate()).padStart(2, '0'); const hours = String(date.getHours()).padStart(2, '0'); const minutes = String(date.getMinutes()).padStart(2, '0'); const seconds = String(date.getSeconds()).padStart(2, '0'); return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; } } } </script> <style scoped> .barcode-scanner-container { max-width: 1200px; margin: 0 auto; padding: 10px; display: flex; flex-direction: column; height: 100%; gap: 8px; } /* ç´§åå¸å±æ ·å¼ */ .compact { margin-bottom: 0; } .compact-form { margin-bottom: 0; } .compact-item { margin-bottom: 0; } .compact-card { margin-bottom: 0; } .compact-card>>>.el-card__body { padding: 12px; } .compact-header { display: flex; justify-content: space-between; align-items: center; padding: 0 !important; } .compact-header>>>.el-card__header { padding: 8px 12px; } .compact-input { margin: 8px 0; } .compact-tips { margin-top: 8px; font-size: 11px; } /* ä»åºåºåéæ© - ç´§å */ .location-section.compact { margin-bottom: 8px; } .location-section.compact>>>.el-form-item { margin-bottom: 0; } /* æçä¿¡æ¯ - ç´§å */ .tray-info.compact { padding: 6px 10px; margin-bottom: 8px; font-size: 13px; } /* æ«ç åºå - ç´§å */ .input-section.compact { margin-bottom: 8px; flex-shrink: 0; } /* ç©æå表 - åºå®é«åº¦å¸¦æ»å¨ */ .material-list.compact { flex: 1; min-height: 0; /* éè¦ï¼å 许flexå项æ¶ç¼© */ display: flex; flex-direction: column; } .material-list.compact>>>.el-card { display: flex; flex-direction: column; height: 100%; } .material-list.compact>>>.el-card__body { flex: 1; display: flex; flex-direction: column; padding: 0; min-height: 0; } .table-container { flex: 1; min-height: 0; overflow: hidden; } .material-list.compact>>>.el-table { flex: 1; } .material-list.compact>>>.el-table__body-wrapper { overflow-y: auto; } /* ç´§åçç©ºç¶æ */ .empty-state.compact { padding: 20px 0; flex: 1; display: flex; flex-direction: column; justify-content: center; align-items: center; } .empty-state.compact i { font-size: 36px; margin-bottom: 8px; } .empty-state.compact p { font-size: 13px; } /* å ¶ä»åææ ·å¼è°æ´ */ .page-title { text-align: center; margin-bottom: 15px; } .scan-status { font-size: 12px; color: #67C23A; } .scan-indicator { display: inline-block; width: 8px; height: 8px; border-radius: 50%; background-color: #67C23A; margin-right: 5px; animation: pulse 1.5s infinite; } @keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.4; } 100% { opacity: 1; } } .input-wrapper { position: relative; } .input-tips { margin-top: 6px; color: #909399; } .warning-text { color: #E6A23C; font-weight: bold; } .loading.compact { text-align: center; margin: 10px 0; padding: 5px; } .loading.compact p { margin-top: 5px; color: #409EFF; font-size: 12px; } .error-message.compact { margin: 5px 0; } .error-message.compact>>>.el-alert { padding: 6px 12px; } .list-actions { display: flex; align-items: center; gap: 4px; } .list-actions>>>.el-tag { height: 24px; line-height: 22px; padding: 0 6px; } .clear-all-btn { margin-left: 8px; } .material-code { font-family: 'Courier New', monospace; font-weight: bold; color: #409EFF; } .location-info { color: #606266; font-weight: normal; } .debug-info { background: #f5f7fa; padding: 8px; border-radius: 4px; margin-top: 8px; font-size: 11px; color: #909399; } .small-button { padding: 6px 8px; font-size: 11px; } /* è¾å ¥æ¡ç»æ ·å¼è°æ´ */ .custom-input-group { display: flex; align-items: center; width: 100%; margin: 8px 0; border: 1px solid #DCDFE6; border-radius: 4px; overflow: hidden; background: #fff; } .input-label { padding: 0 12px; background: #F5F7FA; border-right: 1px solid #DCDFE6; color: #606266; font-size: 13px; white-space: nowrap; height: 36px; line-height: 36px; flex-shrink: 0; min-width: 70px; text-align: center; } .input-container { display: flex; flex: 1; align-items: center; } .custom-input { flex: 1; } .custom-input>>>.el-input__inner { border: none; border-radius: 0; height: 36px; line-height: 36px; font-size: 13px; } /* ååºå¼è°æ´ */ @media (max-width: 768px) { .barcode-scanner-container { padding: 5px; } .custom-input-group { flex-direction: column; border: none; } .input-label { width: 100%; border-right: none; border-bottom: 1px solid #DCDFE6; margin-bottom: 5px; } .input-container { width: 100%; border: 1px solid #DCDFE6; border-radius: 4px; } } </style> ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/extension/outbound/extend/outOrderDetail.vue
@@ -24,6 +24,7 @@ <el-link type="primary" size="small" v-if="isBatch === 0" style="float: right; height: 20px" @click="handleOpenPicking" >æ£é</el-link> @@ -31,11 +32,13 @@ type="primary" size="small" style="float: right; height: 20px; margin-right: 10px" v-if="isBatch === 1" @click="handleOpenBatchPicking" >åæ¹æ£é</el-link> <el-link type="primary" size="small" v-if="isBatch === 0" style="float: right; height: 20px; margin-right: 10px" @click="outbound" >ç´æ¥åºåº</el-link @@ -43,6 +46,7 @@ <el-link type="primary" size="small" v-if="isBatch === 1" style="float: right; height: 20px; margin-right: 10px" @click="outboundbatch" >åæ¹åºåº</el-link @@ -133,6 +137,7 @@ data() { return { row: null, isBatch :0, showDetialBox: false, flag: false, currentRow: null, @@ -300,6 +305,8 @@ open(row) { this.row = row; this.showDetialBox = true; console.log(this.row); this.isBatch = row.isBatch; this.getDictionaryData(); this.getData(); }, ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/check/ReCheckOrder.vue
@@ -5,15 +5,30 @@ *ä¸å¡è¯·å¨@/extension/widesea_wcs/order/Dt_CheckOrder.jsæ¤å¤ç¼å --> <template> <view-grid ref="grid" :columns="columns" :editFormFields="editFormFields" <view-grid ref="grid" @openPalletDialog="handleOpenPalletDialog" :columns="columns" :editFormFields="editFormFields" :editFormOptions="editFormOptions" :searchFormFields="searchFormFields" :searchFormOptions="searchFormOptions" :table="table" :extend="extend"> </view-grid> <!-- 2. ç»çå¼¹çªï¼ç¡®ä¿propsåäºä»¶ç»å®æ£ç¡® --> <PalletDialog v-model:visible="palletVisible" :docNo="currentPalletDocNo" @back-success="handlePalletBackSuccess" ></PalletDialog> </template> <script> import extend from "@/extension/check/recheckOrder.js"; import ViewGrid from '@/components/basic/ViewGrid/ViewGrid.vue'; import { ref, defineComponent } from "vue"; import PalletDialog from "@/extension/inbound/extend/PickingRetuenPallet.vue"; export default defineComponent({ components: { viewGrid: ViewGrid, PalletDialog // 注åç»çå¼¹çª }, setup() { const table = ref({ key: 'id', @@ -100,7 +115,20 @@ { field: 'modifier', title: 'ä¿®æ¹äºº', type: 'string', width: 100, align: 'left' }, { field: 'modifyDate', title: 'ä¿®æ¹æ¥æ', type: 'datetime', width: 150, align: 'left', sort: true }, ]); // 6. ç»çå¼¹çªèå¨ï¼ææåéå¿ é¡»è¿åï¼ const palletVisible = ref(false); const currentPalletDocNo = ref(""); const handleOpenPalletDialog = (docNo) => { console.log('主ç»ä»¶æ¶å°ç»çäºä»¶ï¼åæ®å·ï¼', docNo); currentPalletDocNo.value = docNo; palletVisible.value = true; }; const handlePalletBackSuccess = () => { console.log('ç»çåä¼ æåï¼å·æ°è¡¨æ ¼'); grid.value?.refresh(); // æ¤æ¶gridRefå·²æè½½ï¼å¯è°ç¨æ¹æ³ }; return { table, extend, @@ -108,7 +136,12 @@ editFormOptions, searchFormFields, searchFormOptions, columns columns, PalletDialog, // å¼¹çªç»ä»¶ï¼æ éè¿åï¼æ³¨åå³å¯ï¼ä½åééè¿åï¼ palletVisible, currentPalletDocNo, handleOpenPalletDialog, handlePalletBackSuccess }; }, }); ÏîÄ¿´úÂë/WIDESEA_WMSClient/src/views/outbound/BatchPickingConfirm.vue
@@ -550,8 +550,8 @@ this.$message.success('æ£é确认æå'); this.scanData.barcode = ''; await this.loadPalletData(); if(res.data && res.data.splitResults && res.data.splitResults.length>0){ this.$refs.childs.open(res.data.splitResults); if(res.data && res.data && res.data.length>0){ this.$refs.childs.open(res.data); } this.$nextTick(() => { this.$refs.barcodeInput.focus(); ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Common/StockEnum/StockStatusEmun.cs
@@ -104,10 +104,15 @@ [Description("å·²æ¸ ç")] å·²æ¸ ç = 33, [Description("è¿æ")] è¿æ =98, [Description("ç»çæ¤é")] ç»çæ¤é = 99, [Description("å ¥åºæ¤é")] å ¥åºæ¤é = 199, } } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_IOutboundService/IOutboundPickingService.cs
@@ -6,6 +6,7 @@ using WIDESEA_Core; using WIDESEA_Core.BaseRepository; using WIDESEA_Core.BaseServices; using WIDESEA_DTO.Inbound; using WIDESEA_DTO.Outbound; using WIDESEA_Model.Models; @@ -43,9 +44,12 @@ /// <returns></returns> WebResponseContent DeleteBarcode(NoStockOutModel noStockOut); WebResponseContent NoStockOutSubmit(NoStockOutSubmit noStockOutSubmit); Task<WebResponseContent> NoStockOutSubmit(NoStockOutSubmit noStockOutSubmit); Task<WebResponseContent> RemoveEmptyPallet(string orderNo, string palletCode); WebResponseContent UnPalletQuantity(string orderNo); WebResponseContent BarcodeMaterielGroup(BarcodeMaterielGroupDTO materielGroupDTO); } } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_Model/Models/Stock/Dt_StockInfoDetail.cs
@@ -132,6 +132,7 @@ public string BarcodeUnit { get; set; } public DateTime? ValidDate { get; set; } /// <summary> /// 夿³¨ /// </summary> ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundBatchPickingService.cs
@@ -10,9 +10,12 @@ using WIDESEA_Common.CommonEnum; using WIDESEA_Common.OrderEnum; using WIDESEA_Common.StockEnum; using WIDESEA_Common.TaskEnum; using WIDESEA_Core; using WIDESEA_Core.BaseRepository; using WIDESEA_Core.BaseServices; using WIDESEA_Core.Enums; using WIDESEA_DTO.Basic; using WIDESEA_DTO.Outbound; using WIDESEA_IAllocateService; using WIDESEA_IBasicService; @@ -217,7 +220,7 @@ .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode && x.CurrentBarcode == barcode //&& x.Status == (int)OutLockStockStatusEnum.åºåºä¸ //&& x.Status == (int)OutLockStockStatusEnum.åºåºä¸ ) .FirstAsync(); @@ -241,166 +244,6 @@ #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 && 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 忹忣 /// <summary> @@ -433,7 +276,7 @@ (lockInfo, orderDetail, stockDetail, batch) = refreshedValidation.Data; // éè¦ä¿®å¤ï¼èªå¨æå åï¼ç«å³æ§è¡åæ¡ç ç忣 var actualPickedQty = lockInfo.AssignQuantity; var pickingResult = await ExecutePickingLogic(lockInfo, orderDetail, stockDetail, actualPickedQty); @@ -445,15 +288,7 @@ _unitOfWorkManage.CommitTran(); return WebResponseContent.Instance.OK("èªå¨æå 并忣æå", new { AutoSplitted = true, NewBarcode = autoSplitResult.NewBarcode, OriginalBarcode = barcode, SplitQuantity = autoSplitResult.SplitQuantity, PickedQuantity = actualPickedQty, MaterialCode = lockInfo.MaterielCode }); return WebResponseContent.Instance.OK("èªå¨æå 并忣æå", autoSplitResult); } // æ£å¸¸åæ£æµç¨ï¼ä¸éè¦èªå¨æå ï¼ @@ -529,9 +364,159 @@ } #endregion #region æå¨æå #region æå¨æå ç¸å ³æ¹æ³ #region å走空箱é»è¾ /// <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 && 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 private List<SplitResult> CreateSplitResults(Dt_OutStockLockInfo lockInfo, decimal splitQty, decimal remainQty, string newBarcode, string originalBarcode) { return new List<SplitResult> { new SplitResult { materialCode = lockInfo.MaterielCode, supplierCode = lockInfo.SupplyCode, quantityTotal = splitQty.ToString("F2"), batchNumber = newBarcode, batch = lockInfo.BatchNo, factory = lockInfo.FactoryArea, date = DateTime.Now.ToString("yyyy-MM-dd"), }, new SplitResult { materialCode = lockInfo.MaterielCode, supplierCode = lockInfo.SupplyCode, quantityTotal = remainQty.ToString("F2"), batchNumber = originalBarcode, batch = lockInfo.BatchNo, factory = lockInfo.FactoryArea, date = DateTime.Now.ToString("yyyy-MM-dd"), } }; } #region æå¨æå /// <summary> /// æå¨æå @@ -554,12 +539,7 @@ _unitOfWorkManage.CommitTran(); return WebResponseContent.Instance.OK("æå¨æå æå", new { NewBarcode = splitResult.NewBarcode, OriginalBarcode = originalBarcode, SplitQuantity = splitQuantity }); return WebResponseContent.Instance.OK("æå¨æå æå", splitResult); } catch (Exception ex) { @@ -676,7 +656,7 @@ /// <summary> /// æ§è¡æå¨æå é»è¾ - å¢å¼ºåé æ°éæ§å¶ /// </summary> private async Task<SplitResultDto> ExecuteManualSplitLogic(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail, private async Task<List<SplitResult>> ExecuteManualSplitLogic(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail, decimal splitQuantity, string palletCode) { _logger.LogInformation($"å¼å§æ§è¡æå¨æå é»è¾ - åæ¡ç : {stockDetail.Barcode}, æå æ°é: {splitQuantity}"); @@ -700,8 +680,12 @@ // è®°å½æå åçåé æ°é decimal originalAssignQtyBefore = lockInfo.AssignQuantity; decimal originalOrderQtyBefore = lockInfo.OrderQuantity; // 1. å建æ°åºåæç» decimal remainQty = originalAssignQtyBefore - splitQuantity; if (remainQty < 0) { throw new InvalidOperationException($"æå åå©ä½æ°éä¸ºè´æ°ï¼ååé æ°é: {originalAssignQtyBefore}, æå æ°é: {splitQuantity}"); } // å建æ°åºåæç» var newStockDetail = new Dt_StockInfoDetail { StockId = stockDetail.StockId, @@ -717,18 +701,23 @@ BarcodeQty = stockDetail.BarcodeQty, BarcodeUnit = stockDetail.BarcodeUnit, BusinessType = stockDetail.BusinessType, InboundOrderRowNo = stockDetail.InboundOrderRowNo, InboundOrderRowNo = stockDetail.InboundOrderRowNo, }; await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync(); _logger.LogInformation($"å建æ°åºåæç» - æ¡ç : {newBarcode}, åºåæ°é: {splitQuantity}"); // 2. æ´æ°ååºåæç» // æ´æ°ååºåæç» decimal originalStockQtyBefore = stockDetail.StockQuantity; stockDetail.StockQuantity -= splitQuantity; if (stockDetail.StockQuantity < 0) { stockDetail.StockQuantity = 0; _logger.LogWarning($"ååºåæ°éåºç°è´æ°ï¼é置为0"); } await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); _logger.LogInformation($"æ´æ°ååºåæç» - åºåæ°éä» {originalStockQtyBefore} åå°å° {stockDetail.StockQuantity}"); // 3. å建æ°éå®ä¿¡æ¯ // å建æ°éå®ä¿¡æ¯ var newLockInfo = new Dt_OutStockLockInfo { OrderNo = lockInfo.OrderNo, @@ -755,35 +744,35 @@ lineNo = lockInfo.lineNo, WarehouseCode = lockInfo.WarehouseCode, BarcodeQty = lockInfo.BarcodeQty, BarcodeUnit = lockInfo.BarcodeUnit, BarcodeUnit = lockInfo.BarcodeUnit, }; await _outStockLockInfoService.Db.Insertable(newLockInfo).ExecuteCommandAsync(); _logger.LogInformation($"å建æ°éå®ä¿¡æ¯ - æ¡ç : {newBarcode}, åé æ°é: {splitQuantity}"); // 4. æ´æ°åéå®ä¿¡æ¯ lockInfo.AssignQuantity -= splitQuantity; lockInfo.OrderQuantity -= splitQuantity; // æ´æ°åéå®ä¿¡æ¯ lockInfo.AssignQuantity = remainQty; lockInfo.OrderQuantity = remainQty; _logger.LogInformation($"æ´æ°åéå®ä¿¡æ¯ - åé æ°éä» {originalAssignQtyBefore} åå°å° {lockInfo.AssignQuantity}"); _logger.LogInformation($"æ´æ°åéå®ä¿¡æ¯ - è®¢åæ°éä» {originalOrderQtyBefore} åå°å° {lockInfo.OrderQuantity}"); await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); // éè¦ä¿®å¤ï¼æå¨æå ä¸ä¼æ¹å订åæç»çåé æ°éï¼å 为æ»åé æ°éä¸å // åªæ¯ä»ä¸ä¸ªéå®ä¿¡æ¯è½¬ç§»å°å¦ä¸ä¸ªéå®ä¿¡æ¯ _logger.LogInformation($"订åæç»åé æ°éä¿æä¸å - å·²åé æ°é: {orderDetail.AllocatedQuantity}"); // 5. è®°å½æå åå² // è®°å½æå åå² await RecordSplitHistory(lockInfo, stockDetail, splitQuantity, newBarcode, false); // å建æå ç»æå表 var splitResults = CreateSplitResults(lockInfo, splitQuantity, remainQty, newBarcode, stockDetail.Barcode); _logger.LogInformation($"æå¨æå é»è¾æ§è¡å®æ"); return new SplitResultDto { NewBarcode = newBarcode }; return splitResults; } #endregion #endregion @@ -848,7 +837,7 @@ decimal originalAssignQtyBefore = originalLockInfo.AssignQuantity; decimal originalOrderQtyBefore = originalLockInfo.OrderQuantity; // éè¦ä¿®å¤ï¼æ¢å¤åé æ°éåè®¢åæ°é originalLockInfo.AssignQuantity += splitRecord.SplitQty; originalLockInfo.OrderQuantity += splitRecord.SplitQty; @@ -885,17 +874,20 @@ await _stockInfoDetailService.Db.Updateable(originalStock).ExecuteCommandAsync(); } // å 餿°éå®ä¿¡æ¯ _logger.LogInformation($"å 餿°éå®ä¿¡æ¯ - æ¡ç : {newLockInfo.CurrentBarcode}, åé æ°é: {newLockInfo.AssignQuantity}"); await _outStockLockInfoService.Db.Deleteable<Dt_OutStockLockInfo>() .Where(x => x.Id == newLockInfo.Id) .ExecuteCommandAsync(); if (newLockInfo != null) { // å 餿°éå®ä¿¡æ¯ _logger.LogInformation($"å 餿°éå®ä¿¡æ¯ - æ¡ç : {newLockInfo?.CurrentBarcode}, åé æ°é: {newLockInfo?.AssignQuantity}"); await _outStockLockInfoService.Db.Deleteable<Dt_OutStockLockInfo>() .Where(x => x.Id == newLockInfo.Id) .ExecuteCommandAsync(); // å 餿°åºåæç» _logger.LogInformation($"å 餿°åºåæç» - æ¡ç : {newStockDetail.Barcode}, åºåæ°é: {newStockDetail.StockQuantity}"); await _stockInfoDetailService.Db.Deleteable<Dt_StockInfoDetail>() .Where(x => x.Barcode == newLockInfo.CurrentBarcode) .ExecuteCommandAsync(); // å 餿°åºåæç» _logger.LogInformation($"å 餿°åºåæç» - æ¡ç : {newStockDetail.Barcode}, åºåæ°é: {newStockDetail.StockQuantity}"); await _stockInfoDetailService.Db.Deleteable<Dt_StockInfoDetail>() .Where(x => x.Barcode == newLockInfo.CurrentBarcode) .ExecuteCommandAsync(); } // æ è®°æå è®°å½ä¸ºå·²æ¤é splitRecord.IsReverted = true; @@ -1108,7 +1100,7 @@ return WebResponseContent.Instance.Error("è·åæ¡ç ç¶æå¤±è´¥"); } } /// <summary> /// <summary> /// è·åæä½å»ºè®® /// </summary> private List<string> GetOperationSuggestions(BarcodeStatusInfoDto statusInfo) @@ -1284,7 +1276,7 @@ NewBarcode = x.NewBarcode, SplitQuantity = x.SplitQty, Operator = x.Operator, IsReverted = x.IsReverted , IsReverted = x.IsReverted, IsAutoSplit = x.IsAutoSplit }).ToList() }; @@ -1374,61 +1366,708 @@ #endregion #region åæ¹ååº #region ç»ä¸ååºé»è¾ private async Task<Dt_Task> GetCurrentTask(string orderNo, string palletCode) { // å å°è¯éè¿è®¢åå·åæçå·æ¥æ¾ä»»å¡ var task = await _taskRepository.Db.Queryable<Dt_Task>() .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode) .FirstAsync(); if (task == null) { // 妿æ¾ä¸å°ï¼åéè¿æçå·æ¥æ¾ task = await _taskRepository.Db.Queryable<Dt_Task>() .Where(x => x.PalletCode == palletCode) .FirstAsync(); } return task; } private async Task<PalletStatusAnalysis> AnalyzePalletStatus(string orderNo, string palletCode, int stockId) { var result = new PalletStatusAnalysis { OrderNo = orderNo, PalletCode = palletCode, StockId = stockId }; // åææªåæ£çåºåºéå®è®°å½ var remainingLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() .Where(it => it.OrderNo == orderNo && it.PalletCode == palletCode && it.Status == (int)OutLockStockStatusEnum.åºåºä¸) .ToListAsync(); if (remainingLocks.Any()) { result.HasRemainingLocks = true; result.RemainingLocks = remainingLocks; result.RemainingLocksReturnQty = remainingLocks.Sum(x => x.AssignQuantity - x.PickedQty); _logger.LogInformation($"åç°{remainingLocks.Count}æ¡æªåæ£éå®è®°å½ï¼æ»æ°é: {result.RemainingLocksReturnQty}"); } // åææçä¸çåºåè´§ç© var palletStockGoods = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() .Where(it => it.StockId == stockId && (it.Status == StockStatusEmun.å ¥åºç¡®è®¤.ObjToInt() || it.Status == StockStatusEmun.å ¥åºå®æ.ObjToInt() || it.Status == StockStatusEmun.åºåºéå®.ObjToInt())) .Where(it => it.StockQuantity > 0) .ToListAsync(); if (palletStockGoods.Any()) { result.HasPalletStockGoods = true; result.PalletStockGoods = palletStockGoods; result.PalletStockReturnQty = palletStockGoods.Sum(x => x.StockQuantity); _logger.LogInformation($"åç°{palletStockGoods.Count}个åºåè´§ç©ï¼æ»æ°é: {result.PalletStockReturnQty}"); // è®°å½è¯¦ç»ç¶æåå¸ var statusGroups = palletStockGoods.GroupBy(x => x.Status); foreach (var group in statusGroups) { _logger.LogInformation($"åºåç¶æ{group.Key}: {group.Count()}个货ç©ï¼æ°é: {group.Sum(x => x.StockQuantity)}"); } } //åææå è®°å½ var splitRecords = await _splitPackageService.Db.Queryable<Dt_SplitPackageRecord>() .Where(it => it.OrderNo == orderNo && it.PalletCode == palletCode && !it.IsReverted && it.Status != (int)SplitPackageStatusEnum.å·²æ£é && it.Status != (int)SplitPackageStatusEnum.å·²ååº) .ToListAsync(); if (splitRecords.Any()) { result.HasSplitRecords = true; result.SplitRecords = splitRecords; result.SplitReturnQty = await CalculateSplitReturnQuantity(splitRecords, stockId); _logger.LogInformation($"åç°{splitRecords.Count}æ¡æªæ£éæå è®°å½ï¼æ»æ°é: {result.SplitReturnQty}"); } // 4. è®¡ç®æ»ååºæ°éå空æçç¶æ result.TotalReturnQty = result.RemainingLocksReturnQty + result.PalletStockReturnQty + result.SplitReturnQty; result.HasItemsToReturn = result.TotalReturnQty > 0; result.IsEmptyPallet = !result.HasItemsToReturn; // 5. æ£æ¥æ¯å¦æè¿è¡ä¸çä»»å¡ result.HasActiveTasks = await _taskRepository.Db.Queryable<Dt_Task>() .Where(x => x.OrderNo == orderNo && x.TaskType == TaskTypeEnum.InPick.ObjToInt() && x.PalletCode == palletCode && x.TaskStatus == (int)TaskStatusEnum.New) .AnyAsync(); _logger.LogInformation($"æçç¶æåæå®æ - 订å: {orderNo}, æç: {palletCode}, " + $"æ»ååºæ°é: {result.TotalReturnQty}, æ¯å¦ç©ºæç: {result.IsEmptyPallet}, " + $"æè¿è¡ä¸ä»»å¡: {result.HasActiveTasks}"); return result; } private async Task<decimal> CalculateSplitReturnQuantity(List<Dt_SplitPackageRecord> splitRecords, int stockId) { decimal totalQty = 0; var processedBarcodes = new HashSet<string>(); foreach (var splitRecord in splitRecords) { if (splitRecord.Status != (int)SplitPackageStatusEnum.å·²æ¤é) continue; // æ£æ¥åæ¡ç if (!processedBarcodes.Contains(splitRecord.OriginalBarcode)) { var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() .Where(it => it.Barcode == splitRecord.OriginalBarcode && it.StockId == stockId && it.Status != StockStatusEmun.åºåºå®æ.ObjToInt()) .FirstAsync(); if (originalStock != null && originalStock.StockQuantity > 0) { totalQty += originalStock.StockQuantity; processedBarcodes.Add(splitRecord.OriginalBarcode); } } // æ£æ¥æ°æ¡ç if (!processedBarcodes.Contains(splitRecord.NewBarcode)) { var newStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() .Where(it => it.Barcode == splitRecord.NewBarcode && it.StockId == stockId && it.Status != StockStatusEmun.åºåºå®æ.ObjToInt()) .FirstAsync(); if (newStock != null && newStock.StockQuantity > 0) { totalQty += newStock.StockQuantity; processedBarcodes.Add(splitRecord.NewBarcode); } } } return totalQty; } /// <summary> /// åæ¹ååº - éæ¾æªæ£éçåºå /// ç»ä¸ååºæ¹æ³ - å¤çæç䏿æå©ä½è´§ç© /// </summary> public async Task<WebResponseContent> BatchReturnStock(string orderNo, string palletCode) public async Task<WebResponseContent> ExecutePalletReturn(string orderNo, string palletCode, string returnReason = "åæ¹ååº") { try { _unitOfWorkManage.BeginTran(); // æ¥æ¾æç䏿ªå®æçéå®è®°å½ï¼åªå¤çåºåºä¸çè®°å½ï¼ var unfinishedLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() .Where(x => x.OrderNo == orderNo && x.PalletCode == palletCode && x.Status == (int)OutLockStockStatusEnum.åºåºä¸) .ToListAsync(); if (string.IsNullOrEmpty(orderNo) || string.IsNullOrEmpty(palletCode)) return WebResponseContent.Instance.Error("订åå·åæçç ä¸è½ä¸ºç©º"); if (!unfinishedLocks.Any()) return WebResponseContent.Instance.Error("该æçæ²¡ææªå®æçéå®è®°å½"); // è·ååºåä¿¡æ¯ var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>().FirstAsync(x => x.PalletCode == palletCode); if (stockInfo == null) return WebResponseContent.Instance.Error($"æªæ¾å°æç {palletCode} 对åºçåºåä¿¡æ¯"); // æåºåºæ¹æ¬¡åç»å¤ç var batchGroups = unfinishedLocks.GroupBy(x => x.OutboundBatchNo); // ä½¿ç¨ OutboundBatchNo var task = await GetCurrentTask(orderNo, palletCode); if (task == null) return WebResponseContent.Instance.Error("æªæ¾å°å¯¹åºçä»»å¡ä¿¡æ¯"); foreach (var batchGroup in batchGroups) // åææçç¶æ var statusAnalysis = await AnalyzePalletStatus(orderNo, palletCode, stockInfo.Id); if (!statusAnalysis.HasItemsToReturn) return await HandleNoReturnItems(orderNo, palletCode, task, stockInfo.Id); // æ£æ¥æ¯å¦æè¿è¡ä¸çä»»å¡ if (statusAnalysis.HasActiveTasks) { var outboundBatchNo = batchGroup.Key; var batchLocks = batchGroup.ToList(); // éæ¾åºååéå®è®°å½ foreach (var lockInfo in batchLocks) { await ReleaseLockAndStock(lockInfo); } // æ´æ°æ¹æ¬¡ç¶æ await UpdateBatchStatusForReturn(outboundBatchNo, batchLocks); // æ´æ°è®¢åæç»çå·²åé æ°é await UpdateOrderDetailAfterReturn(batchLocks); return WebResponseContent.Instance.Error($"æç {palletCode} æè¿è¡ä¸çä»»å¡ï¼ä¸è½æ§è¡ååºæä½"); } // æ§è¡ååºæä½ await ExecuteReturnDataOperations(orderNo, palletCode, stockInfo, task, statusAnalysis); // éæ¾ææéå®ä»¥ä¾¿éæ°åé await ReleaseAllLocksForReallocation(orderNo, palletCode, statusAnalysis); _unitOfWorkManage.CommitTran(); return WebResponseContent.Instance.OK("åæ¹ååºæå"); // æ¶éææéè¦ååºçæ¡ç var returnBarcodes = await CollectReturnBarcodes(statusAnalysis); // å建ESSååºä»»å¡ if (returnBarcodes.Any()) { _logger.LogInformation($"å建ååºAGVä»»å¡ - 订å: {orderNo}, æç: {palletCode}, æ¡ç æ°é: {returnBarcodes.Count}, åå : {returnReason}"); await CreateReturnTaskAndHandleESS(orderNo, palletCode, task, TaskTypeEnum.InPick, stockInfo.PalletType); } // æ´æ°è®¢åç¶æï¼ä¸è§¦åMESåä¼ ï¼ await UpdateOrderStatusForReturn(orderNo); return WebResponseContent.Instance.OK($"ååºæä½æåï¼å ±ååºæ°éï¼{statusAnalysis.TotalReturnQty}", new { ReturnQuantity = statusAnalysis.TotalReturnQty, ReturnBarcodes = returnBarcodes, Reason = returnReason, PalletCode = palletCode, OrderNo = orderNo }); } catch (Exception ex) { _unitOfWorkManage.RollbackTran(); _logger.LogError($"åæ¹ååºå¤±è´¥ - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}"); return WebResponseContent.Instance.Error($"åæ¹ååºå¤±è´¥ï¼{ex.Message}"); _logger.LogError($"ååºæä½å¤±è´¥ - OrderNo: {orderNo}, PalletCode: {palletCode}, Error: {ex.Message}"); return WebResponseContent.Instance.Error($"ååºæä½å¤±è´¥: {ex.Message}"); } } private async Task ExecuteReturnDataOperations(string orderNo, string palletCode, Dt_StockInfo stockInfo,Dt_Task task, PalletStatusAnalysis statusAnalysis) { _logger.LogInformation($"å¼å§æ§è¡ååºæ°æ®æä½ - 订å: {orderNo}, æç: {palletCode}"); // 1. å¤çæªåæ£çéå®è®°å½ if (statusAnalysis.HasRemainingLocks) { _logger.LogInformation($"å¤ç {statusAnalysis.RemainingLocks.Count} æ¡æªåæ£éå®è®°å½"); await HandleRemainingLocksReturn(statusAnalysis.RemainingLocks); } // 2. å¤çæçä¸çåºåè´§ç© if (statusAnalysis.HasPalletStockGoods) { _logger.LogInformation($"å¤ç {statusAnalysis.PalletStockGoods.Count} 个åºåè´§ç©"); await HandlePalletStockGoodsReturn(statusAnalysis.PalletStockGoods, stockInfo.Id); } // 3. å¤çæå è®°å½ if (statusAnalysis.HasSplitRecords) { _logger.LogInformation($"å¤ç {statusAnalysis.SplitRecords.Count} æ¡æå è®°å½"); await HandleSplitRecordsReturn(statusAnalysis.SplitRecords, stockInfo.Id); } _logger.LogInformation($"ååºæ°æ®æä½å®æ - æ»ååºæ°é: {statusAnalysis.TotalReturnQty}"); } /// <summary> /// åæ¹ååº - è°ç¨ç»ä¸ååºæ¹æ³ /// </summary> public async Task<WebResponseContent> BatchReturnStock(string orderNo, string palletCode) { return await ExecutePalletReturn(orderNo, palletCode, "åæ¹ååº"); } /// <summary> /// å©ä½ååº - è°ç¨ç»ä¸ååºæ¹æ³ /// </summary> public async Task<WebResponseContent> ReturnRemaining(string orderNo, string palletCode, string reason) { return await ExecutePalletReturn(orderNo, palletCode, reason); } /// <summary> /// å走空箱 - å æ§è¡ååºåæ¸ ç - å¢å¼ºçæ¬ /// </summary> public async Task<WebResponseContent> RemoveEmptyPallet(string orderNo, string palletCode) { try { _unitOfWorkManage.BeginTran(); _logger.LogInformation($"å¼å§å走空箱 - 订å: {orderNo}, æç: {palletCode}"); // 1. éªè¯ç©ºç®±åèµ°æ¡ä»¶ï¼å¿ é¡»å ¨é¨å®ææ£éï¼ var validationResult = await ValidateEmptyPalletRemoval(orderNo, palletCode); if (!validationResult.IsValid) { _unitOfWorkManage.RollbackTran(); return WebResponseContent.Instance.Error(validationResult.ErrorMessage); } var completedLocks = validationResult.Data; // 2. æ¸ ç已宿çéå®è®°å½ï¼æ 记为已åèµ°ï¼ await CleanupCompletedLocks(completedLocks); // 3. æ¸ ç对åºçåºåè®°å½ç¶æ foreach (var lockInfo in completedLocks) { await CleanupStockInfo(lockInfo); } // 4. æ´æ°ç¸å ³è®¢åç¶æ await UpdateOrderStatusAfterPalletRemoval(orderNo); // 5. è®°å½æä½åå² await RecordEmptyPalletRemoval(orderNo, palletCode, completedLocks); _unitOfWorkManage.CommitTran(); _logger.LogInformation($"å走空箱æå - 订å: {orderNo}, æç: {palletCode}"); 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<List<string>> CollectReturnBarcodes(PalletStatusAnalysis status) { var returnBarcodes = new HashSet<string>(); try { _logger.LogInformation($"å¼å§æ¶éååºæ¡ç - 订å: {status.OrderNo}, æç: {status.PalletCode}"); // 1. æ¶éæªåæ£éå®è®°å½çæ¡ç if (status.HasRemainingLocks) { foreach (var lockInfo in status.RemainingLocks) { if (!string.IsNullOrEmpty(lockInfo.CurrentBarcode)) { returnBarcodes.Add(lockInfo.CurrentBarcode); _logger.LogInformation($"æ·»å éå®è®°å½æ¡ç : {lockInfo.CurrentBarcode}"); } } } // 2. æ¶éæçä¸åºåè´§ç©çæ¡ç if (status.HasPalletStockGoods) { foreach (var stockDetail in status.PalletStockGoods) { if (!string.IsNullOrEmpty(stockDetail.Barcode) && stockDetail.StockQuantity > 0) { returnBarcodes.Add(stockDetail.Barcode); _logger.LogInformation($"æ·»å åºåè´§ç©æ¡ç : {stockDetail.Barcode}, æ°é: {stockDetail.StockQuantity}"); } } } // 3. æ¶éæå è®°å½ç¸å ³çæ¡ç if (status.HasSplitRecords) { foreach (var splitRecord in status.SplitRecords) { // æ·»å åæ¡ç if (!string.IsNullOrEmpty(splitRecord.OriginalBarcode)) { var originalStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() .FirstAsync(x => x.Barcode == splitRecord.OriginalBarcode && x.StockId == status.StockId); if (originalStock != null && originalStock.StockQuantity > 0) { returnBarcodes.Add(splitRecord.OriginalBarcode); _logger.LogInformation($"æ·»å æå åæ¡ç : {splitRecord.OriginalBarcode}, æ°é: {originalStock.StockQuantity}"); } } // æ·»å æ°æ¡ç if (!string.IsNullOrEmpty(splitRecord.NewBarcode)) { var newStock = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() .FirstAsync(x => x.Barcode == splitRecord.NewBarcode && x.StockId == status.StockId); if (newStock != null && newStock.StockQuantity > 0) { returnBarcodes.Add(splitRecord.NewBarcode); _logger.LogInformation($"æ·»å æå æ°æ¡ç : {splitRecord.NewBarcode}, æ°é: {newStock.StockQuantity}"); } } } } _logger.LogInformation($"ååºæ¡ç æ¶é宿 - å ± {returnBarcodes.Count} 个æ¡ç : {string.Join(", ", returnBarcodes)}"); return returnBarcodes.ToList(); } catch (Exception ex) { _logger.LogError($"æ¶éååºæ¡ç 失败 - Error: {ex.Message}"); return returnBarcodes.ToList(); } } #endregion #region ååºæä½æ ¸å¿æ¹æ³ /// <summary> /// æ§è¡ååºæä½ - å¢å¼ºçæ¬ /// </summary> private async Task ExecuteReturnOperations(string orderNo, string palletCode, Dt_StockInfo stockInfo, Dt_Task task, PalletStatusAnalysis statusAnalysis) { _logger.LogInformation($"å¼å§æ§è¡ååºæä½ - 订å: {orderNo}, æç: {palletCode}"); // å¤çæªåæ£çéå®è®°å½ if (statusAnalysis.HasRemainingLocks) { _logger.LogInformation($"å¤ç {statusAnalysis.RemainingLocks.Count} æ¡æªåæ£éå®è®°å½"); await HandleRemainingLocksReturn(statusAnalysis.RemainingLocks); } //å¤çæçä¸çåºåè´§ç© if (statusAnalysis.HasPalletStockGoods) { _logger.LogInformation($"å¤ç {statusAnalysis.PalletStockGoods.Count} 个åºåè´§ç©"); await HandlePalletStockGoodsReturn(statusAnalysis.PalletStockGoods, stockInfo.Id); } //å¤çæå è®°å½ if (statusAnalysis.HasSplitRecords) { _logger.LogInformation($"å¤ç {statusAnalysis.SplitRecords.Count} æ¡æå è®°å½"); await HandleSplitRecordsReturn(statusAnalysis.SplitRecords, stockInfo.Id); } _logger.LogInformation($"ååºæä½å®æ - æ»ååºæ°é: {statusAnalysis.TotalReturnQty}"); } /// <summary> /// å¤çæªåæ£çéå®è®°å½ååº /// </summary> private async Task HandleRemainingLocksReturn(List<Dt_OutStockLockInfo> remainingLocks) { foreach (var lockInfo in remainingLocks) { // 计ç®ååºæ°éï¼æªæ£éçé¨åï¼ decimal returnQty = lockInfo.AssignQuantity - lockInfo.PickedQty; if (returnQty > 0) { // æ¢å¤åºåç¶æ var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() .FirstAsync(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId); if (stockDetail != null) { stockDetail.StockQuantity += returnQty; stockDetail.OutboundQuantity -= returnQty; // ç¡®ä¿åºåºæ°éä¸ä¼ä¸ºè´æ° if (stockDetail.OutboundQuantity < 0) { stockDetail.OutboundQuantity = 0; } // ååºååºåç¶ææ¢å¤ä¸ºå ¥åºå®æï¼å¯ç¨ç¶æï¼ stockDetail.Status = (int)StockStatusEmun.å ¥åºå®æ; await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); _logger.LogInformation($"æ¢å¤åºå - æ¡ç : {stockDetail.Barcode}, æ°é: {returnQty}, æ°åºå: {stockDetail.StockQuantity}"); } // æ´æ°éå®è®°å½ç¶æä¸ºå·²ååº lockInfo.Status = (int)OutLockStockStatusEnum.å·²ååº; lockInfo.Operator = App.User.UserName; await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); _logger.LogInformation($"æ´æ°éå®ç¶æ - éå®ID: {lockInfo.Id}, ååºæ°é: {returnQty}"); } } } /// <summary> /// å¤çæçä¸çåºåè´§ç©ååº /// </summary> private async Task HandlePalletStockGoodsReturn(List<Dt_StockInfoDetail> palletStockGoods, int stockId) { foreach (var stockDetail in palletStockGoods) { // åªå¤çåºåºéå®ç¶æçåºå if (stockDetail.Status == (int)StockStatusEmun.åºåºéå® && stockDetail.StockQuantity > 0) { // æ¢å¤åºåç¶æä¸ºå¯ç¨ç¶æ stockDetail.Status = (int)StockStatusEmun.å ¥åºå®æ; await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); _logger.LogInformation($"æ¢å¤åºåè´§ç© - æ¡ç : {stockDetail.Barcode}, æ°é: {stockDetail.StockQuantity}"); } } } /// <summary> /// å¤çæå è®°å½ååº /// </summary> private async Task HandleSplitRecordsReturn(List<Dt_SplitPackageRecord> splitRecords, int stockId) { foreach (var splitRecord in splitRecords) { // å¤çæ°æ¡ç var newLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() .Where(x => x.CurrentBarcode == splitRecord.NewBarcode && x.Status == (int)OutLockStockStatusEnum.åºåºä¸) .FirstAsync(); if (newLockInfo != null) { await HandleSingleLockReturn(newLockInfo); } // å¤çåæ¡ç var originalLockInfo = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() .Where(x => x.CurrentBarcode == splitRecord.OriginalBarcode && x.Status == (int)OutLockStockStatusEnum.åºåºä¸) .FirstAsync(); if (originalLockInfo != null) { await HandleSingleLockReturn(originalLockInfo); } // æ´æ°æå è®°å½ç¶æä¸ºå·²ååº splitRecord.Status = (int)SplitPackageStatusEnum.å·²ååº; await _splitPackageService.Db.Updateable(splitRecord).ExecuteCommandAsync(); } } /// <summary> /// å¤çå个éå®è®°å½ååº /// </summary> private async Task HandleSingleLockReturn(Dt_OutStockLockInfo lockInfo) { decimal returnQty = lockInfo.AssignQuantity - lockInfo.PickedQty; if (returnQty > 0) { // æ¢å¤åºå var stockDetail = await _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>() .FirstAsync(x => x.Barcode == lockInfo.CurrentBarcode && x.StockId == lockInfo.StockId); if (stockDetail != null) { stockDetail.StockQuantity += returnQty; stockDetail.OutboundQuantity -= returnQty; if (stockDetail.OutboundQuantity < 0) { stockDetail.OutboundQuantity = 0; } stockDetail.Status = (int)StockStatusEmun.å ¥åºå®æ; await _stockInfoDetailService.Db.Updateable(stockDetail).ExecuteCommandAsync(); } // æ´æ°éå®ç¶æ lockInfo.Status = (int)OutLockStockStatusEnum.å·²ååº; lockInfo.Operator = App.User.UserName; await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); _logger.LogInformation($"å¤çæå ç¸å ³éå® - æ¡ç : {lockInfo.CurrentBarcode}, ååºæ°é: {returnQty}"); } } /// <summary> /// éæ¾ææéå®ä»¥ä¾¿éæ°åé /// </summary> private async Task ReleaseAllLocksForReallocation(string orderNo, string palletCode, PalletStatusAnalysis statusAnalysis) { // æ´æ°è®¢åæç»çå·²åé æ°é if (statusAnalysis.HasRemainingLocks) { var orderDetailGroups = statusAnalysis.RemainingLocks.GroupBy(x => x.OrderDetailId); foreach (var group in orderDetailGroups) { var orderDetailId = group.Key; var returnedQty = group.Sum(x => x.AssignQuantity - x.PickedQty); var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() .FirstAsync(x => x.Id == orderDetailId); if (orderDetail != null) { orderDetail.AllocatedQuantity -= returnedQty; orderDetail.LockQuantity = orderDetail.AllocatedQuantity; await UpdateBatchAllocateStatus(orderDetail); await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync(); _logger.LogInformation($"æ´æ°è®¢åæç» - OrderDetailId: {orderDetailId}, åå°åé æ°é: {returnedQty}"); } } } } #endregion #region è¾ å©æ¹æ³ /// <summary> /// å¤ç没æååºç©åçæ åµ /// </summary> private async Task<WebResponseContent> HandleNoReturnItems(string orderNo, string palletCode, Dt_Task originalTask, int stockId) { _logger.LogInformation($"æç {palletCode} 没æéè¦ååºçç©å"); // æ£æ¥æ¯å¦æ¯ç©ºæç var statusAnalysis = await AnalyzePalletStatus(orderNo, palletCode, stockId); if (statusAnalysis.IsEmptyPallet) { try { var locationtype = 0; var stockInfo = await _stockInfoService.Db.Queryable<Dt_StockInfo>() .Where(x => x.PalletCode == palletCode) .FirstAsync(); if (stockInfo == null) { var firstLocation = await _locationInfoService.Db.Queryable<Dt_LocationInfo>().FirstAsync(x => x.LocationCode == originalTask.SourceAddress); locationtype = firstLocation?.LocationType ?? 1; } else { locationtype = stockInfo.LocationType; _stockInfoService.DeleteData(stockInfo); } var targetAddress = originalTask.TargetAddress; await CleanupZeroStockData(stockId); var emptystockInfo = new Dt_StockInfo() { PalletType = PalletTypeEnum.Empty.ObjToInt(), StockStatus = StockStatusEmun.ç»çæå.ObjToInt(), PalletCode = palletCode, LocationType = locationtype }; emptystockInfo.Details = new List<Dt_StockInfoDetail>(); _stockInfoService.AddMaterielGroup(emptystockInfo); //空æçå¦ä½å¤ç è¿æä¸ä¸ªåºåºä»»å¡è¦å¤çã originalTask.PalletType = PalletTypeEnum.Empty.ObjToInt(); await CreateReturnTaskAndHandleESS(orderNo, palletCode, originalTask, TaskTypeEnum.InEmpty, PalletTypeEnum.Empty.ObjToInt()); } catch (Exception ex) { _logger.LogError($" HandleNoReturnItems 失败: {ex.Message}"); return WebResponseContent.Instance.Error($" ååºç©ºæç失败ï¼"); } return WebResponseContent.Instance.OK("空æçååºä»»å¡å建æå"); } else { return WebResponseContent.Instance.Error("æçç¶æå¼å¸¸ï¼æç©å使 æ³è®¡ç®ååºæ°é"); } } private async Task CleanupZeroStockData(int stockId) { try { // 1. å é¤åºåæ°é为0çæç»è®°å½ var deleteDetailCount = await _stockInfoDetailService.Db.Deleteable<Dt_StockInfoDetail>() .Where(x => x.StockId == stockId && x.StockQuantity == 0 && (x.Status == StockStatusEmun.åºåºå®æ.ObjToInt() || x.Status == StockStatusEmun.å ¥åºå®æ.ObjToInt())) .ExecuteCommandAsync(); await _stockInfoService.Db.Deleteable<Dt_StockInfo>() .Where(x => x.Id == stockId).ExecuteCommandAsync(); _logger.LogInformation($"æ¸ çé¶åºåæç»è®°å½ - StockId: {stockId}, å é¤è®°å½æ°: {deleteDetailCount}"); } catch (Exception ex) { _logger.LogWarning($"æ¸ çé¶åºåæ°æ®å¤±è´¥ - StockId: {stockId}, Error: {ex.Message}"); // 注æï¼æ¸ ç失败ä¸åºè¯¥å½±å主æµç¨ } } /// <summary> /// æ´æ°è®¢åç¶æï¼ååºåï¼ /// </summary> private async Task UpdateOrderStatusForReturn(string orderNo) { // æ£æ¥è®¢åæ¯å¦æææçé½å·²å®ææå·²ååº var allLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() .Where(x => x.OrderNo == orderNo) .ToListAsync(); var activeLocks = allLocks.Where(x => x.Status == (int)OutLockStockStatusEnum.åºåºä¸ || x.Status == (int)OutLockStockStatusEnum.ååºä¸).ToList(); // å¦ææ²¡ææ´»è·çéå®è®°å½ï¼æ´æ°è®¢åç¶æ if (!activeLocks.Any()) { await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>() .SetColumns(x => new Dt_OutboundOrder { OrderStatus = (int)OutOrderStatusEnum.åºåºå®æ, }) .Where(x => x.OrderNo == orderNo) .ExecuteCommandAsync(); _logger.LogInformation($"æ´æ°è®¢åç¶æä¸ºåºåºå®æ - 订å: {orderNo}"); } } #endregion #region éªè¯æ¹æ³ private async Task<ValidationResult<(Dt_OutStockLockInfo, Dt_OutboundOrderDetail, Dt_StockInfoDetail, Dt_OutboundBatch)>> ValidatePickingRequest( @@ -1510,7 +2149,7 @@ /// <summary> /// æ£æ¥å¹¶æ§è¡èªå¨æå ï¼å¦æéè¦ï¼ /// </summary> private async Task<AutoSplitResult> CheckAndAutoSplitIfNeeded(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail, string palletCode) private async Task<List<SplitResult>> CheckAndAutoSplitIfNeeded(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail, string palletCode) { // æ£æ¥æ¯å¦éè¦èªå¨æå çæ¡ä»¶ï¼ // 1. åºåæ°é大äºåé æ°é @@ -1529,16 +2168,12 @@ // æ§è¡èªå¨æå var splitResult = await ExecuteAutoSplitLogic(lockInfo, stockDetail, splitQuantity, palletCode); return new AutoSplitResult { NewBarcode = splitResult.NewBarcode, SplitQuantity = splitQuantity }; return splitResult; } /// <summary> /// æ§è¡èªå¨æå é»è¾ /// </summary> private async Task<SplitResultDto> ExecuteAutoSplitLogic(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail, private async Task<List<SplitResult>> ExecuteAutoSplitLogic(Dt_OutStockLockInfo lockInfo, Dt_StockInfoDetail stockDetail, decimal splitQuantity, string palletCode) { _logger.LogInformation($"å¼å§æ§è¡èªå¨æå é»è¾ - åæ¡ç : {stockDetail.Barcode}, æå æ°é: {splitQuantity}"); @@ -1549,8 +2184,9 @@ // éè¦ä¿®å¤ï¼è®°å½æå åçåé æ°é decimal originalAssignQtyBefore = lockInfo.AssignQuantity; decimal originalOrderQtyBefore = lockInfo.OrderQuantity; decimal remainQty = originalAssignQtyBefore; // èªå¨æå ååæ¡ç åé æ°éä¿æä¸å // 1. å建æ°åºåæç»ï¼å©ä½é¨åï¼ // å建æ°åºåæç»ï¼å©ä½é¨åï¼ var newStockDetail = new Dt_StockInfoDetail { StockId = stockDetail.StockId, @@ -1566,20 +2202,18 @@ BarcodeQty = stockDetail.BarcodeQty, BarcodeUnit = stockDetail.BarcodeUnit, BusinessType = stockDetail.BusinessType, InboundOrderRowNo = stockDetail.InboundOrderRowNo, InboundOrderRowNo = stockDetail.InboundOrderRowNo, }; await _stockInfoDetailService.Db.Insertable(newStockDetail).ExecuteCommandAsync(); _logger.LogInformation($"å建æ°åºåæç» - æ¡ç : {newBarcode}, åºåæ°é: {splitQuantity}"); // 2. æ´æ°ååºåæç» // éè¦ä¿®å¤ï¼ååºåæ°é设置为åé æ°éï¼ä¿æä¸åï¼ decimal originalStockQtyBefore = stockDetail.StockQuantity; // 注æï¼èªå¨æå æ¶ï¼ååºåæ°éä¿æä¸åï¼å 为æä»¬è¦åæ£çå°±æ¯åé æ°é // æ´æ°ååºåæç» // éè¦ä¿®å¤ï¼èªå¨æå æ¶ï¼ååºåæ°éä¿æä¸åï¼å 为æä»¬è¦åæ£çå°±æ¯åé æ°é // stockDetail.StockQuantity ä¿æä¸å _logger.LogInformation($"ååºåæç»ä¿æä¸å - åºåæ°é: {stockDetail.StockQuantity}"); // 3. å建æ°éå®ä¿¡æ¯ï¼å©ä½é¨åï¼ // å建æ°éå®ä¿¡æ¯ï¼å©ä½é¨åï¼ var newLockInfo = new Dt_OutStockLockInfo { OrderNo = lockInfo.OrderNo, @@ -1606,25 +2240,28 @@ lineNo = lockInfo.lineNo, WarehouseCode = lockInfo.WarehouseCode, BarcodeQty = lockInfo.BarcodeQty, BarcodeUnit = lockInfo.BarcodeUnit, BarcodeUnit = lockInfo.BarcodeUnit, }; await _outStockLockInfoService.Db.Insertable(newLockInfo).ExecuteCommandAsync(); _logger.LogInformation($"å建æ°éå®ä¿¡æ¯ - æ¡ç : {newBarcode}, åé æ°é: {splitQuantity}"); // 4. éè¦ä¿®å¤ï¼èªå¨æå æ¶ï¼åéå®ä¿¡æ¯çåé æ°éä¿æä¸å // èªå¨æå æ¶ï¼åéå®ä¿¡æ¯çåé æ°éä¿æä¸å // å 为èªå¨æå åï¼åæ¡ç ä»ç¶éè¦è¢«åæ£ï¼åé æ°éä¸åºè¯¥æ¹å _logger.LogInformation($"åéå®ä¿¡æ¯ä¿æä¸å - åé æ°é: {lockInfo.AssignQuantity}, è®¢åæ°é: {lockInfo.OrderQuantity}"); // 5. è®°å½æå åå² await RecordSplitHistory(lockInfo, stockDetail, splitQuantity, newBarcode, true, originalStockQtyBefore); // è®°å½æå åå² await RecordSplitHistory(lockInfo, stockDetail, splitQuantity, newBarcode, true, stockDetail.StockQuantity); // å建æå ç»æå表 var splitResults = CreateSplitResults(lockInfo, splitQuantity, remainQty, newBarcode, stockDetail.Barcode); _logger.LogInformation($"èªå¨æå é»è¾æ§è¡å®æ"); return new SplitResultDto { NewBarcode = newBarcode }; return splitResults; } #endregion #region æ ¸å¿é»è¾æ¹æ³ @@ -1635,7 +2272,7 @@ { _logger.LogInformation($"å¼å§æ§è¡åæ£é»è¾ - æ¡ç : {stockDetail.Barcode}, åé æ°é: {lockInfo.AssignQuantity}, å®é æ£é: {actualPickedQty}"); // éè¦ä¿®å¤ï¼å次éªè¯è®¢åæç»çåé æ°éï¼é²æ¢å¹¶åæä½ï¼ // 忬¡éªè¯è®¢åæç»çåé æ°éï¼é²æ¢å¹¶åæä½ï¼ if (orderDetail.AllocatedQuantity < actualPickedQty) { throw new InvalidOperationException($"订åæç»åé æ°éä¸è¶³ï¼éè¦æ£é {actualPickedQty}ï¼å¯ç¨åé æ°é {orderDetail.AllocatedQuantity}"); @@ -1646,7 +2283,7 @@ throw new InvalidOperationException($"订åæç»é宿°éä¸è¶³ï¼éè¦æ£é {actualPickedQty}ï¼å¯ç¨é宿°é {orderDetail.LockQuantity}"); } // 1. æ´æ°éå®ä¿¡æ¯ // æ´æ°éå®ä¿¡æ¯ lockInfo.PickedQty += actualPickedQty; _logger.LogInformation($"æ´æ°éå®ä¿¡æ¯ - å·²æ£éæ°éä» {lockInfo.PickedQty - actualPickedQty} å¢å å° {lockInfo.PickedQty}"); @@ -1665,7 +2302,7 @@ lockInfo.Operator = App.User.UserName; await _outStockLockInfoService.Db.Updateable(lockInfo).ExecuteCommandAsync(); // 2. æ´æ°åºåä¿¡æ¯ // æ´æ°åºåä¿¡æ¯ decimal originalStockQty = stockDetail.StockQuantity; decimal originalOutboundQty = stockDetail.OutboundQuantity; @@ -1783,7 +2420,7 @@ StockDetail = stockDetail }; } #endregion #region æ°æ®æ´æ°æ¹æ³ @@ -1791,16 +2428,25 @@ private async Task UpdateBatchAndOrderData(Dt_OutboundBatch batch, Dt_OutboundOrderDetail orderDetail, decimal pickedQty, string orderNo) { _logger.LogInformation($"å¼å§æ´æ°æ¹æ¬¡åè®¢åæ°æ® - æ£éæ°é: {pickedQty}"); var latestOrderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>().FirstAsync(x => x.Id == orderDetail.Id); if (latestOrderDetail == null) throw new InvalidOperationException("æªæ¾å°è®¢åæç»"); orderDetail = latestOrderDetail; // éè¦ä¿®å¤ï¼éªè¯åé æ°éä¸ä¼åæè´æ° if (orderDetail.AllocatedQuantity < pickedQty) { throw new InvalidOperationException($"æ´æ°è®¢åæ°æ®æ¶åé æ°éä¸è¶³ï¼éè¦åå° {pickedQty}ï¼å½ååé æ°é {orderDetail.AllocatedQuantity}"); decimal actualPickedQty = orderDetail.AllocatedQuantity; _logger.LogWarning($"åé æ°éä¸è¶³ï¼è°æ´æ£éæ°é - åéè¦: {pickedQty}, å®é å¯ç¨: {actualPickedQty}"); pickedQty = actualPickedQty; } if (orderDetail.LockQuantity < pickedQty) { throw new InvalidOperationException($"æ´æ°è®¢åæ°æ®æ¶é宿°éä¸è¶³ï¼éè¦åå° {pickedQty}ï¼å½åé宿°é {orderDetail.LockQuantity}"); decimal actualPickedQty = orderDetail.LockQuantity; _logger.LogWarning($"é宿°éä¸è¶³ï¼è°æ´æ£éæ°é - åéè¦: {pickedQty}, å®é å¯ç¨: {actualPickedQty}"); pickedQty = actualPickedQty; } // 1. æ´æ°æ¹æ¬¡å®ææ°é @@ -1816,7 +2462,7 @@ } await _outboundBatchRepository.Db.Updateable(batch).ExecuteCommandAsync(); // 2. æ´æ°è®¢åæç» // æ´æ°è®¢åæç» decimal originalOverOutQty = orderDetail.OverOutQuantity; decimal originalAllocatedQty = orderDetail.AllocatedQuantity; decimal originalLockQty = orderDetail.LockQuantity; @@ -1825,14 +2471,15 @@ orderDetail.AllocatedQuantity -= pickedQty; // LockQuantity 忥åå° orderDetail.LockQuantity = orderDetail.AllocatedQuantity; if (orderDetail.AllocatedQuantity < 0) orderDetail.AllocatedQuantity = 0; if (orderDetail.LockQuantity < 0) orderDetail.LockQuantity = 0; _logger.LogInformation($"æ´æ°è®¢åæç» - å·²åºåºæ°éä» {originalOverOutQty} å¢å å° {orderDetail.OverOutQuantity}"); _logger.LogInformation($"æ´æ°è®¢åæç» - å·²åé æ°éä» {originalAllocatedQty} åå°å° {orderDetail.AllocatedQuantity}"); _logger.LogInformation($"æ´æ°è®¢åæç» - é宿°éä» {originalLockQty} åå°å° {orderDetail.LockQuantity}"); await _outboundOrderDetailService.Db.Updateable(orderDetail).ExecuteCommandAsync(); // 3. æ£æ¥è®¢åç¶æ // æ£æ¥è®¢åç¶æ await CheckAndUpdateOrderStatus(orderNo); _logger.LogInformation($"æ¹æ¬¡åè®¢åæ°æ®æ´æ°å®æ"); @@ -1842,7 +2489,7 @@ { _logger.LogInformation($"å¼å§æ¢å¤æ¹æ¬¡åè®¢åæ°æ®"); // 1. æ¢å¤æ¹æ¬¡å®ææ°é // æ¢å¤æ¹æ¬¡å®ææ°é var batch = await _outboundBatchRepository.Db.Queryable<Dt_OutboundBatch>() .FirstAsync(x => x.BatchNo == revertResult.LockInfo.OutboundBatchNo); @@ -1850,7 +2497,11 @@ { decimal originalCompletedQty = batch.CompletedQuantity; batch.CompletedQuantity -= pickingRecord.PickQuantity; if (batch.CompletedQuantity < 0) { batch.CompletedQuantity = 0; _logger.LogWarning($"æ¹æ¬¡å®ææ°éåºç°è´æ°ï¼é置为0"); } _logger.LogInformation($"æ¢å¤æ¹æ¬¡å®ææ°é - ä» {originalCompletedQty} åå°å° {batch.CompletedQuantity}"); // éæ°è®¡ç®æ¹æ¬¡ç¶æ @@ -1868,7 +2519,7 @@ await _outboundBatchRepository.Db.Updateable(batch).ExecuteCommandAsync(); } // 2. æ¢å¤è®¢åæç» // æ¢å¤è®¢åæç» var orderDetail = await _outboundOrderDetailService.Db.Queryable<Dt_OutboundOrderDetail>() .FirstAsync(x => x.Id == pickingRecord.OrderDetailId); @@ -1880,7 +2531,11 @@ // éè¦ä¿®å¤ï¼åªæ¢å¤ç¸å ³æ°éï¼åé æ°éä¿æä¸å orderDetail.OverOutQuantity -= pickingRecord.PickQuantity; // 注æï¼AllocatedQuantity å LockQuantity å¨åæ¶åæ£æ¶ä¸åºè¯¥æ¹å orderDetail.AllocatedQuantity += pickingRecord.PickQuantity; orderDetail.LockQuantity += pickingRecord.PickQuantity; if (orderDetail.OverOutQuantity < 0) orderDetail.OverOutQuantity = 0; if (orderDetail.AllocatedQuantity < 0) orderDetail.AllocatedQuantity = 0; if (orderDetail.LockQuantity < 0) orderDetail.LockQuantity = 0; _logger.LogInformation($"æ¢å¤è®¢åæç» - å·²åºåºæ°éä» {originalOverOutQty} åå°å° {orderDetail.OverOutQuantity}"); _logger.LogInformation($"订åæç»åé æ°éä¿æä¸å: {originalAllocatedQty}"); @@ -1992,6 +2647,99 @@ } #endregion private async Task CreateReturnTaskAndHandleESS(string orderNo, string palletCode, Dt_Task originalTask, TaskTypeEnum taskTypeEnum, int palletType) { var firstLocation = await _locationInfoService.Db.Queryable<Dt_LocationInfo>() .FirstAsync(x => x.LocationCode == originalTask.SourceAddress); // åé æ°è´§ä½ var newLocation = _locationInfoService.AssignLocation(firstLocation.LocationType); Dt_Task returnTask = new() { CurrentAddress = stations[originalTask.TargetAddress], Grade = 0, PalletCode = palletCode, NextAddress = "", // OrderNo = originalTask.OrderNo, OrderNo = orderNo, Roadway = newLocation.RoadwayNo, SourceAddress = stations[originalTask.TargetAddress], TargetAddress = newLocation.LocationCode, TaskStatus = TaskStatusEnum.New.ObjToInt(), TaskType = taskTypeEnum.ObjToInt(), PalletType = palletType, WarehouseId = originalTask.WarehouseId }; // ä¿åååºä»»å¡ await _taskRepository.Db.Insertable(returnTask).ExecuteCommandAsync(); var targetAddress = originalTask.TargetAddress; // å é¤åå§åºåºä»»å¡ _taskRepository.DeleteAndMoveIntoHty(originalTask, OperateTypeEnum.èªå¨å®æ); // await _taskRepository.Db.Deleteable(originalTask).ExecuteCommandAsync(); // ç» ESS åéæµå¨ä¿¡å·ååå»ºä»»å¡ await SendESSCommands(palletCode, targetAddress, returnTask); } /// <summary> /// ç»ESSä¸ä»»å¡ /// </summary> /// <param name="palletCode"></param> /// <param name="targetAddress"></param> /// <param name="returnTask"></param> /// <returns></returns> /// <exception cref="Exception"></exception> private async Task SendESSCommands(string palletCode, string targetAddress, Dt_Task returnTask) { try { // 1. åéæµå¨ä¿¡å· var moveResult = await _eSSApiService.MoveContainerAsync(new WIDESEA_DTO.Basic.MoveContainerRequest { slotCode = movestations[targetAddress], containerCode = palletCode }); //if (moveResult) //{ // 2. å建ååºä»»å¡ var essTask = new TaskModel() { taskType = "putaway", taskGroupCode = "", groupPriority = 0, tasks = new List<TasksType>{ new() { taskCode = returnTask.TaskNum.ToString(), taskPriority = 0, taskDescribe = new TaskDescribeType { containerCode = palletCode, containerType = "CT_KUBOT_STANDARD", fromLocationCode = stations.GetValueOrDefault(targetAddress) ?? "", toStationCode = "", toLocationCode = returnTask.TargetAddress, deadline = 0, storageTag = "" } } } }; var resultTask = await _eSSApiService.CreateTaskAsync(essTask); _logger.LogInformation($"ReturnRemaining åå»ºä»»å¡æå: {resultTask}"); //} } catch (Exception ex) { _logger.LogError($"ReturnRemaining ESSå½ä»¤åé失败: {ex.Message}"); throw new Exception($"ESSç³»ç»é信失败: {ex.Message}"); } } #region è¾ å©æ¹æ³ private async Task<string> GenerateNewBarcode() @@ -2013,7 +2761,7 @@ SplitQty = splitQty, SplitTime = DateTime.Now, Status = (int)SplitPackageStatusEnum.å·²æå , IsAutoSplit = isAutoSplit , IsAutoSplit = isAutoSplit, // SplitType = isAutoSplit ? "èªå¨æå " : "æå¨æå " OriginalStockQuantity = originalStockQuantity ?? stockDetail.StockQuantity, //RemainingStockQuantity = stockDetail.StockQuantity - splitQty @@ -2145,12 +2893,5 @@ } #endregion } // æ¯æç±» public class SplitResultDto { public string NewBarcode { get; set; } } } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundOrderDetailService.cs
@@ -140,7 +140,7 @@ private bool CanReassignOrder(Dt_OutboundOrder outboundOrder) { // å è®¸éæ°åé çç¶æ var allowedStatus = new[] {OutOrderStatusEnum.æªå¼å§, OutOrderStatusEnum.åºåºä¸,OutOrderStatusEnum.é¨å宿}; var allowedStatus = new[] { OutOrderStatusEnum.æªå¼å§, OutOrderStatusEnum.åºåºä¸, OutOrderStatusEnum.é¨å宿 }; return allowedStatus.Contains((OutOrderStatusEnum)outboundOrder.OrderStatus); } @@ -682,12 +682,18 @@ decimal remainingAssign = assignQuantity; // æå è¿å åºåé åºåæç» var sortedDetails = stock.Details var sorteds = stock.Details .Where(d => d.MaterielCode == detail.MaterielCode && d.BatchNo == detail.BatchNo && (d.StockQuantity - d.OutboundQuantity) > 0) .OrderBy(d => d.CreateDate) .ToList(); (d.StockQuantity - d.OutboundQuantity) > 0); // .OrderBy(d => d.CreateDate); if (!string.IsNullOrEmpty(detail.BatchNo)) { sorteds= stock.Details.Where(x => x.BatchNo == detail.BatchNo); } var sortedDetails= sorteds.ToList().OrderBy(d => d.CreateDate); foreach (var stockDetail in sortedDetails) { ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_OutboundService/OutboundPickingService.cs
@@ -20,6 +20,7 @@ using WIDESEA_Core.BaseServices; using WIDESEA_Core.Enums; using WIDESEA_Core.Helper; using WIDESEA_Core.Utilities; using WIDESEA_DTO.Allocate; using WIDESEA_DTO.Basic; using WIDESEA_DTO.Inbound; @@ -30,6 +31,7 @@ using WIDESEA_IOutboundService; using WIDESEA_IStockService; using WIDESEA_Model.Models; using WIDESEA_Model.Models.Basic; namespace WIDESEA_OutboundService { @@ -56,7 +58,7 @@ private readonly IAllocateService _allocateService; private readonly IRepository<Dt_InboundOrder> _inboundOrderRepository; private readonly IInboundOrderDetailService _inboundOrderDetailService; private readonly IRepository<Dt_WarehouseArea> _warehouseAreaRepository; private readonly ILogger<OutboundPickingService> _logger; @@ -77,7 +79,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, IRepository<Dt_InboundOrder> inboundOrderRepository,IInboundOrderDetailService inboundOrderDetailService) : base(BaseDal) IRepository<Dt_Task> taskRepository, IESSApiService eSSApiService, ILogger<OutboundPickingService> logger, IInvokeMESService invokeMESService, IDailySequenceService dailySequenceService, IAllocateService allocateService, IRepository<Dt_InboundOrder> inboundOrderRepository, IInboundOrderDetailService inboundOrderDetailService, IRepository<Dt_WarehouseArea> warehouseAreaRepository) : base(BaseDal) { _unitOfWorkManage = unitOfWorkManage; _stockInfoService = stockInfoService; @@ -96,6 +98,7 @@ _allocateService = allocateService; _inboundOrderRepository = inboundOrderRepository; _inboundOrderDetailService = inboundOrderDetailService; _warehouseAreaRepository = warehouseAreaRepository; } @@ -747,13 +750,13 @@ PickTime = DateTime.Now, Operator = App.User.UserName, OutStockLockId = result.FinalLockInfo.Id, BarcodeUnit=result.FinalLockInfo.BarcodeUnit, BarcodeQty=result.FinalLockInfo.BarcodeQty, BatchNo= result.FinalLockInfo.BatchNo, lineNo= result.FinalLockInfo.lineNo , SupplyCode= result.FinalLockInfo.SupplyCode , WarehouseCode = result.FinalLockInfo.WarehouseCode , BarcodeUnit = result.FinalLockInfo.BarcodeUnit, BarcodeQty = result.FinalLockInfo.BarcodeQty, BatchNo = result.FinalLockInfo.BatchNo, lineNo = result.FinalLockInfo.lineNo, SupplyCode = result.FinalLockInfo.SupplyCode, WarehouseCode = result.FinalLockInfo.WarehouseCode, }; @@ -1383,7 +1386,7 @@ .SetColumns(it => new Dt_OutStockLockInfo { Status = (int)OutLockStockStatusEnum.已鿾, // éè¦æ°å¢è¿ä¸ªç¶æ // ReleaseTime = DateTime.Now, // ReleaseTime = DateTime.Now, Operator = App.User.UserName }) .Where(it => lockIds.Contains(it.Id)) @@ -1777,7 +1780,7 @@ { newStatus = (int)OutOrderStatusEnum.åºåºå®æ; } else if (hasPartial ) else if (hasPartial) { newStatus = (int)OutOrderStatusEnum.åºåºä¸; } @@ -1852,7 +1855,7 @@ .SetColumns(it => new Dt_OutboundOrderDetail { OrderDetailStatus = newStatus, }) .Where(it => it.Id == orderDetailId) .ExecuteCommandAsync(); @@ -1934,13 +1937,13 @@ ReqCode = Guid.NewGuid().ToString(), ReqTime = DateTime.Now.ToString(), BusinessType = "3", FactoryArea = outboundOrder.FactoryArea, OperationType = 1, Operator = App.User.UserName, OrderNo = outboundOrder.UpperOrderNo, // documentsNO = outboundOrder.OrderNo, // status = outboundOrder.OrderStatus, // documentsNO = outboundOrder.OrderNo, // status = outboundOrder.OrderStatus, fromWarehouse = allocate?.FromWarehouse ?? "", toWarehouse = allocate?.ToWarehouse ?? "", Details = new List<AllocateDtoDetail>() @@ -1958,7 +1961,7 @@ LineNo = group.Key.lineNo, WarehouseCode = group.Key.WarehouseCode, Qty = group.Sum(x => x.PickedQty), Unit = group.Key.Unit, Barcodes = group.Select(row => new BarcodeInfo { @@ -1969,13 +1972,13 @@ Qty = row.PickedQty }).ToList() }).ToList(); allocatefeedmodel.Details = groupedData; var result = await _invokeMESService.FeedbackAllocate(allocatefeedmodel); if (result != null && result.code == 200) { { await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>() .SetColumns(x => x.ReturnToMESStatus == 1) .Where(x => x.OrderId == outboundOrder.Id).ExecuteCommandAsync(); @@ -2436,8 +2439,8 @@ FactoryArea = originalLock.FactoryArea, lineNo = originalLock.lineNo, WarehouseCode = originalLock.WarehouseCode, BarcodeQty=originalLock.BarcodeQty, BarcodeUnit=originalLock.BarcodeUnit, BarcodeQty = originalLock.BarcodeQty, BarcodeUnit = originalLock.BarcodeUnit, }; @@ -2550,7 +2553,7 @@ { 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); return WebResponseContent.Instance.OK("æå", data: InOderCodes); } public WebResponseContent GetAvailablePickingOrders() @@ -2566,7 +2569,7 @@ 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) if (inboundOrder == null) { return WebResponseContent.Instance.Error($"æªæ¾å°éè´åï¼{noStockOut.inOder}"); } @@ -2593,7 +2596,7 @@ //å©ä½å ¥åºæ°éå³èæåºå ¥åºå©ä½å¯åºæ°é decimal outQuantity = matchedDetail.OrderQuantity - matchedDetail.ReceiptQuantity; if(outQuantity == 0) if (outQuantity == 0) { return WebResponseContent.Instance.Error($"该éè´åä¸çæ¡ç 对åºçå¯åºæ°é为0"); } @@ -2607,7 +2610,7 @@ if ((matchedCode.LockQuantity + matchedCode.NoStockOutQty) > matchedCode.OrderQuantity) { return WebResponseContent.Instance.Error($"åºåºåæç»æ°é溢åº{matchedCode.LockQuantity - matchedCode.OrderQuantity}"); return WebResponseContent.Instance.Error($"åºåºåæç»æ°é溢åº{matchedCode.LockQuantity - matchedCode.OrderQuantity}"); } matchedDetail.OrderDetailStatus = OrderDetailStatusEnum.Inbounding.ObjToInt(); matchedCode.OrderDetailStatus = OrderDetailStatusEnum.AssignOver.ObjToInt(); @@ -2618,7 +2621,7 @@ _unitOfWorkManage.CommitTran(); return WebResponseContent.Instance.OK(); } catch(Exception ex) catch (Exception ex) { _unitOfWorkManage.RollbackTran(); return WebResponseContent.Instance.Error(ex.Message); @@ -2643,7 +2646,7 @@ } matchedDetail.NoStockOutQty = 0; if(matchedDetail.ReceiptQuantity==0 && matchedDetail.OverInQuantity==0) if (matchedDetail.ReceiptQuantity == 0 && matchedDetail.OverInQuantity == 0) { matchedDetail.OrderDetailStatus = OrderDetailStatusEnum.New.ObjToInt(); } @@ -2671,23 +2674,23 @@ return WebResponseContent.Instance.OK(); } catch(Exception ex) catch (Exception ex) { _unitOfWorkManage.RollbackTran(); return WebResponseContent.Instance.Error(ex.Message); } } public WebResponseContent NoStockOutSubmit(NoStockOutSubmit noStockOutSubmit) public async Task<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(); Dt_InboundOrder inboundOrder = _inboundOrderRepository.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(); Dt_OutboundOrder outboundOrder = _inboundOrderRepository.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}"); @@ -2696,9 +2699,9 @@ 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()); var inboundOrderDetail = inboundOrder.Details.FirstOrDefault(detail => detail.Barcode == BarCode && detail.OrderDetailStatus != OrderDetailStatusEnum.Over.ObjToInt()); if(inboundOrderDetail == null) if (inboundOrderDetail == null) { return WebResponseContent.Instance.Error($"å¨éè´å {noStockOutSubmit.InOderSubmit} 䏿ªæ¾å°æ¡ç 为 {BarCode} çæç»ã"); } @@ -2715,7 +2718,7 @@ outboundOrderDetail.LockQuantity += outboundOrderDetail.NoStockOutQty; outboundOrderDetail.OverOutQuantity = outboundOrderDetail.LockQuantity; if(outboundOrderDetail.OrderQuantity == outboundOrderDetail.OverOutQuantity) if (outboundOrderDetail.OrderQuantity == outboundOrderDetail.OverOutQuantity) { outboundOrderDetail.OrderDetailStatus = OrderDetailStatusEnum.Over.ObjToInt(); } @@ -2752,9 +2755,115 @@ _outboundOrderService.UpdateData(outboundOrder); _unitOfWorkManage.CommitTran(); if (inboundOrder.OrderStatus == InOrderStatusEnum.å ¥åºå®æ.ObjToInt()) { var feedmodel = new FeedbackInboundRequestModel { reqCode = Guid.NewGuid().ToString(), reqTime = DateTime.Now.ToString(), business_type = inboundOrder.BusinessType, factoryArea = inboundOrder.FactoryArea, operationType = 1, Operator = inboundOrder.Operator, orderNo = inboundOrder.UpperOrderNo, status = inboundOrder.OrderStatus, details = new List<FeedbackInboundDetailsModel>() }; var groupedData = inboundOrder.Details.GroupBy(item => new { item.MaterielCode, item.SupplyCode, item.BatchNo, item.lineNo, item.BarcodeUnit, item.WarehouseCode }) .Select(group => new FeedbackInboundDetailsModel { materialCode = group.Key.MaterielCode, supplyCode = group.Key.SupplyCode, batchNo = group.Key.BatchNo, lineNo = group.Key.lineNo, warehouseCode = group.Key.WarehouseCode, qty = group.Sum(x => x.BarcodeQty), // warehouseCode= "1072", unit = group.Key.BarcodeUnit, barcodes = group.Select(row => new FeedbackBarcodesModel { barcode = row.Barcode, qty = row.BarcodeQty }).ToList() }).ToList(); feedmodel.details = groupedData; var result = await _invokeMESService.FeedbackInbound(feedmodel); if (result != null && result.code == 200) { _inboundOrderRepository.Db.Updateable<Dt_InboundOrder>().SetColumns(it => new Dt_InboundOrder { ReturnToMESStatus = 1 }) .Where(it => it.Id == inboundOrder.Id).ExecuteCommand(); _inboundOrderDetailService.Db.Updateable<Dt_InboundOrderDetail>().SetColumns(it => new Dt_InboundOrderDetail { ReturnToMESStatus = 1 }) .Where(it => it.OrderId == inboundOrder.Id).ExecuteCommand(); } } if (outboundOrder.OrderStatus == OutOrderStatusEnum.åºåºå®æ.ObjToInt()) { var feedmodel = new FeedbackOutboundRequestModel { reqCode = Guid.NewGuid().ToString(), reqTime = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"), business_type = outboundOrder.BusinessType, factoryArea = outboundOrder.FactoryArea, operationType = 1, Operator = outboundOrder.Operator, orderNo = outboundOrder.UpperOrderNo, documentsNO = outboundOrder.OrderNo, status = outboundOrder.OrderStatus, details = new List<FeedbackOutboundDetailsModel>() }; foreach (var detail in outboundOrder.Details) { // è·å该æç»å¯¹åºçæ¡ç ä¿¡æ¯ï¼ä»éå®è®°å½ï¼ var detailLocks = await _outStockLockInfoService.Db.Queryable<Dt_OutStockLockInfo>() .Where(x => x.OrderNo == outboundOrder.OrderNo && x.OrderDetailId == detail.Id && x.Status == (int)OutLockStockStatusEnum.æ£é宿) .ToListAsync(); var detailModel = new FeedbackOutboundDetailsModel { materialCode = detail.MaterielCode, lineNo = detail.lineNo, // 注æï¼è¿éå¯è½éè¦è°æ´å段å warehouseCode = detail.WarehouseCode, qty = detail.OverOutQuantity, // 使ç¨è®¢åæç»çå·²åºåºæ°é currentDeliveryQty = detail.OverOutQuantity, unit = detail.Unit, barcodes = detailLocks.Select(lockInfo => new WIDESEA_DTO.Outbound.BarcodesModel { barcode = lockInfo.CurrentBarcode, supplyCode = lockInfo.SupplyCode, batchNo = lockInfo.BatchNo, unit = lockInfo.Unit, qty = lockInfo.PickedQty // æ¡ç 级å«çæ°éä»ç¨éå®è®°å½ }).ToList() }; feedmodel.details.Add(detailModel); } var result = await _invokeMESService.FeedbackOutbound(feedmodel); if (result != null && result.code == 200) { await _outboundOrderDetailService.Db.Updateable<Dt_OutboundOrderDetail>() .SetColumns(x => x.ReturnToMESStatus == 1) .Where(x => x.OrderId == outboundOrder.Id) .ExecuteCommandAsync(); await _outboundOrderService.Db.Updateable<Dt_OutboundOrder>() .SetColumns(x => x.ReturnToMESStatus == 1) .Where(x => x.Id == outboundOrder.Id) .ExecuteCommandAsync(); } } return WebResponseContent.Instance.OK(); } catch(Exception ex) catch (Exception ex) { _unitOfWorkManage.RollbackTran(); return WebResponseContent.Instance.Error(ex.Message); @@ -2762,11 +2871,183 @@ } #endregion public WebResponseContent UnPalletQuantity(string orderNo) { // åå§åè¿åDTOï¼é»è®¤å¼é½ä¸º0ï¼é¿å nullï¼ var resultDTO = new PalletSumQuantityDTO { StockSumQuantity = 0, StockCount = 0, UniqueUnit = "" }; WebResponseContent content = new WebResponseContent(); try { if (string.IsNullOrWhiteSpace(orderNo)) { return content.Error("ä¼ å ¥ç订åå·orderNo为空æç©ºç½"); } var orderDetail = Db.Queryable<Dt_PickingRecord>().Where(s => s.OrderNo == orderNo).ToList(); if (orderDetail == null) { return content.Error("æªæ¾å°åæ®"); } var unitGroups = orderDetail.GroupBy(d => d.BarcodeUnit).ToList(); if (unitGroups.Count == 1) { resultDTO.UniqueUnit = unitGroups.First().Key; } else { resultDTO.UniqueUnit = ""; } var validDetails = _stockInfoDetailService.Db.Queryable<Dt_StockInfoDetail>().Where(s => s.OrderNo == orderNo).ToList(); resultDTO.StockSumQuantity = orderDetail.Sum(d => d.PickQuantity); resultDTO.StockCount = orderDetail.Count; if (validDetails.Any()) { resultDTO.StockSumQuantity -= validDetails.Sum(d => d.StockQuantity); // æç»è®°å½æ°ï¼ç¬¦åæ¡ä»¶çææè®°å½æ¡æ° resultDTO.StockCount -= validDetails.Count; } return content.OK("", resultDTO); } catch (Exception ex) { return content.Error("SumQuantity ç»è®¡åºåæ°é失败ï¼è®¢åå·ï¼{OrderNo}"); } } public WebResponseContent BarcodeMaterielGroup(BarcodeMaterielGroupDTO materielGroupDTO) { WebResponseContent content = new WebResponseContent(); try { (bool, string, object?) result2 = ModelValidate.ValidateModelData(materielGroupDTO); if (!result2.Item1) return content = WebResponseContent.Instance.Error(result2.Item2); // materielGroupDTO.WarehouseCode var code = _warehouseAreaRepository.Db.Queryable<Dt_WarehouseArea>().Where(x => x.Code == materielGroupDTO.WarehouseType).Select(x => x.Code).First(); if (string.IsNullOrEmpty(code)) { return content = WebResponseContent.Instance.Error($"ä»åºä¸æ²¡æè¯¥{materielGroupDTO.WarehouseType}ç¼å·ã"); } // Dt_InboundOrder inboundOrder = GetInboundOrder(materielGroupDTO.OrderNo); var dbinboundOrderDetails = Db.Queryable<Dt_PickingRecord>().Where(x=>x.OrderNo== materielGroupDTO.OrderNo && !x.IsCancelled && x.Barcode==materielGroupDTO.Barcodes).ToList(); if (dbinboundOrderDetails != null && !dbinboundOrderDetails.Any()) { return content = WebResponseContent.Instance.Error($"忮䏿²¡æè¯¥{materielGroupDTO.Barcodes}æ¡ç æ°æ®ã"); } List<string?> materielCodes = dbinboundOrderDetails.GroupBy(x => x.Barcode).Select(x => x.Key).ToList(); Dt_StockInfo? stockInfo = _stockService.StockInfoService.GetStockByPalletCode(materielGroupDTO.PalletCode); (bool, string, object?) result = CheckMaterielGroupParam(materielGroupDTO, materielCodes, stockInfo); if (!result.Item1) return content = WebResponseContent.Instance.Error(result.Item2); if (stockInfo == null) { stockInfo = new Dt_StockInfo() { PalletType = (int)PalletTypeEnum.None, LocationType = materielGroupDTO.locationType.ObjToInt() }; stockInfo.Details = new List<Dt_StockInfoDetail>(); } foreach (var item in dbinboundOrderDetails) { stockInfo.Details.Add(new Dt_StockInfoDetail { StockId = stockInfo == null ? 0 : stockInfo.Id, Barcode = item.Barcode, MaterielCode = item.MaterielCode, BatchNo = item.BatchNo, Unit = item.BarcodeUnit, InboundOrderRowNo = item.lineNo, SupplyCode = item.SupplyCode, WarehouseCode = materielGroupDTO.WarehouseType, StockQuantity = item.PickQuantity, BarcodeQty = item.BarcodeQty, BarcodeUnit = item.BarcodeUnit, FactoryArea = item.FactoryArea, Status = 0, OrderNo = item.OrderNo, }); item.WarehouseCode = item.WarehouseCode; } if (stockInfo.Id == 0) { stockInfo.PalletCode = materielGroupDTO.PalletCode; stockInfo.StockStatus = StockStatusEmun.ç»çæå.ObjToInt(); } stockInfo.PalletType = (int)PalletTypeEnum.None; List<int> updateDetailIds = dbinboundOrderDetails.Select(x => x.Id).ToList(); try { _unitOfWorkManage.BeginTran(); _stockService.StockInfoService.AddMaterielGroup(stockInfo); _unitOfWorkManage.CommitTran(); return WebResponseContent.Instance.OK(); } catch (Exception ex) { _unitOfWorkManage.RollbackTran(); return WebResponseContent.Instance.Error(ex.Message); } } catch (Exception ex) { content = WebResponseContent.Instance.Error(ex.Message); } finally { } return content; } public (bool, string, object?) CheckMaterielGroupParam(BarcodeMaterielGroupDTO materielGroupDTO, List<string> barcodeCodes, Dt_StockInfo stockInfo) { (bool, string, object?) result = ModelValidate.ValidateModelData(materielGroupDTO); if (!result.Item1) return result; if (_taskRepository.QueryFirst(x => x.PalletCode == materielGroupDTO.PalletCode) != null) { return (false, "该æçå·å·²æä»»å¡", materielGroupDTO); } if (stockInfo != null && !string.IsNullOrEmpty(stockInfo.LocationCode) && stockInfo.StockStatus != StockStatusEmun.ç»çæå.ObjToInt()) { return (false, "已䏿¶çæçä¸è½å次ç»ç", materielGroupDTO); } if (_stockService.StockInfoDetailService.ExistBarcodes(barcodeCodes)) { return (false, $"{barcodeCodes[0]} æ¡ç å¨åºåä¸å·²åå¨", materielGroupDTO); } return (true, "æå", materielGroupDTO); } #endregion } #region æ¯æç±»å®ä¹ ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Outbound/OutboundPickingController.cs
@@ -2,6 +2,7 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks; using WIDESEA_Core; using WIDESEA_Core.BaseController; using WIDESEA_DTO.Outbound; @@ -140,9 +141,9 @@ } [HttpPost, HttpGet, Route("NoStockOutSubmit"), AllowAnonymous] public WebResponseContent NoStockOutSubmit([FromBody] NoStockOutSubmit noStockOutSubmit) public async Task<WebResponseContent> NoStockOutSubmit([FromBody] NoStockOutSubmit noStockOutSubmit) { return Service.NoStockOutSubmit(noStockOutSubmit); return await Service.NoStockOutSubmit(noStockOutSubmit); } } } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Controllers/Outbound/PickingReturnController.cs
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,44 @@ using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using WIDESEA_Core; using WIDESEA_Core.Attributes; using WIDESEA_Core.BaseController; using WIDESEA_DTO.Inbound; using WIDESEA_IOutboundService; using WIDESEA_Model.Models; namespace WIDESEA_WMSServer.Controllers.Outbound { [Route("api/PickingReturn")] [ApiController] public class PickingReturnController : ApiBaseController<IOutboundPickingService, Dt_PickingRecord> { private readonly IOutStockLockInfoService _outStockLockInfoService; public PickingReturnController(IOutboundPickingService service, IOutStockLockInfoService outStockLockInfoService) : base(service) { _outStockLockInfoService = outStockLockInfoService; } [HttpPost, Route("UnPalletQuantity"), AllowAnonymous, MethodParamsValidate] public WebResponseContent UnPalletQuantity(string orderNo) { return Service.UnPalletQuantity(orderNo); } /// <summary> /// /// </summary> /// <param name="materielGroupDTO"></param> /// <returns></returns> [HttpPost, Route("BarcodeMaterielGroup"), AllowAnonymous, MethodParamsValidate] public WebResponseContent BarcodeMaterielGroup([FromBody] BarcodeMaterielGroupDTO data) { var content = Service.BarcodeMaterielGroup(data); return content; } } } ÏîÄ¿´úÂë/WMSÎÞ²Ö´¢°æ/WIDESEA_WMSServer/WIDESEA_WMSServer/Jobs/InventoryLockJob.cs
@@ -1,5 +1,9 @@ using Quartz; using SqlSugar; using SqlSugar.Extensions; using WIDESEA_Common.StockEnum; using WIDESEA_Model.Models; using WIDESEA_Model.Models.Basic; namespace WIDESEA_WMSServer.Jobs { @@ -16,7 +20,24 @@ } public Task Execute(IJobExecutionContext context) { return Task.CompletedTask; // ç¬¬ä¸æ¥ï¼æ´æ°ValidDateçåçSQLï¼SQL Serverï¼ string updateSql = @" UPDATE s SET s.ValidDate = DATEADD(DAY, m.ValidityDays, s.CreateDate) FROM Dt_StockInfoDetail s INNER JOIN Dt_MaterialExpirationDate m ON SUBSTRING(s.MaterielCode, 1, 6) = m.MaterialCode WHERE s.ValidDate IS NULL"; int updateValidDateResult = _db.Ado.ExecuteCommand(updateSql); // ç¬¬äºæ¥ï¼æ´æ°è¿æç¶æçåçSQL string updateStatusSql = @" UPDATE Dt_StockInfoDetail SET Status = 98 WHERE ValidDate IS NOT NULL AND ValidDate < GETDATE()"; int updateStatusResult = _db.Ado.ExecuteCommand(updateStatusSql); return Task.CompletedTask; } } }