|
|
@@ -419,6 +419,31 @@
|
|
|
import tableColumnsMixin from '@/mixins/tableColumnsMixin';
|
|
|
import { uniqById } from '@/utils/util.js';
|
|
|
|
|
|
+ // 采购退货报错提示的物品描述,便于仓储人员定位到具体物品、仓库与批次。
|
|
|
+ function describeReturnProduct(product) {
|
|
|
+ const item = product || {};
|
|
|
+ const code = item.categoryCode || item.productCode || item.productId || '';
|
|
|
+ const name = item.categoryName || item.productName || '';
|
|
|
+ const title = [code, name].filter(Boolean).join(' ') || '未指定物品';
|
|
|
+ const warehouse = item.warehouseName || item.warehouseId || '未指定仓库';
|
|
|
+ const batch = item.batchNo || '未指定批次';
|
|
|
+ return title + '(仓库:' + warehouse + ',批次:' + batch + ')';
|
|
|
+ }
|
|
|
+
|
|
|
+ // 出库明细自身的描述:提示里用明细里的物品/仓库/批次,避免拿同单其它退货物品张冠李戴。
|
|
|
+ function describeOutInDetail(detail) {
|
|
|
+ const item = detail || {};
|
|
|
+ const code = item.categoryCode || item.productCode || item.categoryId || '';
|
|
|
+ const name = item.categoryName || item.productName || '';
|
|
|
+ const title = [code, name].filter(Boolean).join(' ') || '未指定物品';
|
|
|
+ const warehouse = item.warehouseName || item.warehouseId || '未指定仓库';
|
|
|
+ const batch = item.batchNo || '未指定批次';
|
|
|
+ const systemBatch = item.systemBatchNo || '未指定系统批次';
|
|
|
+ return (
|
|
|
+ title + '(仓库:' + warehouse + ',批次:' + batch + ',系统批次:' + systemBatch + ')'
|
|
|
+ );
|
|
|
+ }
|
|
|
+
|
|
|
export default {
|
|
|
mixins: [tableColumnsMixin],
|
|
|
components: {
|
|
|
@@ -1379,8 +1404,35 @@
|
|
|
if (!needed) break;
|
|
|
}
|
|
|
if (needed > 0) {
|
|
|
+ const matched = rows.filter((row) =>
|
|
|
+ this.$refs.assetsDialogRef.matchesReturnProduct(
|
|
|
+ product,
|
|
|
+ row
|
|
|
+ )
|
|
|
+ );
|
|
|
+ const desc = describeReturnProduct(product);
|
|
|
+ if (!matched.length) {
|
|
|
+ throw new Error(
|
|
|
+ `采购退货物品 ${desc} 未找到可出库的来源批次:需要 ${needed}。请核对退货单的仓库、业务批次、系统批次是否与该物品现有库存一致。`
|
|
|
+ );
|
|
|
+ }
|
|
|
+ const availableSum = matched.reduce(
|
|
|
+ (sum, row) =>
|
|
|
+ sum +
|
|
|
+ Math.max(
|
|
|
+ 0,
|
|
|
+ Number(row.availableCountBase || 0) -
|
|
|
+ Number(row.lockQuantity || 0)
|
|
|
+ ),
|
|
|
+ 0
|
|
|
+ );
|
|
|
+ if (availableSum <= 0) {
|
|
|
+ throw new Error(
|
|
|
+ `采购退货物品 ${desc} 的可用库存不足:需要 ${needed},该物品在当前仓库/批次的候选库存已耗尽(可分配 0)。若实物已退回,请先做「归还入库」或「不良品隔离调拨」并关联原采购收货来源,或联系仓管核对该批次库存。`
|
|
|
+ );
|
|
|
+ }
|
|
|
throw new Error(
|
|
|
- `采购退货物品 ${product.productId} 的来源批次库存不足`
|
|
|
+ `采购退货物品 ${desc} 的可用库存不足:需要 ${needed},当前可分配 0(候选批次合计可用 ${availableSum} 已被本单其它退货物品优先占用)。请调小本行退货数量,或拆分成多张退货单后重试。`
|
|
|
);
|
|
|
}
|
|
|
}
|
|
|
@@ -1404,7 +1456,9 @@
|
|
|
!matchingProducts.length ||
|
|
|
new Set(matchingProducts.map((item) => String(item.receiveProductId))).size !== 1
|
|
|
) {
|
|
|
- throw new Error('采购退货来源批次不一致');
|
|
|
+ throw new Error(
|
|
|
+ `采购退货来源批次不一致:${describeOutInDetail(detail)};出库明细 ${detail.id},请刷新后重试或联系仓管核对。`
|
|
|
+ );
|
|
|
}
|
|
|
// 历史入库明细缺少来源 ID 时,仍按收货单和唯一退货产品回填来源。
|
|
|
detail.sourceReceiveProductId = matchingProducts[0].receiveProductId;
|
|
|
@@ -1413,7 +1467,35 @@
|
|
|
);
|
|
|
}
|
|
|
if (!details?.length || requested.some((item) => item.num !== 0)) {
|
|
|
- throw new Error('采购退货来源批次可出库数量不足');
|
|
|
+ const pending = requested.filter((item) => item.num !== 0);
|
|
|
+ const pendingProduct = pending.length
|
|
|
+ ? returnProducts.find((item) =>
|
|
|
+ this.$refs.assetsDialogRef.matchesReturnProduct(
|
|
|
+ item,
|
|
|
+ pending[0]
|
|
|
+ )
|
|
|
+ )
|
|
|
+ : null;
|
|
|
+ const pendingDesc = pending.length
|
|
|
+ ? `:${
|
|
|
+ pendingProduct
|
|
|
+ ? describeReturnProduct(pendingProduct)
|
|
|
+ : describeOutInDetail(pending[0])
|
|
|
+ }`
|
|
|
+ : '';
|
|
|
+ const pendingNum = Number(
|
|
|
+ pending
|
|
|
+ .reduce((sum, item) => sum + Number(item.num || 0), 0)
|
|
|
+ .toFixed(4)
|
|
|
+ );
|
|
|
+ const pendingDetail = pending.length
|
|
|
+ ? pending
|
|
|
+ .map((item) => `出库明细 ${item.id} 剩余 ${item.num}`)
|
|
|
+ .join(';')
|
|
|
+ : '候选批次未返回可出库明细';
|
|
|
+ throw new Error(
|
|
|
+ `采购退货来源批次可出库数量不足${pendingDesc};仍有 ${pendingNum} 未分配(${pendingDetail}),请刷新候选批次后重试。`
|
|
|
+ );
|
|
|
}
|
|
|
if (!this.productList.length) this.detailData(details, '2');
|
|
|
} catch (error) {
|