fix:订单配备货相关链路功能调整

dev_1.0.2
jiangpeng 2026-06-01 01:02:48 +08:00
parent 7597a444b9
commit 71e8eb1e37
22 changed files with 623 additions and 308 deletions

View File

@ -243,6 +243,15 @@
<div class="stocking-product-meta">
<span class="stocking-product-meta__label">产品型号</span>
<span class="stocking-product-meta__value stocking-product-meta__value--strong">{{ item.model || '-' }}</span>
<el-button
v-if="item.vendorName && item.vendorName.startsWith('新华三')"
type="primary"
plain
icon="el-icon-plus"
size="mini"
style="margin-left: 12px;"
@click="handleGenerateVirtualPurchase(item)"
>生成虚拟采购单</el-button>
</div>
<div class="stocking-product-summary">
<div class="stocking-product-summary__item">
@ -250,32 +259,38 @@
<span class="stocking-product-summary__value">{{ item.orderNum !== undefined && item.orderNum !== null ? item.orderNum : '-' }}</span>
</div>
<div class="stocking-product-summary__item">
<span class="stocking-product-summary__label">已配:</span>
<span class="stocking-product-summary__value stocking-product-summary__value--green">{{ item.phNum !== undefined && item.phNum !== null ? item.phNum : '-' }}</span>
<span v-if="(item.orderNum !== undefined && item.orderNum !== null ? item.orderNum-(item.phNum||0) : 0) > 0" class="stocking-product-summary__value stocking-product-summary__value--red-bg" style="margin-left: 8px; font-size: 12px; font-weight: normal; padding: 2px 6px;">: {{ item.orderNum !== undefined && item.orderNum !== null ? item.orderNum-(item.phNum||0): '-'}}</span>
<span class="stocking-product-summary__label">已配:</span>
<span class="stocking-product-summary__value stocking-product-summary__value--green">{{ calculateCurrentTotalPhNum(item) }}</span>
<span v-if="(item.orderNum !== undefined && item.orderNum !== null ? item.orderNum-calculateCurrentTotalPhNum(item) : 0) > 0" class="stocking-product-summary__value stocking-product-summary__value--red-bg" style="margin-left: 8px; font-size: 12px; font-weight: normal; padding: 2px 6px;">: {{ item.orderNum !== undefined && item.orderNum !== null ? item.orderNum-calculateCurrentTotalPhNum(item): '-'}}</span>
</div>
<div class="stocking-product-summary__item">
<span class="stocking-product-summary__label">已备货:</span>
<span class="stocking-product-summary__value stocking-product-summary__value--green">{{ item.bhNum !== undefined && item.bhNum !== null ? item.bhNum : '-' }}</span>
<span v-if="(item.orderNum !== undefined && item.orderNum !== null ? item.orderNum-(item.bhNum||0) : 0) > 0" class="stocking-product-summary__value stocking-product-summary__value--red-bg" style="margin-left: 8px; font-size: 12px; font-weight: normal; padding: 2px 6px;">: {{ item.orderNum !== undefined && item.orderNum !== null ? item.orderNum-(item.bhNum||0): '-'}}</span>
<span v-if="item.bhNum !== undefined && item.bhNum !== null && item.orderNum !== undefined && item.orderNum !== null && item.bhNum === item.orderNum" class="stocking-product-summary__value" style="margin-left: 8px; font-size: 12px; font-weight: normal; padding: 2px 6px; background: #67c23a; color: #fff; border-radius: 3px;"></span>
</div>
</div>
</div>
<el-table :data="item.bindList || []" class="stocking-product-table">
<el-table-column label="采购单号" align="center" prop="purchaseNo" width="200" />
<el-table-column label="制造者" align="center" prop="vendorAddress" width="200" />
<el-table-column label="批次数量" align="center" prop="cgNum" />
<el-table-column label="可用" align="center" prop="kyNum">
<el-table-column label="采购单号" align="center" prop="purchaseNo" width="250" />
<el-table-column label="入库状态" align="center" prop="status" >
<template slot-scope="scope">
<span>{{ String(scope.row.status) === '0' ? '-' : scope.row.kyNum }}</span>
<dict-tag :options="dict.type.purchase_status" :value="String(scope.row.status)"/>
</template>
</el-table-column>
<el-table-column label="配额量" align="center" prop="phNum" width="140">
<el-table-column label="制造者" align="center" prop="vendorAddress" />
<el-table-column label="单价" align="center" prop="price" width="130">
<template slot-scope="scope">
<span style="color: #f05a00;"> {{ formatCurrency(scope.row.price) }}</span>
</template>
</el-table-column>
<el-table-column label="批次数量" align="center" prop="cgNum" />
<el-table-column label="可用" align="center" prop="kyNum" />
<el-table-column label="配额" align="center" prop="phNum" >
<template slot-scope="scope">
<el-input
v-model="scope.row.phNum"
type="number"
:disabled="String(scope.row.status) === '0'"
:min="scope.row.bhNum || 0"
:max="calculateMaxPhNum(scope.row, item)"
size="small"
@ -285,19 +300,14 @@
></el-input>
</template>
</el-table-column>
<el-table-column label="入库状态" align="center" prop="status" width="120">
<template slot-scope="scope">
<dict-tag :options="dict.type.purchase_status" :value="String(scope.row.status)"/>
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="200">
<el-table-column label="已出库" align="center" prop="bhNum" />
<el-table-column label="操作" align="center" width="130" >
<template slot-scope="scope">
<el-button
v-if="['1', '2'].includes(String(scope.row.status))"
type="text"
icon="el-icon-edit-outline"
@click="handleAssignOuterSnCode(scope.row, item)"
>分配出库SN码</el-button>
>查看出库SN码</el-button>
</template>
</el-table-column>
</el-table>
@ -321,7 +331,7 @@
</div>
<div>
<el-button @click="stockingDetailVisible = false"> </el-button>
<!-- <el-button type="primary" :loading="stockingDetailSaving" @click="handleSaveStockingBind"></el-button>-->
<el-button type="primary" :loading="stockingDetailSaving" @click="handleSaveStockingBind"></el-button>
</div>
</div>
</el-dialog>
@ -337,8 +347,8 @@
<div class="stocking-save-confirm__title">本次将保存 {{ stockingSaveChangedList.length }} 条配额量变更是否继续</div>
<el-table :data="stockingSaveChangedList" max-height="300" size="mini">
<el-table-column label="采购单号" align="center" prop="purchaseNo" min-width="160" :show-overflow-tooltip="true" />
<el-table-column label="原配额" align="center" prop="originalBindNum" width="100" />
<el-table-column label="新配额" align="center" prop="bindNum" width="100" />
<el-table-column label="原配额" align="center" prop="originalBindNum" width="100" />
<el-table-column label="新配额" align="center" prop="bindNum" width="100" />
</el-table>
</div>
<div slot="footer" class="dialog-footer">
@ -356,7 +366,7 @@
>
<div slot="title" class="outer-sn-dialog-title">
<i class="el-icon-box"></i>
<span>分配出库 SN </span>
<span>出库 SN </span>
</div>
<div v-loading="outerSnLoading" class="outer-sn-dialog-body">
<div class="outer-sn-header">
@ -406,22 +416,17 @@
<div class="outer-sn-grid-container">
<div class="outer-sn-grid-header">
<el-checkbox
v-model="outerSnSelectAll"
:indeterminate="outerSnSelectIndeterminate"
@change="handleOuterSnSelectAll"
>
全选
</el-checkbox>
<span class="outer-sn-current-count">当前仓库SN码总数{{ getWarehouseSnCount(outerSnActiveWarehouse) }}</span>
<el-button
type="text"
size="small"
style="color: #f56c6c;"
@click="handleOuterSnClearSelected"
>
清空已选
</el-button>
<pagination
small
layout="total, sizes, prev, pager, next"
:total="outerSnPagination.total"
:page.sync="outerSnPagination.pageNum"
:limit.sync="outerSnPagination.pageSize"
:page-sizes="[100, 200, 500]"
@pagination="handleOuterSnPageChange"
style="display: inline-block;"
/>
</div>
<div class="outer-sn-grid">
<div
@ -432,27 +437,29 @@
'outer-sn-card--selected': isSnSelected(sn.productSn),
'outer-sn-card--disabled': isSnDisabled(sn)
}"
@click="handleSnCardClick(sn)"
>
<el-checkbox
:value="isSnSelected(sn.productSn)"
:disabled="isSnDisabled(sn)"
@click.native.stop
@change="handleSnCheckboxChange(sn, $event)"
/>
<span class="outer-sn-card-text">{{ sn.productSn }}</span>
</div>
</div>
</div>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="outerSnDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleConfirmSnSelection">
确认分配 {{ outerSnSelectedCount }}
</el-button>
<el-button @click="outerSnDialogVisible = false">关闭</el-button>
</div>
</el-dialog>
<!-- 新增采购单弹窗 -->
<el-dialog :title="purchaseTitle" :close-on-click-modal="false" :visible.sync="purchaseOpen" width="80vw" append-to-body>
<purchase-order-detail ref="purchaseOrderDetail" :order-data="currentPurchaseOrderData"
@close="purchaseOpen = false"
@success="handlePurchaseSuccess">
</purchase-order-detail>
<template slot="footer" class="dialog-footer">
<el-button type="primary" @click="submitPurchaseForm"> </el-button>
<el-button @click="cancelPurchase"> </el-button>
</template>
</el-dialog>
</div>
</template>
@ -464,13 +471,15 @@ import { getToken } from "@/utils/auth";
import OrderDetailDrawer from '../../project/order/OrderDetailDrawer.vue';
import ProjectDetailDrawer from '../../project/info/ProjectDetailDrawer.vue';
import Edit from './edit.vue';
import PurchaseOrderDetail from '../../purchaseorder/components/PurchaseOrderDetail.vue';
export default {
name: "Execution",
components: {
ProjectDetailDrawer,
OrderDetailDrawer,
Edit // Register the Edit component
Edit,
PurchaseOrderDetail
},
dicts: ['execution_outer_status', 'execution_delivery_status', 'execution_sign_status', 'purchase_status'],
data() {
@ -518,6 +527,16 @@ export default {
outerSnSelectedList: [],
outerSnSelectAll: false,
outerSnSelectIndeterminate: false,
outerSnPagination: {
pageNum: 1,
pageSize: 100,
total: 0
},
// --- ---
purchaseOpen: false,
purchaseTitle: '',
currentPurchaseOrderData: null,
virtualPurchaseProductInfo: null,
//
queryParams: {
pageNum: 1,
@ -550,6 +569,23 @@ export default {
created() {
this.getList();
},
watch: {
purchaseOpen(val) {
if (!val) {
this.currentPurchaseOrderData = null;
this.virtualPurchaseProductInfo = null;
this.$refs.purchaseOrderDetail?.resetForm();
} else {
this.$nextTick(() => {
if (this.virtualPurchaseProductInfo) {
this.$refs.purchaseOrderDetail?.initVirtualPurchase(this.virtualPurchaseProductInfo);
} else {
this.$refs.purchaseOrderDetail?.resetForm();
}
});
}
}
},
computed: {
outerSnSelectedCount() {
return this.outerSnSelectedList.length;
@ -559,7 +595,24 @@ export default {
return Number.isNaN(quotaQuantity) ? 0 : quotaQuantity;
},
stockingTotalCost() {
return (this.stockingDetailList || []).reduce((total, item) => total + (Number(item.sumCost) || 0), 0);
return (this.stockingDetailList || []).reduce((total, item) => {
const baseCost = Number(item.sumCost) || 0;
let additionalCost = 0;
if (item.allBindList) {
item.allBindList.forEach(bindItem => {
const originalPhNum = bindItem.originalPhNum === '' || bindItem.originalPhNum === null || bindItem.originalPhNum === undefined ? 0 : Number(bindItem.originalPhNum);
const currentPhNum = this.stockingBindDraftMap[bindItem.purchaseId] !== undefined
? Number(this.stockingBindDraftMap[bindItem.purchaseId])
: (bindItem.phNum === '' || bindItem.phNum === null || bindItem.phNum === undefined ? 0 : Number(bindItem.phNum));
const price = Number(bindItem.price) || 0;
const diff = currentPhNum - originalPhNum;
additionalCost += diff * price;
});
}
return total + baseCost + additionalCost;
}, 0);
},
filteredSnList() {
let list = this.outerSnDetail.snList || [];
@ -570,7 +623,10 @@ export default {
const keyword = this.outerSnSearchKeyword.toLowerCase();
list = list.filter(sn => (sn.productSn || '').toLowerCase().includes(keyword));
}
return list;
this.outerSnPagination.total = list.length;
const start = (this.outerSnPagination.pageNum - 1) * this.outerSnPagination.pageSize;
const end = start + this.outerSnPagination.pageSize;
return list.slice(start, end);
}
},
methods: {
@ -689,13 +745,22 @@ export default {
this.stockingDetailLoading = true;
productMatchList(row.orderCode).then(response => {
const list = response.data || [];
return Promise.all(list.map(item => this.loadBindPageData(row.orderCode, {
...item,
originalTotalPhNum: item.phNum,
bindList: [],
bindTotal: 0,
bindPageNum: 1,
bindPageSize: this.stockingBindPageSize
return Promise.all(list.map(item => this.loadAllBindData(row.orderCode, item).then(allBindList => {
const bindPageNum = 1;
const bindPageSize = this.stockingBindPageSize;
const startIndex = (bindPageNum - 1) * bindPageSize;
const endIndex = startIndex + bindPageSize;
const bindList = allBindList.slice(startIndex, endIndex);
return {
...item,
originalTotalPhNum: item.phNum,
allBindList: allBindList,
bindList: bindList,
bindTotal: allBindList.length,
bindPageNum: bindPageNum,
bindPageSize: bindPageSize
};
})));
}).then(list => {
this.stockingDetailList = list;
@ -720,6 +785,7 @@ export default {
row.phNum = validValue;
this.$set(this.stockingBindDraftMap, row.purchaseId, validValue);
this.updateItemTotalPhNum(item);
},
calculateMaxPhNum(row, item) {
const originalPhNum = row.originalPhNum === '' || row.originalPhNum === null || row.originalPhNum === undefined ? 0 : Number(row.originalPhNum);
@ -728,58 +794,45 @@ export default {
const maxPhysical = originalPhNum + originalKyNum;
let otherRowsPhNumSum = 0;
if (item && item.bindList) {
item.bindList.forEach(b => {
if (b.purchaseId !== row.purchaseId) {
const p = Number(b.phNum) || 0;
otherRowsPhNumSum += p;
}
});
}
const currentTotalPhNum = this.calculateCurrentTotalPhNum(item);
const currentRowPhNum = this.stockingBindDraftMap[row.purchaseId] !== undefined
? Number(this.stockingBindDraftMap[row.purchaseId])
: (row.phNum === '' || row.phNum === null || row.phNum === undefined ? 0 : Number(row.phNum));
const maxAllowedByOrder = orderNum - otherRowsPhNumSum;
const maxAllowedByOrder = orderNum - currentTotalPhNum + currentRowPhNum;
return Math.min(maxPhysical, maxAllowedByOrder);
},
loadBindPageData(orderCode, item) {
loadAllBindData(orderCode, item) {
return productMatchBindList(orderCode, item.productCode, {
pageNum: item.bindPageNum,
pageSize: item.bindPageSize,
pageNum: 1,
pageSize: 9999,
orderByColumn: 't6.createTime',
isAsc: 'desc'
}).then(bindResponse => {
const bindList = (bindResponse.rows || []).map(bindItem => ({
return (bindResponse.rows || []).map(bindItem => ({
...bindItem,
phNum: this.getDraftBindNum(bindItem.purchaseId, bindItem.phNum),
originalPhNum: bindItem.originalPhNum !== undefined ? bindItem.originalPhNum : bindItem.phNum,
originalKyNum: bindItem.originalKyNum !== undefined ? bindItem.originalKyNum : bindItem.kyNum
}));
return {
...item,
originalTotalPhNum: item.originalTotalPhNum !== undefined ? item.originalTotalPhNum : item.phNum,
bindList,
bindTotal: bindResponse.total || 0
};
}).catch(() => {
return {
...item,
bindList: [],
bindTotal: 0
};
return [];
});
},
handleBindPageChange(item) {
this.stockingDetailLoading = true;
this.loadBindPageData(this.stockingDetailRow.orderCode, item).then(updatedItem => {
this.stockingDetailList = this.stockingDetailList.map(currentItem => {
if (currentItem.productCode === updatedItem.productCode) {
return updatedItem;
}
return currentItem;
});
}).finally(() => {
this.stockingDetailLoading = false;
const startIndex = (item.bindPageNum - 1) * item.bindPageSize;
const endIndex = startIndex + item.bindPageSize;
const bindList = (item.allBindList || []).slice(startIndex, endIndex);
this.stockingDetailList = this.stockingDetailList.map(currentItem => {
if (currentItem.productCode === item.productCode) {
return {
...currentItem,
bindList: bindList
};
}
return currentItem;
});
},
handleBindNumChange(row, item) {
@ -797,24 +850,56 @@ export default {
row.kyNum = originalKyNum - diff;
this.$set(this.stockingBindDraftMap, row.purchaseId, nextValue);
this.updateItemTotalPhNum(item);
},
calculateCurrentTotalPhNum(item) {
if (!item || !item.productCode || !item.allBindList) return 0;
let total = 0;
item.allBindList.forEach(bindItem => {
const purchaseId = bindItem.purchaseId;
if (this.stockingBindDraftMap && this.stockingBindDraftMap[purchaseId] !== undefined) {
total += Number(this.stockingBindDraftMap[purchaseId]);
} else {
const phNum = bindItem.phNum === '' || bindItem.phNum === null || bindItem.phNum === undefined ? 0 : Number(bindItem.phNum);
total += phNum;
}
});
return total;
},
findBindItemByPurchaseId(productCode, purchaseId) {
const product = this.stockingDetailList.find(item => item.productCode === productCode);
if (!product) return null;
return product.allBindList?.find(b => String(b.purchaseId) === String(purchaseId));
},
updateItemTotalPhNum(item) {
if (item) {
this.$set(item, 'phNum', this.calculateCurrentTotalPhNum(item));
}
},
handleSaveStockingBind() {
const changedList = [];
this.stockingDetailList.forEach(item => {
(item.bindList || []).forEach(bindItem => {
this.handleBindNumChange(bindItem, item);
const currentPhNum = bindItem.phNum === '' || bindItem.phNum === null || bindItem.phNum === undefined ? 0 : Number(bindItem.phNum);
const originalPhNum = bindItem.originalPhNum === '' || bindItem.originalPhNum === null || bindItem.originalPhNum === undefined ? 0 : Number(bindItem.originalPhNum);
if (currentPhNum !== originalPhNum) {
changedList.push({
orderId: this.stockingDetailRow.id,
purchaseId: bindItem.purchaseId,
purchaseNo: bindItem.purchaseNo,
originalBindNum: originalPhNum,
bindNum: currentPhNum
});
}
});
if (item.allBindList) {
item.allBindList.forEach(bindItem => {
const currentPhNum = this.stockingBindDraftMap[bindItem.purchaseId] !== undefined
? Number(this.stockingBindDraftMap[bindItem.purchaseId])
: (bindItem.phNum === '' || bindItem.phNum === null || bindItem.phNum === undefined ? 0 : Number(bindItem.phNum));
const originalPhNum = bindItem.originalPhNum === '' || bindItem.originalPhNum === null || bindItem.originalPhNum === undefined ? 0 : Number(bindItem.originalPhNum);
if (currentPhNum !== originalPhNum) {
changedList.push({
orderId: this.stockingDetailRow.id,
purchaseId: bindItem.purchaseId,
purchaseNo: bindItem.purchaseNo,
originalBindNum: originalPhNum,
bindNum: currentPhNum
});
}
});
}
});
if (!changedList.length) {
this.$message.warning('暂无变更数据');
@ -832,13 +917,33 @@ export default {
}))).then(() => {
this.$message.success('保存成功');
this.stockingDetailList.forEach(item => {
item.originalTotalPhNum = item.phNum;
(item.bindList || []).forEach(bindItem => {
bindItem.originalPhNum = bindItem.phNum;
bindItem.originalKyNum = bindItem.kyNum;
this.$delete(this.stockingBindDraftMap, bindItem.purchaseId);
});
item.originalTotalPhNum = this.calculateCurrentTotalPhNum(item);
if (item.allBindList) {
item.allBindList.forEach(bindItem => {
if (this.stockingBindDraftMap[bindItem.purchaseId] !== undefined) {
bindItem.phNum = this.stockingBindDraftMap[bindItem.purchaseId];
bindItem.originalPhNum = bindItem.phNum;
const originalKyNum = bindItem.originalKyNum === '' || bindItem.originalKyNum === null || bindItem.originalKyNum === undefined ? 0 : Number(bindItem.originalKyNum);
const diff = bindItem.phNum - (bindItem.originalPhNum || 0);
bindItem.kyNum = originalKyNum - diff;
bindItem.originalKyNum = bindItem.kyNum;
}
});
}
if (item.bindList) {
item.bindList.forEach(bindItem => {
if (this.stockingBindDraftMap[bindItem.purchaseId] !== undefined) {
bindItem.phNum = this.stockingBindDraftMap[bindItem.purchaseId];
bindItem.originalPhNum = bindItem.phNum;
const originalKyNum = bindItem.originalKyNum === '' || bindItem.originalKyNum === null || bindItem.originalKyNum === undefined ? 0 : Number(bindItem.originalKyNum);
const diff = bindItem.phNum - (bindItem.originalPhNum || 0);
bindItem.kyNum = originalKyNum - diff;
bindItem.originalKyNum = bindItem.kyNum;
}
});
}
});
this.stockingBindDraftMap = {};
this.stockingSaveConfirmVisible = false;
this.stockingDetailVisible = false;
this.stockingSaveChangedList = [];
@ -861,6 +966,7 @@ export default {
this.outerSnSelectedList = [];
this.outerSnSelectAll = false;
this.outerSnSelectIndeterminate = false;
this.outerSnPagination = { pageNum: 1, pageSize: 100, total: 0 };
if (!row || !row.purchaseNo) {
this.$message.warning('采购单号为空无法查询SN码');
return;
@ -882,18 +988,8 @@ export default {
const alreadyBoundSns = this.outerSnDetail.snList
.filter(sn => sn.orderCode && sn.orderCode === this.stockingDetailRow.orderCode)
.map(sn => sn.productSn);
const availableSns = this.outerSnDetail.snList
.filter(sn => !this.isSnDisabled(sn))
.map(sn => sn.productSn);
let initialSelected = [...alreadyBoundSns];
if (initialSelected.length < this.outerSnQuotaQuantity) {
const needCount = this.outerSnQuotaQuantity - initialSelected.length;
const remainingToSelect = availableSns.filter(sn => !initialSelected.includes(sn)).slice(0, needCount);
initialSelected = initialSelected.concat(remainingToSelect);
}
this.outerSnSelectedList = initialSelected.slice(0, this.outerSnQuotaQuantity);
this.outerSnSelectedList = alreadyBoundSns;
this.updateSelectAllState();
}).finally(() => {
this.outerSnLoading = false;
@ -907,6 +1003,9 @@ export default {
return this.outerSnSelectedList.includes(productSn);
},
isSnDisabled(sn) {
if (sn.orderCode && sn.orderCode === (this.stockingDetailRow && this.stockingDetailRow.orderCode)) {
return false;
}
return !!(sn.outerCode || (sn.orderCode && sn.orderCode !== (this.stockingDetailRow && this.stockingDetailRow.orderCode)));
},
handleSnCardClick(sn) {
@ -934,36 +1033,6 @@ export default {
}
this.updateSelectAllState();
},
handleOuterSnSelectAll(checked) {
const quotaQuantity = this.outerSnQuotaQuantity;
const availableSns = this.filteredSnList.filter(sn => !this.isSnDisabled(sn)).map(sn => sn.productSn);
if (checked) {
if (quotaQuantity <= 0) {
this.$message.warning('当前配额数量为0不能选择SN码');
this.outerSnSelectAll = false;
this.outerSnSelectIndeterminate = false;
return;
}
const normalizedSelectedList = this.outerSnSelectedList.slice(0, quotaQuantity);
const unselectedSns = availableSns.filter(sn => !normalizedSelectedList.includes(sn));
const remainCount = quotaQuantity - normalizedSelectedList.length;
this.outerSnSelectedList = normalizedSelectedList.concat(unselectedSns.slice(0, Math.max(remainCount, 0)));
if (availableSns.length > this.outerSnSelectedList.filter(sn => availableSns.includes(sn)).length) {
this.$message.warning(`最多只能选择 ${quotaQuantity} 个SN码`);
}
} else {
this.outerSnSelectedList = this.outerSnSelectedList.filter(sn => !availableSns.includes(sn)).slice(0, quotaQuantity);
}
this.updateSelectAllState();
},
handleOuterSnClearSelected() {
const quotaQuantity = this.outerSnQuotaQuantity;
if (this.outerSnSelectedList.length > quotaQuantity) {
this.$message.warning(`已选SN码数量超过配额 ${quotaQuantity},已清空选择`);
}
this.outerSnSelectedList = [];
this.updateSelectAllState();
},
canSelectMoreSn(showMessage = true) {
if (this.outerSnSelectedList.length < this.outerSnQuotaQuantity) {
return true;
@ -984,72 +1053,6 @@ export default {
this.outerSnSelectAll = selectedInCurrent === availableSns.length;
this.outerSnSelectIndeterminate = selectedInCurrent > 0 && selectedInCurrent < availableSns.length;
},
handleConfirmSnSelection() {
if (this.outerSnSelectedList.length > this.outerSnQuotaQuantity) {
this.$message.warning(`最多只能选择 ${this.outerSnQuotaQuantity} 个SN码`);
return;
}
this.$confirm(`确认将选中的 ${this.outerSnSelectedList.length} 个SN码分配给当前订单`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}).then(() => {
const loading = this.$loading({
lock: true,
text: '正在处理中...',
spinner: 'el-icon-loading',
background: 'rgba(0, 0, 0, 0.7)'
});
//
const changedList = [];
this.stockingDetailList.forEach(item => {
(item.bindList || []).forEach(bindItem => {
const currentPhNum = bindItem.phNum === '' || bindItem.phNum === null || bindItem.phNum === undefined ? 0 : Number(bindItem.phNum);
const originalPhNum = bindItem.originalPhNum === '' || bindItem.originalPhNum === null || bindItem.originalPhNum === undefined ? 0 : Number(bindItem.originalPhNum);
if (currentPhNum !== originalPhNum) {
changedList.push({
orderId: this.stockingDetailRow.id,
purchaseId: bindItem.purchaseId,
purchaseNo: bindItem.purchaseNo,
originalBindNum: originalPhNum,
bindNum: currentPhNum,
bindItem: bindItem
});
}
});
});
let saveQuotaPromise = Promise.resolve();
if (changedList.length > 0) {
saveQuotaPromise = Promise.all(changedList.map(item => savePurchaseOrderMap({
orderId: item.orderId,
purchaseId: item.purchaseId,
bindNum: item.bindNum
})));
}
saveQuotaPromise.then(() => {
return bindOrderSnCodes(this.stockingDetailRow.orderCode, this.outerSnProductRow.productCode, this.outerSnBindRow.purchaseNo, this.stockingDetailRow.id, this.outerSnBindRow.purchaseId, this.outerSnSelectedList);
}).then(response => {
loading.close();
this.$message.success(`成功分配 ${this.outerSnSelectedList.length} 个SN码` + (changedList.length > 0 ? ',并同步保存了配额修改' : ''));
this.outerSnDialogVisible = false;
if (changedList.length > 0) {
changedList.forEach(item => {
item.bindItem.originalPhNum = item.bindNum;
this.$delete(this.stockingBindDraftMap, item.purchaseId);
});
}
this.handleShowStockingDetail(this.stockingDetailRow);
this.getList();
}).catch(() => {
loading.close();
});
}).catch(() => {});
},
/** 格式化备货状态 */
formatOrderStockingStatus(value) {
const statusMap = {
@ -1069,6 +1072,64 @@ export default {
BHWC: 'stocking-status--green'
};
return classMap[value] || 'stocking-status--yellow';
},
handleGenerateVirtualPurchase(item) {
this.currentPurchaseOrderData = null;
this.virtualPurchaseProductInfo = {
productCode: item.productCode,
model: item.model,
productType: item.productType,
vendorName: item.vendorName,
vendorCode: item.vendorCode,
orderNum: item.orderNum,
orderCode: this.stockingDetailRow.orderCode,
projectName: this.stockingDetailRow.projectName,
projectCode: this.stockingDetailRow.projectCode
};
this.purchaseOpen = true;
this.purchaseTitle = `生成虚拟采购单 - ${item.model || item.productCode}`;
},
submitPurchaseForm() {
this.$refs.purchaseOrderDetail.submitForm();
},
cancelPurchase() {
this.purchaseOpen = false;
},
handleOuterSnPageChange() {
},
handlePurchaseSuccess() {
this.purchaseOpen = false;
this.$message.success('采购单创建成功');
this.getList();
if (this.virtualPurchaseProductInfo && this.stockingDetailVisible) {
this.refreshCurrentProductBindList(this.virtualPurchaseProductInfo.productCode);
}
},
refreshCurrentProductBindList(productCode) {
const targetItem = this.stockingDetailList.find(item => item.productCode === productCode);
if (!targetItem) {
return;
}
this.loadAllBindData(this.stockingDetailRow.orderCode, targetItem).then(allBindList => {
const bindPageNum = targetItem.bindPageNum || 1;
const bindPageSize = targetItem.bindPageSize || this.stockingBindPageSize;
const startIndex = (bindPageNum - 1) * bindPageSize;
const endIndex = startIndex + bindPageSize;
const bindList = allBindList.slice(startIndex, endIndex);
this.stockingDetailList = this.stockingDetailList.map(item => {
if (item.productCode === productCode) {
return {
...item,
allBindList: allBindList,
bindList: bindList,
bindTotal: allBindList.length
};
}
return item;
});
}).catch(() => {
this.$message.error('刷新采购列表失败');
});
}
}
};
@ -1374,6 +1435,12 @@ export default {
border-bottom: 1px solid #e4e7ed;
}
.outer-sn-grid-header .pagination-container {
margin: 0;
padding: 0;
background: transparent;
}
.outer-sn-current-count {
flex: 1;
margin-left: 16px;

View File

@ -548,10 +548,10 @@ export default {
this.$modal.msgWarning("请至少添加一条产品信息");
return;
}
if (this.form.quantity!==this.form.inventoryInfoList.length && !this.isServiceIn){
this.$modal.msgWarning("采购数量与入库数量不一致");
return;
}
// if (this.form.quantity!==this.form.inventoryInfoList.length && !this.isServiceIn){
// this.$modal.msgWarning("");
// return;
// }
if (this.form.id != null) {
updateInner(this.form).then(response => {
this.$modal.msgSuccess("修改成功");

View File

@ -227,12 +227,11 @@ export default {
});
},
handleSelectPurchaseBeforeImport() {
if (((this.productData.orderType || '1') === '1' ) && !this.outerData.vendorName.startsWith('新华三') ) {
// if (((this.productData.orderType || '1') === '1' ) && !this.outerData.vendorName.startsWith('') ) {
this.purchaseOrderSelectVisible = true;
}else{
this.handleImport()
}
// }else{
// this.handleImport()
// }
},
handlePurchaseOrderSelect(order) {
this.warehouseId = order.warehouseId;
@ -304,6 +303,7 @@ export default {
outerCode: this.outerData.outerCode,
detailId: this.productData.id,
productSnDataList: this.isImported ? this.snList : [],
orderCode: this.queryParams.orderCode,
};
addDelivery(data).then(() => {

View File

@ -205,6 +205,7 @@ import SelectProduct from "@/views/system/product/selectProduct";
import FileUpload from "@/components/FileUpload";
import { getDicts } from "@/api/system/dict/data";
import TaxRateInput from "@/components/TaxRateInput/TaxInput.vue";
import { listProduct } from "@/api/project/product";
import request from '@/utils/request'
@ -260,6 +261,7 @@ export default {
approveStatus: 0,
approveNode: null,
confirmStatus: null,
isVirtual: false,
omsPurchaseOrderItemList: [
{
productType: null,
@ -488,6 +490,65 @@ export default {
}
});
},
initVirtualPurchase(item) {
this.resetForm();
this.form.isVirtual = true;
const applyDefaults = () => {
if (item.vendorCode && this.vendorOptions.length > 0) {
const vendor = this.vendorOptions.find(v => v.vendorCode === item.vendorCode);
if (vendor) {
this.form.vendorId = vendor.vendorId;
this.$nextTick(() => {
this.handleVendorChange(vendor.vendorId);
});
}
}
if (item.productType && this.form.omsPurchaseOrderItemList.length > 0) {
this.$set(this.form.omsPurchaseOrderItemList[0], 'productType', item.productType);
if (item.productCode && item.vendorCode) {
listProduct({
productCode: item.productCode,
vendorCode: item.vendorCode,
type: item.productType,
pageNum: 1,
pageSize: 1
}).then(response => {
if (response.rows && response.rows.length > 0) {
const product = response.rows[0];
const firstItem = this.form.omsPurchaseOrderItemList[0];
this.$set(firstItem, 'productCode', product.productCode);
this.$set(firstItem, 'productModel', product.model);
this.$set(firstItem, 'productDescription', product.description);
this.$set(firstItem, 'quantity', item.orderNum || 1);
this.calculateRowTotal(firstItem);
}
}).catch(() => {
});
}
}
if (item.projectCode && item.projectName) {
this.form.remark = `${item.projectCode} ${item.projectName}`;
}
};
const vendorReady = this.vendorOptions.length > 0
? Promise.resolve()
: this.getVendorList();
const productTypeReady = this.productTypeOptions.length > 0
? Promise.resolve()
: getDicts('product_type').then(response => {
this.productTypeOptions = response.data;
});
Promise.all([vendorReady, productTypeReady]).then(() => {
this.$nextTick(applyDefaults);
});
},
resetForm() {
this.form = {
id: null,
@ -511,6 +572,7 @@ export default {
approveStatus: 0,
approveNode: null,
confirmStatus: null,
isVirtual: false,
omsPurchaseOrderItemList: [
{
productType: null,

View File

@ -155,10 +155,10 @@ export default {
this.handleQuery();
},
handleSelect(row) {
if (this.quantity && this.quantity!==row.quantity){
this.$message.error("请选择数量为"+this.quantity+"的采购单");
return
}
// if (this.quantity && this.quantity!==row.quantity){
// this.$message.error(""+this.quantity+"");
// return
// }
this.$emit("select", row);
this.handleClose();
},

View File

@ -189,7 +189,7 @@
>修改
</el-button>
<el-button
v-if="scope.row.approveStatus === '0' || scope.row.approveStatus === '3'"
v-if="(scope.row.approveStatus === '0' || scope.row.approveStatus === '3') && !scope.row.isVirtual"
size="mini"
type="text"
icon="el-icon-s-promotion"

View File

@ -128,6 +128,9 @@ public class OmsPurchaseOrder extends BaseEntity
@Excel(name = "备注", wrapText = true)
private String remark;
/** 是否虚拟采购单 */
private Boolean isVirtual;
/** 删除标志0正常 1删除 */
private Integer delFlag;
@ -164,14 +167,12 @@ public class OmsPurchaseOrder extends BaseEntity
this.code = code;
this.value = value;
}
}
@Getter
public enum FlowTypeEnum {
ONLINE("online", "线上"),
OFFLINE("offline", "线下"),
;
private final String value;
@ -181,25 +182,22 @@ public class OmsPurchaseOrder extends BaseEntity
this.code = code;
this.value = value;
}
}
@Getter
@Getter
public enum StatusEnum {
WAIT_COMPLETED(0, "待入库"),
PART_COMPLETED(1, "部分入库"),
COMPLETED(2, "已完成"),
;
private final String value;
private final Integer code;
StatusEnum(Integer code, String value) {
StatusEnum(Integer code, String value) {
this.code = code;
this.value = value;
}
}
}

View File

@ -83,6 +83,8 @@ public class OmsPurchaseOrderHistory extends BaseEntity
/** 确认状态0待确认 1已确认 */
private String confirmStatus;
private Boolean isVirtual;
/** 删除标志0正常 1删除 */
private Integer delFlag;
private String flowType;

View File

@ -4,6 +4,8 @@ import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.math.BigDecimal;
@Data
@AllArgsConstructor
@NoArgsConstructor
@ -24,6 +26,9 @@ public class OrderProductMatchBindDto {
/** 制造商 */
private String vendorAddress;
/** 单价 */
private Double price;
/** 采购数量 */
private Integer cgNum;

View File

@ -17,6 +17,15 @@ public class OrderProductMatchDto {
/** 产品型号 */
private String model;
/** 产品类型 */
private String productType;
/** 制造商编码 */
private String vendorCode;
/** 制造商名称 */
private String vendorName;
/** 订单数量 */
private Integer orderNum;

View File

@ -89,4 +89,6 @@ public interface InventoryInfoMapper
int updateOrderCodeByProductSnList(@Param("productSnList") List<String> productSnList, @Param("orderCode") String orderCode);
int countByOrderCodeAndPurchaseNo(@Param("orderCode") String orderCode, @Param("purchaseNo") String purchaseNo);
void clearOrderCodeByIds(@Param("idList") List<Long> idList);
}

View File

@ -101,5 +101,7 @@ public interface IOmsPurchaseOrderService
OmsPurchaseOrder selectOmsPurchaseOrderByNo(String purchaseNo);
List<OmsPurchaseOrderItem> listByItemId(Long itemId);
AddToNexRes addToNex(AddToNexReq addToNexReq);
}

View File

@ -122,4 +122,6 @@ public interface IProjectOrderInfoService
int bindOrderSnCodes(BindOrderSnCodesDto params);
void refreshOrderStockingStatus(String orderCode);
}

View File

@ -67,6 +67,8 @@ public class ExecutionTrackServiceImpl implements IExecutionTrackService {
private IProjectOrderInfoRecallService projectOrderInfoRecallService;
@Autowired
private OmsPurchaseOrderMapMapper omsPurchaseOrderMapMapper;
@Autowired
private IInventoryOuterDetailService inventoryOuterDetailService;
@Override
public ExecutionOrderVo selectInfo(Long id) {
ExecutionOrderVo vo = new ExecutionOrderVo();
@ -310,6 +312,10 @@ public class ExecutionTrackServiceImpl implements IExecutionTrackService {
inventoryOuter.setOrderCode(projectOrderInfo.getOrderCode());
List<InventoryOuter> inventoryOuters = outerService.selectInventoryOuterList(inventoryOuter);
if (CollUtil.isNotEmpty(inventoryOuters)) {
List<String> outerCodeList = inventoryOuters.stream()
.map(InventoryOuter::getOuterCode)
.collect(Collectors.toList());
inventoryOuterDetailService.deleteByOuterCode(outerCodeList);
outerService.deleteInventoryOuterByIds(inventoryOuters.stream().map(item -> item.getId().toString()).collect(Collectors.joining(",")));
}
Map<String, Long> outerSumMap = inventoryOuters.stream().collect(Collectors.toMap(InventoryOuter::getProductCode, InventoryOuter::getQuantity, Long::sum));

View File

@ -14,8 +14,10 @@ import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.sip.domain.*;
import com.ruoyi.sip.dto.ApiDataQueryDto;
import com.ruoyi.sip.dto.inventory.InventoryDeliveryDetailExcelDto;
import com.ruoyi.sip.mapper.InventoryInfoMapper;
import com.ruoyi.sip.mapper.InventoryOuterDetailMapper;
import com.ruoyi.sip.mapper.InventoryOuterMapper;
import com.ruoyi.sip.mapper.OmsPurchaseOrderMapMapper;
import com.ruoyi.sip.service.*;
import com.ruoyi.sip.vo.DeliveryApproveVo;
import com.ruoyi.sip.vo.DeliveryInfoVo;
@ -69,6 +71,12 @@ public class InventoryDeliveryServiceImpl implements IInventoryDeliveryService {
private IOmsPayableBillService payableBillService;
@Autowired
private IOmsReceivableBillService billService;
@Autowired
private IOmsPurchaseOrderService omsPurchaseOrderService;
@Resource
private OmsPurchaseOrderMapMapper omsPurchaseOrderMapMapper;
@Resource
private InventoryInfoMapper inventoryInfoMapper;
@Value("${oms.inventory.innerTax:0.13}")
private String defaultTax;
@ -104,6 +112,81 @@ public class InventoryDeliveryServiceImpl implements IInventoryDeliveryService {
@Override
public int insertInventoryDelivery(InventoryDelivery inventoryDelivery) {
List<String> productSnList = inventoryDelivery.getProductSnList();
List<InventoryInfo> productSnDataList = inventoryDelivery.getProductSnDataList();
if (CollUtil.isEmpty(productSnList) && CollUtil.isEmpty(productSnDataList)) {
throw new ServiceException("发货清单为空,保存失败");
}
if (StringUtils.isNotEmpty(inventoryDelivery.getOrderCode())) {
ProjectOrderInfo orderInfo = projectOrderInfoService.selectProjectOrderInfoByOrderCode(inventoryDelivery.getOrderCode());
if (orderInfo == null) {
throw new ServiceException("订单信息不存在");
}
if (StringUtils.isNotEmpty(inventoryDelivery.getPurchaseNo())) {
int newSnCount = CollUtil.isEmpty(productSnList) ? productSnDataList.size() : productSnList.size();
OmsPurchaseOrder purchaseOrder = omsPurchaseOrderService.selectOmsPurchaseOrderByNo(inventoryDelivery.getPurchaseNo());
if (purchaseOrder == null) {
throw new ServiceException("采购单信息不存在");
}
OmsPurchaseOrderMap purchaseOrderMap = omsPurchaseOrderMapMapper.selectByOrderIdAndPurchaseId(orderInfo.getId(), purchaseOrder.getId());
if (purchaseOrderMap == null || purchaseOrderMap.getBindNum() == null || purchaseOrderMap.getBindNum() <= 0) {
throw new ServiceException("当前订单与采购单未建立绑定关系或绑定数量为0");
}
int totalBindNum = purchaseOrderMap.getBindNum();
int alreadyBindNum = inventoryInfoMapper.countByOrderCodeAndPurchaseNo(inventoryDelivery.getOrderCode(), inventoryDelivery.getPurchaseNo());
int remainingNum = totalBindNum - alreadyBindNum;
if (newSnCount > remainingNum) {
throw new ServiceException(String.format("新增SN码数量(%d)超过剩余可新增数量(%d),总绑定数(%d),已绑定数(%d)", newSnCount, remainingNum, totalBindNum, alreadyBindNum));
}
} else if (CollUtil.isNotEmpty(productSnList)) {
List<InventoryInfo> inventoryInfoList = inventoryInfoMapper.listByProductSnList(productSnList);
if (CollUtil.isEmpty(inventoryInfoList)) {
throw new ServiceException("未找到SN码对应的库存信息");
}
Map<String, List<InventoryInfo>> innerCodeMap = inventoryInfoList.stream()
.filter(info -> StringUtils.isNotEmpty(info.getInnerCode()))
.collect(Collectors.groupingBy(InventoryInfo::getInnerCode));
Map<String, Long> purchaseNoCountMap = new HashMap<>();
for (Map.Entry<String, List<InventoryInfo>> entry : innerCodeMap.entrySet()) {
String innerCode = entry.getKey();
OmsInventoryInner queryInner = new OmsInventoryInner();
queryInner.setInnerCode(innerCode);
List<OmsInventoryInner> innerList = omsInventoryInnerService.selectOmsInventoryInnerList(queryInner);
if (CollUtil.isNotEmpty(innerList) && StringUtils.isNotEmpty(innerList.get(0).getPurchaseNo())) {
String purchaseNo = innerList.get(0).getPurchaseNo();
purchaseNoCountMap.put(purchaseNo, purchaseNoCountMap.getOrDefault(purchaseNo, 0L) + entry.getValue().size());
}
}
for (Map.Entry<String, Long> entry : purchaseNoCountMap.entrySet()) {
String purchaseNo = entry.getKey();
long newSnCount = entry.getValue();
OmsPurchaseOrder purchaseOrder = omsPurchaseOrderService.selectOmsPurchaseOrderByNo(purchaseNo);
if (purchaseOrder == null) {
throw new ServiceException(String.format("采购单[%s]信息不存在", purchaseNo));
}
OmsPurchaseOrderMap purchaseOrderMap = omsPurchaseOrderMapMapper.selectByOrderIdAndPurchaseId(orderInfo.getId(), purchaseOrder.getId());
if (purchaseOrderMap == null || purchaseOrderMap.getBindNum() == null || purchaseOrderMap.getBindNum() <= 0) {
throw new ServiceException(String.format("订单与采购单[%s]未建立绑定关系或绑定数量为0", purchaseNo));
}
int totalBindNum = purchaseOrderMap.getBindNum();
int alreadyBindNum = inventoryInfoMapper.countByOrderCodeAndPurchaseNo(inventoryDelivery.getOrderCode(), purchaseNo);
int remainingNum = totalBindNum - alreadyBindNum;
if (newSnCount > remainingNum) {
throw new ServiceException(String.format("采购单[%s]新增SN码数量(%d)超过剩余可新增数量(%d),总绑定数(%d),已绑定数(%d)",
purchaseNo, newSnCount, remainingNum, totalBindNum, alreadyBindNum));
}
}
}
}
Date nowDate = DateUtils.getNowDate();
inventoryDelivery.setCreateTime(nowDate);
String currentUserId = ShiroUtils.getUserId().toString();
@ -112,12 +195,6 @@ public class InventoryDeliveryServiceImpl implements IInventoryDeliveryService {
inventoryDelivery.setUpdateBy(currentUserId);
inventoryDelivery.setUpdateTime(nowDate);
inventoryDelivery.setDeliveryStatus(InventoryDelivery.DeliveryStatusEnum.WAIT_DELIVERY.getCode());
//处理库存
List<String> productSnList = inventoryDelivery.getProductSnList();
List<InventoryInfo> productSnDataList = inventoryDelivery.getProductSnDataList();
if (CollUtil.isEmpty(productSnList) && CollUtil.isEmpty(productSnDataList)) {
throw new ServiceException("发货清单为空,保存失败");
}
//修改数据的时候同步修改出库价
BigDecimal bigDecimal = inventoryOuterMapper.selectOutPriceByCode(inventoryDelivery.getOuterCode());
@ -131,6 +208,7 @@ public class InventoryDeliveryServiceImpl implements IInventoryDeliveryService {
info.setUpdateBy(currentUserId);
info.setUpdateTime(nowDate);
info.setOuterPrice(bigDecimal);
info.setOrderCode(inventoryDelivery.getOrderCode());
OmsInventoryDeliveryDetail detail = new OmsInventoryDeliveryDetail();
detail.setProductSn(item);
detailList.add(detail);
@ -149,6 +227,7 @@ public class InventoryDeliveryServiceImpl implements IInventoryDeliveryService {
inventoryInfo.setUpdateBy(currentUserId);
inventoryInfo.setUpdateTime(nowDate);
inventoryInfo.setOuterPrice(bigDecimal);
inventoryInfo.setOrderCode(inventoryDelivery.getOrderCode());
OmsInventoryDeliveryDetail detail = new OmsInventoryDeliveryDetail();
detail.setProductSn(inventoryInfo.getProductSn());
detailList.add(detail);
@ -168,6 +247,7 @@ public class InventoryDeliveryServiceImpl implements IInventoryDeliveryService {
}
deliveryDetailService.saveBatch(detailList);
}
projectOrderInfoService.refreshOrderStockingStatus(inventoryDelivery.getOrderCode());
return i;
}
@ -417,7 +497,12 @@ public class InventoryDeliveryServiceImpl implements IInventoryDeliveryService {
if (realDelete) {
// 原逻辑删除库存表以及sn码的关联
deliveryDetailService.deleteOmsInventoryDeliveryDetailByDeliveryId(id);
return inventoryDeliveryMapper.deleteInventoryDeliveryById(id);
if (CollUtil.isNotEmpty(inventoryInfos)) {
inventoryInfoMapper.clearOrderCodeByIds(inventoryInfos.stream().map(InventoryInfo::getId).collect(Collectors.toList()));
}
int result = inventoryDeliveryMapper.deleteInventoryDeliveryById(id);
projectOrderInfoService.refreshOrderStockingStatus(inventoryDelivery.getOrderCode());
return result;
} else {
// 2026-03-19新逻辑仅修改发货单状态为撤回不删除库存表
InventoryDelivery updateDelivery = new InventoryDelivery();
@ -425,6 +510,10 @@ public class InventoryDeliveryServiceImpl implements IInventoryDeliveryService {
updateDelivery.setDeliveryStatus(InventoryDelivery.DeliveryStatusEnum.RECALL_DELIVERY.getCode());
updateDelivery.setUpdateTime(DateUtils.getNowDate());
int rows = inventoryDeliveryMapper.updateInventoryDelivery(updateDelivery);
if (CollUtil.isNotEmpty(inventoryInfos)) {
inventoryInfoMapper.clearOrderCodeByIds(inventoryInfos.stream().map(InventoryInfo::getId).collect(Collectors.toList()));
}
projectOrderInfoService.refreshOrderStockingStatus(inventoryDelivery.getOrderCode());
return rows;
}
}

View File

@ -144,6 +144,36 @@ public class OmsInventoryInnerServiceImpl implements IOmsInventoryInnerService {
}
omsInventoryInner.setWarehouseId(warehouseIdList.get(0));
if (omsInventoryInner.getItemId() != null) {
List<OmsPurchaseOrderItem> purchaseOrderItems = purchaseOrderService.listByItemId(omsInventoryInner.getItemId());
if (CollUtil.isNotEmpty(purchaseOrderItems)) {
OmsPurchaseOrderItem purchaseOrderItem = purchaseOrderItems.stream()
.filter(item -> item.getId().equals(omsInventoryInner.getItemId()))
.findFirst()
.orElse(null);
if (purchaseOrderItem != null) {
int purchaseQuantity = purchaseOrderItem.getQuantity() != null ? purchaseOrderItem.getQuantity().intValue() : 0;
int alreadyInnerQuantity = purchaseOrderItem.getInnerQuantity() != null ? purchaseOrderItem.getInnerQuantity().intValue() : 0;
int remainingQuantity = purchaseQuantity - alreadyInnerQuantity;
int currentInnerQuantity = omsInventoryInner.getQuantity() != null ? omsInventoryInner.getQuantity().intValue() : inventoryInfoList.size();
if (currentInnerQuantity > remainingQuantity) {
throw new ServiceException(StrUtil.format("入库数量[{}]超过采购单剩余可入库数量[{}]", currentInnerQuantity, remainingQuantity));
}
BigDecimal purchasePrice = purchaseOrderItem.getPrice();
if (purchasePrice != null) {
for (InventoryInfo inventoryInfo : inventoryInfoList) {
if (inventoryInfo.getInnerPrice() != null && inventoryInfo.getInnerPrice().compareTo(purchasePrice) != 0) {
throw new ServiceException(StrUtil.format("入库价[{}]必须等于采购单单价[{}]", inventoryInfo.getInnerPrice(), purchasePrice));
}
}
}
}
}
}
OmsPayableBill payableBill = new OmsPayableBill();
VendorInfo vendorInfo = vendorInfoService.selectVendorInfoByVendorCode(vendorCode);
if ((vendorInfo != null && VendorInfo.PayTypeEnum.INNER_PAY.getCode().equals(vendorInfo.getPayType())) || !Arrays.asList("1", "2").contains(omsInventoryInner.getProductType())) {

View File

@ -160,7 +160,13 @@ public class OmsPurchaseOrderServiceImpl implements IOmsPurchaseOrderService, To
omsPurchaseOrder.setOwnerName(ShiroUtils.getSysUser().getUserName());
}
omsPurchaseOrder.setCreateTime(DateUtils.getNowDate());
omsPurchaseOrder.setPurchaseNo(generatePurchaseNo(omsPurchaseOrder.getVendorId()));
String purchaseNo = generatePurchaseNo(omsPurchaseOrder.getVendorId(), Boolean.TRUE.equals(omsPurchaseOrder.getIsVirtual()));
if (Boolean.TRUE.equals(omsPurchaseOrder.getIsVirtual())) {
omsPurchaseOrder.setApproveStatus(ApproveStatusEnum.APPROVE_COMPLETE.getCode());
omsPurchaseOrder.setConfirmStatus(OmsPurchaseOrder.ConfirmStatusEnum.CONFIRM.getCode());
omsPurchaseOrder.setFlowType(OmsPurchaseOrder.FlowTypeEnum.ONLINE.getCode());
}
omsPurchaseOrder.setPurchaseNo(purchaseNo);
// 设置初始版本号为 1
omsPurchaseOrder.setVersion(1);
int rows = omsPurchaseOrderMapper.insertOmsPurchaseOrder(omsPurchaseOrder);
@ -172,10 +178,11 @@ public class OmsPurchaseOrderServiceImpl implements IOmsPurchaseOrderService, To
*
*
* @param vendorId ID
* @param isVirtual
* @return
*/
private String generatePurchaseNo(Long vendorId) {
private String generatePurchaseNo(Long vendorId, boolean isVirtual) {
VendorInfo vendorInfo = vendorInfoService.selectVendorInfoByVendorId(vendorId);
if (vendorInfo==null || StringUtils.isEmpty(vendorInfo.getProvince())) {
throw new ServiceException("制造商所属省为空,无法生成订单编号");
@ -191,6 +198,9 @@ public class OmsPurchaseOrderServiceImpl implements IOmsPurchaseOrderService, To
}
String shortCode = cnareas.get(0).getShortCode();
StringBuilder result = new StringBuilder();
if (isVirtual) {
result.append("XN-");
}
result.append("ZGCV-").append(currentDate).append(shortCode);
// 查询当天已有的订单数量,用于生成顺序编码
int count = omsPurchaseOrderMapper.selectMaxOrderCode(result.toString());
@ -222,6 +232,11 @@ public class OmsPurchaseOrderServiceImpl implements IOmsPurchaseOrderService, To
omsPurchaseOrder.setUpdateTime(DateUtils.getNowDate());
// 版本号 +1
omsPurchaseOrder.setVersion(oldOrder.getVersion() + 1);
if (Boolean.TRUE.equals(omsPurchaseOrder.getIsVirtual())) {
omsPurchaseOrder.setApproveStatus(ApproveStatusEnum.APPROVE_COMPLETE.getCode());
omsPurchaseOrder.setConfirmStatus(OmsPurchaseOrder.ConfirmStatusEnum.CONFIRM.getCode());
omsPurchaseOrder.setFlowType(OmsPurchaseOrder.FlowTypeEnum.ONLINE.getCode());
}
omsPurchaseOrderMapper.deleteOmsPurchaseOrderItemByPurchaseId(omsPurchaseOrder.getId());
insertOmsPurchaseOrderItem(omsPurchaseOrder);
return omsPurchaseOrderMapper.updateOmsPurchaseOrder(omsPurchaseOrder);
@ -419,6 +434,11 @@ public class OmsPurchaseOrderServiceImpl implements IOmsPurchaseOrderService, To
return omsPurchaseOrder;
}
@Override
public List<OmsPurchaseOrderItem> listByItemId(Long itemId) {
return omsPurchaseOrderMapper.listByItemId(itemId);
}
/**
*
@ -497,17 +517,27 @@ public class OmsPurchaseOrderServiceImpl implements IOmsPurchaseOrderService, To
if (omsPurchaseOrder == null) {
throw new ServiceException("采购订单不存在");
}
// 只有审批状态为“已通过”(2) 且 供应商确认状态为空或 null 时才能撤回
if (ApproveStatusEnum.APPROVE_COMPLETE.getCode().equals(omsPurchaseOrder.getApproveStatus()) &&
(StringUtils.isEmpty(omsPurchaseOrder.getConfirmStatus()) || "".equals(omsPurchaseOrder.getConfirmStatus())|| OmsPurchaseOrder.ConfirmStatusEnum.REJECT.getCode().equals(omsPurchaseOrder.getConfirmStatus()))) {
// 只有审批状态为"已通过"(2) 时才能撤回,虚拟采购单不校验供应商确认状态
boolean isVirtual = Boolean.TRUE.equals(omsPurchaseOrder.getIsVirtual());
boolean canRecall = false;
if (ApproveStatusEnum.APPROVE_COMPLETE.getCode().equals(omsPurchaseOrder.getApproveStatus())) {
if (isVirtual) {
canRecall = true;
} else {
canRecall = StringUtils.isEmpty(omsPurchaseOrder.getConfirmStatus()) ||
"".equals(omsPurchaseOrder.getConfirmStatus()) ||
OmsPurchaseOrder.ConfirmStatusEnum.REJECT.getCode().equals(omsPurchaseOrder.getConfirmStatus());
}
}
if (canRecall) {
// 保存历史记录
saveOrderHistory(omsPurchaseOrder);
omsPurchaseOrder.setApproveStatus(ApproveStatusEnum.WAIT_COMMIT.getCode()); // 设置为待审批(草稿)
omsPurchaseOrder.setApproveTime(null); // 清空审批时间
omsPurchaseOrder.setConfirmStatus(null); // 清空审批时间
omsPurchaseOrder.setApproveStatus(ApproveStatusEnum.WAIT_COMMIT.getCode());
omsPurchaseOrder.setApproveTime(null);
omsPurchaseOrder.setConfirmStatus(null);
omsPurchaseOrder.setUpdateTime(DateUtils.getNowDate());
omsPurchaseOrder.setVersion(omsPurchaseOrder.getVersion() + 1); // 版本号 +1
omsPurchaseOrder.setVersion(omsPurchaseOrder.getVersion() + 1);
return omsPurchaseOrderMapper.recallPurchaseOrder(omsPurchaseOrder);
} else {
throw new ServiceException("当前订单状态不允许撤回");

View File

@ -2879,17 +2879,7 @@ public class ProjectOrderInfoServiceImpl implements IProjectOrderInfoService, To
@Override
public List<OrderProductMatchDto> selectProductMatchList(String orderCode) {
List<OrderProductMatchDto> orderProductMatchDtos = projectOrderInfoMapper.selectProductMatchList(orderCode);
List<OrderProductMatchBindDto> orderProductMatchBindDtos = projectOrderInfoMapper.selectProductMatchBindList(orderCode, null);
Map<String, Integer> kyNumMap = orderProductMatchBindDtos.stream().filter(
item -> !item.getStatus().equals("0"))
.collect(Collectors.toMap(OrderProductMatchBindDto::getProductCode, item -> item.getKyNum() != null ? item.getKyNum() : 0, Integer::sum));
for (OrderProductMatchDto orderProductMatchDto : orderProductMatchDtos) {
orderProductMatchDto.setPhNum(orderProductMatchDto.getPhNum()+
(kyNumMap.get(orderProductMatchDto.getProductCode())==null?0:kyNumMap.get(orderProductMatchDto.getProductCode())));
}
return orderProductMatchDtos;
return projectOrderInfoMapper.selectProductMatchList(orderCode);
}
@Override
@ -2944,21 +2934,6 @@ public class ProjectOrderInfoServiceImpl implements IProjectOrderInfoService, To
if (CollUtil.isNotEmpty(params.getProductSnList())) {
result = inventoryInfoMapper.updateOrderCodeByProductSnList(params.getProductSnList(), params.getOrderCode());
}
//根据实际绑定的sn码修改订单与采购单的绑定数
if (params.getOrderId() != null && params.getPurchaseId() != null && StringUtils.isNotEmpty(params.getPurchaseNo())) {
int actualBindNum = inventoryInfoMapper.countByOrderCodeAndPurchaseNo(params.getOrderCode(), params.getPurchaseNo());
OmsPurchaseOrderMap map = omsPurchaseOrderMapMapper.selectByOrderIdAndPurchaseId(params.getOrderId(), params.getPurchaseId());
if (map == null) {
map = new OmsPurchaseOrderMap();
map.setOrderId(params.getOrderId());
map.setPurchaseId(params.getPurchaseId());
map.setBindNum(actualBindNum);
omsPurchaseOrderMapMapper.insertOmsPurchaseOrderMap(map);
} else {
map.setBindNum(actualBindNum);
omsPurchaseOrderMapMapper.updateOmsPurchaseOrderMap(map);
}
}
//更新订单的备货状态
this.refreshOrderStockingStatus(params.getOrderCode());
return result;
@ -2969,7 +2944,8 @@ public class ProjectOrderInfoServiceImpl implements IProjectOrderInfoService, To
*
* @param orderCode
*/
private void refreshOrderStockingStatus(String orderCode) {
@Override
public void refreshOrderStockingStatus(String orderCode) {
if (StringUtils.isEmpty(orderCode)) {
return;
}

View File

@ -32,7 +32,18 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</sql>
<select id="selectInventoryInfoList" parameterType="InventoryInfo" resultMap="InventoryInfoResult">
<include refid="selectInventoryInfoVo"/>
select t1.id, t1.product_code, t1.product_sn, t1.license_key, t1.inventory_status, t1.inner_code, t1.outer_code, t1.order_code, t1.warehouse_id, t1.inner_price,t1.tax_rate,
t1.outer_price, t1.create_by, t1.create_time, t1.update_by, t1.update_time ,
t2.warehouse_name,t3.description as 'product_desc',t3.model,t3.type as product_type
from oms_inventory_info t1
left join oms_warehouse_info t2 on t1.warehouse_id = t2.id
left join product_info t3 on t1.product_code = t3.product_code
<if test="orderCode != null and orderCode != ''">
left join oms_inventory_inner t4 on t1.inner_code = t4.inner_code
left join oms_purchase_order t5 on t4.purchase_no = t5.purchase_no
left join oms_purchase_order_map t6 on t5.id = t6.purchase_id
left join project_order_info t7 on t6.order_id = t7.id and t7.order_code = #{orderCode}
</if>
<where>
<if test="productCode != null and productCode != ''">and t1.product_code = #{productCode}</if>
<if test="productCodeList != null and productCodeList != ''">and t1.product_code in
@ -51,7 +62,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</if>
<if test="innerCode != null and innerCode != ''">and t1.inner_code = #{innerCode}</if>
<if test="outerCode != null and outerCode != ''">and t1.outer_code = #{outerCode}</if>
<if test="orderCode != null and orderCode != ''">and t1.order_code = #{orderCode}</if>
<if test="orderCode != null and orderCode != ''">
and t6.bind_num > 0
</if>
<if test="warehouseId != null ">and t1.warehouse_id = #{warehouseId}</if>
<if test="innerPrice != null ">and t1.inner_price = #{innerPrice}</if>
<if test="outerPrice != null ">and t1.outer_price = #{outerPrice}</if>
@ -166,10 +179,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
</insert>
<insert id="saveBatch">
insert into oms_inventory_info (product_code, product_sn, license_key, inventory_status, inner_code, outer_code
, warehouse_id, inner_price, outer_price, create_by, create_time, update_by, update_time,payable_bill_code,tax_rate) values
, warehouse_id, inner_price, outer_price, create_by, create_time, update_by, update_time,payable_bill_code,tax_rate,order_code) values
<foreach item="item" index="index" collection="list" separator=",">
(#{item.productCode}, #{item.productSn}, #{item.licenseKey}, #{item.inventoryStatus}, #{item.innerCode}, #{item.outerCode}
, #{item.warehouseId}, #{item.innerPrice}, #{item.outerPrice}, #{item.createBy}, #{item.createTime}, #{item.updateBy}, #{item.updateTime},#{item.payableBillCode},#{item.taxRate})
, #{item.warehouseId}, #{item.innerPrice}, #{item.outerPrice}, #{item.createBy}, #{item.createTime}, #{item.updateBy}, #{item.updateTime},#{item.payableBillCode},#{item.taxRate},#{item.orderCode})
</foreach>
ON DUPLICATE KEY UPDATE
license_key = VALUES(license_key),
@ -177,7 +190,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
outer_code = VALUES(outer_code),
outer_price = VALUES(outer_price),
update_by = VALUES(update_by),
update_time = now()
update_time = now(),
order_code = VALUES(order_code)
</insert>
<update id="updateInventoryInfo" parameterType="InventoryInfo">
@ -241,6 +255,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
and t2.purchase_no = #{purchaseNo}
</select>
<update id="clearOrderCodeByIds">
update oms_inventory_info
set order_code = null,
update_time = now()
where id in
<foreach item="id" collection="idList" separator="," open="(" close=")">
#{id}
</foreach>
</update>
<delete id="deleteInventoryInfoById" parameterType="Long">
delete from oms_inventory_info where id = #{id}
</delete>

View File

@ -29,6 +29,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<result property="approveTime" column="approve_time" />
<result property="approveNode" column="approve_node" />
<result property="confirmStatus" column="confirm_status" />
<result property="isVirtual" column="is_virtual" />
<result property="delFlag" column="del_flag" />
<result property="version" column="version" />
<result property="createBy" column="create_by" />
@ -69,6 +70,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="approveTime != null">approve_time,</if>
<if test="approveNode != null">approve_node,</if>
<if test="confirmStatus != null">confirm_status,</if>
<if test="isVirtual != null">is_virtual,</if>
<if test="delFlag != null">del_flag,</if>
<if test="version != null">version,</if>
<if test="createBy != null">create_by,</if>
@ -103,6 +105,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="approveTime != null">#{approveTime},</if>
<if test="approveNode != null">#{approveNode},</if>
<if test="confirmStatus != null">#{confirmStatus},</if>
<if test="isVirtual != null">#{isVirtual},</if>
<if test="delFlag != null">#{delFlag},</if>
<if test="version != null">#{version},</if>
<if test="createBy != null">#{createBy},</if>

View File

@ -48,7 +48,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<sql id="selectOmsPurchaseOrderVo">
select t1.id, t1.purchase_no, t1.buyer_name, t1.buyer_address, t1.vendor_id, t1.currency, t1.purchaser_id, t1.purchaser_name
, t1.purchaser_mobile, t1.purchaser_email, t1.warehouse_id, t1.pay_method, t1.owner_id, t1.owner_name, t1.remark, t1.total_amount
, t1.status, t1.approve_status, t1.approve_time, t1.approve_node, t1.confirm_status, t1.purchase_date, t1.create_time, t1.update_time, t1.del_flag,t1.version,t1.flow_type
, t1.status, t1.approve_status, t1.approve_time, t1.approve_node, t1.confirm_status, t1.is_virtual, t1.purchase_date, t1.create_time, t1.update_time, t1.del_flag,t1.version,t1.flow_type
, t1.file_id
from oms_purchase_order t1
</sql>
@ -71,7 +71,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<sql id="selectOmsPurchaseOrderRelationVo">
select t1.id, t1.purchase_no, t1.buyer_name, t1.buyer_address, t1.vendor_id, t1.currency, t1.purchaser_id, t1.purchaser_name
, t1.purchaser_mobile, t1.purchaser_email, t1.warehouse_id, t1.pay_method, t1.owner_id, t1.owner_name, t1.remark, t1.total_amount,t1.flow_type
, t1.status, t1.approve_status, t1.approve_time, t1.approve_node, t1.confirm_status, t1.purchase_date, t1.create_time, t1.update_time, t1.del_flag,t1.version
, t1.status, t1.approve_status, t1.approve_time, t1.approve_node, t1.confirm_status, t1.is_virtual, t1.purchase_date, t1.create_time, t1.update_time, t1.del_flag,t1.version
, t1.file_id
,t2.vendor_address,t2.vendor_name,t2.vendor_user,t2.vendor_code,t2.vendor_phone,t2.vendor_email,t3.warehouse_name,t1.remark,
(select max(tax_rate) from oms_purchase_order_item as it where it.purchase_id = t1.id) as tax_rate
@ -301,6 +301,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="approveTime != null">approve_time,</if>
<if test="approveNode != null">approve_node,</if>
<if test="confirmStatus != null">confirm_status,</if>
<if test="isVirtual != null">is_virtual,</if>
<if test="purchaseDate != null">purchase_date,</if>
<if test="createTime != null">create_time,</if>
<if test="updateTime != null">update_time,</if>
@ -330,6 +331,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="approveTime != null">#{approveTime},</if>
<if test="approveNode != null">#{approveNode},</if>
<if test="confirmStatus != null">#{confirmStatus},</if>
<if test="isVirtual != null">#{isVirtual},</if>
<if test="purchaseDate != null">#{purchaseDate},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateTime != null">#{updateTime},</if>
@ -363,6 +365,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
<if test="approveTime != null">approve_time = #{approveTime},</if>
<if test="approveNode != null">approve_node = #{approveNode},</if>
<if test="confirmStatus != null">confirm_status = #{confirmStatus},</if>
<if test="isVirtual != null">is_virtual = #{isVirtual},</if>
<if test="purchaseDate != null">purchase_date = #{purchaseDate},</if>
<if test="createTime != null">create_time = #{createTime},</if>
<if test="updateTime != null">update_time = #{updateTime},</if>

View File

@ -931,25 +931,29 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
select
t3.product_code as product_code,
t3.model,
t3.type as product_type,
t7.vendor_code,
t7.vendor_name,
t1.quantity as order_num,
sum(ifnull(t5.bind_num, 0)) as ph_num,
ifnull(t6.bh_num,0) as bh_num,ifnull(t6.sum_cost,0) as sum_cost
ifnull(t6.bh_num,0) as bh_num,
sum(ifnull(t5.bind_num, 0) * t4.price) as sum_cost
from project_product_info as t1
inner join project_order_info as t2
on t1.project_id = t2.project_id
inner join product_info as t3
on t1.product_bom_code = t3.product_code
left join oms_purchase_order_item as t4
on t3.product_code = t4.product_code
left join oms_purchase_order_map as t5
on t2.id = t5.order_id and t4.purchase_id = t5.purchase_id
left join (select count(1) bh_num, sum(inner_price) sum_cost, product_code
from oms_inventory_info
where order_code = #{orderCode}
group by product_code) t6 on t3.product_code = t6.product_code
inner join project_order_info as t2 on t1.project_id = t2.project_id
inner join product_info as t3 on t1.product_bom_code = t3.product_code
left join oms_purchase_order_item as t4 on t3.product_code = t4.product_code
left join oms_purchase_order_map as t5 on t2.id = t5.order_id and t4.purchase_id = t5.purchase_id
left join (
select count(1) bh_num, product_code
from oms_inventory_info
where order_code = #{orderCode}
group by product_code
) t6 on t3.product_code = t6.product_code
left join oms_vendor_info as t7 on t3.vendor_code = t7.vendor_code
where t2.order_code = #{orderCode}
and t3.type in (1, 2)
group by t3.product_code, t3.model, t1.quantity
group by t3.product_code, t3.model
order by t3.type, t3.product_code
</select>
<select id="selectProductMatchBindList" resultType="com.ruoyi.sip.dto.OrderProductMatchBindDto">
@ -959,6 +963,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
t6.id as purchase_id,
t6.purchase_no,
(select vendor_address from oms_vendor_info as tt where tt.vendor_id = t6.vendor_id) as vendor_address,
max(t4.price) as price,
t4.quantity as cg_num,
t4.quantity - ifnull((select sum(bind_num) from oms_purchase_order_map as tt where tt.purchase_id = t4.purchase_id), 0) as ky_num,
t5.bind_num as ph_num,
@ -1024,7 +1029,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
on t1.inner_code = t2.inner_code
left join oms_warehouse_info as t3
on t2.warehouse_id = t3.id
where t2.purchase_no = #{purchaseNo} and t1.inventory_status=0
where t2.purchase_no = #{purchaseNo}
order by t1.product_sn
</select>