|
|
@@ -180,6 +180,7 @@
|
|
|
<el-table-column prop="batchNo" label="业务批次" />
|
|
|
<el-table-column prop="warehouseName" label="仓库" />
|
|
|
<el-table-column prop="categoryCode" label="物品编码" />
|
|
|
+ <el-table-column prop="packingSpecification" label="包装规格" />
|
|
|
<el-table-column prop="stockCountBase" label="计量数量" />
|
|
|
<el-table-column prop="packingQuantity" label="包装数量" />
|
|
|
<el-table-column prop="weight" label="重量" />
|
|
|
@@ -199,14 +200,16 @@
|
|
|
<el-table-column label="计量数量">
|
|
|
<template slot-scope="{ row }">
|
|
|
<el-input
|
|
|
+ v-if="!hasPacking && !isAutoMeasureTarget(row.target)"
|
|
|
v-model="row.target.measureQuantity"
|
|
|
maxlength="19"
|
|
|
:disabled="Boolean(pendingApproveId)"
|
|
|
size="mini"
|
|
|
placeholder="请输入"
|
|
|
- @input="markMeasureQuantityManual(row.target)"
|
|
|
+ @input="handleMeasureQuantityInput(row.target)"
|
|
|
@blur="validateTargetLimit(row.target, 'measureQuantity')"
|
|
|
/>
|
|
|
+ <span v-else>{{ row.measureQuantity }}</span>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
<el-table-column label="包装数量">
|
|
|
@@ -225,6 +228,7 @@
|
|
|
<el-table-column label="重量">
|
|
|
<template slot-scope="{ row }">
|
|
|
<el-input
|
|
|
+ v-if="!hasPacking"
|
|
|
v-model="row.target.weight"
|
|
|
maxlength="19"
|
|
|
:disabled="Boolean(pendingApproveId)"
|
|
|
@@ -232,6 +236,7 @@
|
|
|
placeholder="请输入"
|
|
|
@blur="validateTargetLimit(row.target, 'weight')"
|
|
|
/>
|
|
|
+ <span v-else>{{ row.weight }}</span>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
<el-table-column prop="lockQuantity" label="锁库数量" />
|
|
|
@@ -284,12 +289,6 @@
|
|
|
@click="assignSelectedPackingTarget"
|
|
|
>批量分配</el-button
|
|
|
>
|
|
|
- <el-button
|
|
|
- size="mini"
|
|
|
- :disabled="Boolean(pendingApproveId)"
|
|
|
- @click="assignAllPackingTargets"
|
|
|
- >一键分配</el-button
|
|
|
- >
|
|
|
<el-button
|
|
|
size="mini"
|
|
|
:disabled="Boolean(pendingApproveId)"
|
|
|
@@ -901,7 +900,6 @@
|
|
|
return {
|
|
|
targetDetailId,
|
|
|
measureQuantity: '0',
|
|
|
- measureQuantityManual: false,
|
|
|
packageQuantity: '0',
|
|
|
weight: '0'
|
|
|
};
|
|
|
@@ -974,122 +972,35 @@
|
|
|
handlePackingSelectionChange(rows) {
|
|
|
this.selectedPackingRows = rows;
|
|
|
},
|
|
|
- packingRowSelectable() {
|
|
|
- if (this.pendingApproveId || !this.batchPackingTargetId) return false;
|
|
|
- const target = this.targets.find(
|
|
|
- (item) => item.targetDetailId === this.batchPackingTargetId
|
|
|
+ packingRowSelectable(row) {
|
|
|
+ return (
|
|
|
+ !this.pendingApproveId &&
|
|
|
+ Boolean(this.batchPackingTargetId) &&
|
|
|
+ !row.targetDetailId
|
|
|
+ );
|
|
|
+ },
|
|
|
+ isAutoMeasureTarget(target) {
|
|
|
+ return (
|
|
|
+ this.mode === 'split' &&
|
|
|
+ this.targets.length === 2 &&
|
|
|
+ target.targetDetailId === this.targets[1].targetDetailId
|
|
|
);
|
|
|
- return Boolean(target && !isEmptyDecimal(target.measureQuantity));
|
|
|
},
|
|
|
/** 将勾选的整包装一次分配到同一目标子批次。 */
|
|
|
assignSelectedPackingTarget() {
|
|
|
if (!this.batchPackingTargetId) {
|
|
|
return this.$message.warning('请选择目标子批次');
|
|
|
}
|
|
|
- const target = this.targets.find(
|
|
|
- (item) => item.targetDetailId === this.batchPackingTargetId
|
|
|
- );
|
|
|
- if (!target || isEmptyDecimal(target.measureQuantity)) {
|
|
|
- return this.$message.warning('请先填写目标子批次的计量数量');
|
|
|
- }
|
|
|
- const assignedMeasureQuantity = this.packingRows
|
|
|
- .filter((row) => row.targetDetailId === target.targetDetailId)
|
|
|
- .reduce(
|
|
|
- (sum, row) => sum.plus(decimal(row.measureQuantity)),
|
|
|
- new BigNumber(0)
|
|
|
- );
|
|
|
- const remainingMeasureQuantity = decimal(target.measureQuantity).minus(
|
|
|
- assignedMeasureQuantity
|
|
|
- );
|
|
|
- if (remainingMeasureQuantity.lte(0)) {
|
|
|
- return this.$message.warning('该目标子批次已按计量数量分配整包装');
|
|
|
+ if (!this.selectedPackingRows.length) {
|
|
|
+ return this.$message.warning('请先选择整包装');
|
|
|
}
|
|
|
- const selectedRows = [];
|
|
|
- let selectedMeasureQuantity = new BigNumber(0);
|
|
|
- this.packingRows
|
|
|
- .filter((row) => !row.targetDetailId)
|
|
|
- .some((packing) => {
|
|
|
- const packingMeasureQuantity = decimal(packing.measureQuantity);
|
|
|
- if (
|
|
|
- selectedMeasureQuantity
|
|
|
- .plus(packingMeasureQuantity)
|
|
|
- .gt(remainingMeasureQuantity)
|
|
|
- ) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- selectedRows.push(packing);
|
|
|
- selectedMeasureQuantity = selectedMeasureQuantity.plus(
|
|
|
- packingMeasureQuantity
|
|
|
- );
|
|
|
- return selectedMeasureQuantity.eq(remainingMeasureQuantity);
|
|
|
- });
|
|
|
- if (!selectedRows.length) {
|
|
|
- return this.$message.warning('没有可按该计量数量分配的整包装');
|
|
|
- }
|
|
|
- selectedRows.forEach((packing) => {
|
|
|
+ this.selectedPackingRows.forEach((packing) => {
|
|
|
packing.targetDetailId = this.batchPackingTargetId;
|
|
|
this.assignPackingTarget(packing, this.batchPackingTargetId, false);
|
|
|
});
|
|
|
this.syncTargetPackingMinimums();
|
|
|
- this.selectedPackingRows = selectedRows;
|
|
|
- this.$refs.packingTable.clearSelection();
|
|
|
- this.$nextTick(() => {
|
|
|
- selectedRows.forEach((packing) => {
|
|
|
- this.$refs.packingTable.toggleRowSelection(packing, true);
|
|
|
- });
|
|
|
- });
|
|
|
- },
|
|
|
- /** 按所有目标子批次的计量数量依次匹配整包装。 */
|
|
|
- assignAllPackingTargets() {
|
|
|
- if (this.pendingApproveId) return;
|
|
|
- const availableRows = this.packingRows.filter(
|
|
|
- (row) => !row.targetDetailId
|
|
|
- );
|
|
|
- const assignments = [];
|
|
|
- for (const target of this.targets) {
|
|
|
- const assignedMeasureQuantity = this.packingRows
|
|
|
- .filter((row) => row.targetDetailId === target.targetDetailId)
|
|
|
- .reduce(
|
|
|
- (sum, row) => sum.plus(decimal(row.measureQuantity)),
|
|
|
- new BigNumber(0)
|
|
|
- );
|
|
|
- const remainingMeasureQuantity = decimal(
|
|
|
- target.measureQuantity
|
|
|
- ).minus(assignedMeasureQuantity);
|
|
|
- if (remainingMeasureQuantity.lte(0)) continue;
|
|
|
-
|
|
|
- const selectedRows = this.findPackingCombination(
|
|
|
- availableRows,
|
|
|
- remainingMeasureQuantity
|
|
|
- );
|
|
|
- if (!selectedRows.length) {
|
|
|
- return this.$message.warning(
|
|
|
- `${this.targetLabel(
|
|
|
- target.targetDetailId
|
|
|
- )}没有可按计量数量分配的整包装`
|
|
|
- );
|
|
|
- }
|
|
|
- assignments.push({
|
|
|
- target,
|
|
|
- selectedRows
|
|
|
- });
|
|
|
- selectedRows.forEach((packing) => {
|
|
|
- availableRows.splice(availableRows.indexOf(packing), 1);
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- assignments.forEach(({ target, selectedRows }) => {
|
|
|
- selectedRows.forEach((packing) => {
|
|
|
- packing.targetDetailId = target.targetDetailId;
|
|
|
- this.assignPackingTarget(packing, target.targetDetailId, false);
|
|
|
- });
|
|
|
- });
|
|
|
- this.syncTargetPackingMinimums();
|
|
|
this.selectedPackingRows = [];
|
|
|
- if (this.$refs.packingTable) this.$refs.packingTable.clearSelection();
|
|
|
- if (assignments.some(({ selectedRows }) => selectedRows.length)) {
|
|
|
- this.$message.success('已按所有目标计量数量自动分配整包装');
|
|
|
- }
|
|
|
+ this.$refs.packingTable.clearSelection();
|
|
|
},
|
|
|
/** 清除所有包装和锁定单的目标分配,保留目标数量输入。 */
|
|
|
clearPackingTargets() {
|
|
|
@@ -1102,24 +1013,15 @@
|
|
|
lock.targetConflict = false;
|
|
|
});
|
|
|
this.selectedPackingRows = [];
|
|
|
+ this.targets.forEach((target) => {
|
|
|
+ if (this.hasPacking) {
|
|
|
+ target.measureQuantity = '0';
|
|
|
+ target.weight = '0';
|
|
|
+ }
|
|
|
+ });
|
|
|
if (this.$refs.packingTable) this.$refs.packingTable.clearSelection();
|
|
|
this.$message.success('已清空全部分配');
|
|
|
},
|
|
|
- findPackingCombination(rows, targetQuantity) {
|
|
|
- const selected = [];
|
|
|
- const search = (start, quantity) => {
|
|
|
- if (quantity.eq(0)) return true;
|
|
|
- for (let index = start; index < rows.length; index += 1) {
|
|
|
- const packingQuantity = decimal(rows[index].measureQuantity);
|
|
|
- if (packingQuantity.gt(quantity)) continue;
|
|
|
- selected.push(rows[index]);
|
|
|
- if (search(index + 1, quantity.minus(packingQuantity))) return true;
|
|
|
- selected.pop();
|
|
|
- }
|
|
|
- return false;
|
|
|
- };
|
|
|
- return search(0, targetQuantity) ? selected.slice() : [];
|
|
|
- },
|
|
|
/** 包装分配变化时,同步其关联锁定单的全部明细和包装。 */
|
|
|
assignPackingTarget(packing, targetDetailId, shouldSync = true) {
|
|
|
const relatedLocks = this.lockRows.filter((lock) =>
|
|
|
@@ -1175,28 +1077,45 @@
|
|
|
}
|
|
|
});
|
|
|
},
|
|
|
- /** 包装变更后同步目标下限,超出包装汇总的部分作为散量。 */
|
|
|
+ /** 包装分配后由最后一个目标承接来源剩余计量和重量。 */
|
|
|
syncTargetPackingMinimums() {
|
|
|
+ if (!this.hasPacking) return;
|
|
|
+ const source = this.sources[0];
|
|
|
this.targets.forEach((target) => {
|
|
|
const packages = this.packingRows.filter(
|
|
|
(row) => row.targetDetailId === target.targetDetailId
|
|
|
);
|
|
|
const measureMinimum = decimal(this.sum(packages, 'measureQuantity'));
|
|
|
const weightMinimum = decimal(this.sum(packages, 'weight'));
|
|
|
- if (
|
|
|
- packages.length &&
|
|
|
- (!target.measureQuantityManual ||
|
|
|
- decimal(target.measureQuantity).lt(measureMinimum))
|
|
|
- ) {
|
|
|
- target.measureQuantity = measureMinimum.toFixed(6);
|
|
|
- }
|
|
|
- if (decimal(target.weight).lt(weightMinimum)) {
|
|
|
- target.weight = weightMinimum.toFixed(6);
|
|
|
- }
|
|
|
+ target.measureQuantity = measureMinimum.toFixed(6);
|
|
|
+ target.weight = weightMinimum.toFixed(6);
|
|
|
});
|
|
|
+ if (this.mode === 'split') {
|
|
|
+ const lastTarget = this.targets[this.targets.length - 1];
|
|
|
+ const assigned = this.targets.slice(0, -1);
|
|
|
+ lastTarget.measureQuantity = decimal(source.stockCountBase)
|
|
|
+ .minus(this.sum(assigned, 'measureQuantity'))
|
|
|
+ .max(0)
|
|
|
+ .toFixed(6);
|
|
|
+ lastTarget.weight = decimal(source.weight)
|
|
|
+ .minus(this.sum(assigned, 'weight'))
|
|
|
+ .max(0)
|
|
|
+ .toFixed(6);
|
|
|
+ }
|
|
|
},
|
|
|
- markMeasureQuantityManual(target) {
|
|
|
- target.measureQuantityManual = true;
|
|
|
+ handleMeasureQuantityInput(target) {
|
|
|
+ if (
|
|
|
+ this.mode !== 'split' ||
|
|
|
+ this.targets.length !== 2 ||
|
|
|
+ this.isAutoMeasureTarget(target) ||
|
|
|
+ !this.sources.length
|
|
|
+ ) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ const remainder = decimal(this.sources[0].stockCountBase).minus(
|
|
|
+ decimal(target.measureQuantity)
|
|
|
+ );
|
|
|
+ this.targets[1].measureQuantity = remainder.max(0).toFixed(6);
|
|
|
},
|
|
|
validateTargetLimit(target, field) {
|
|
|
if (this.mode !== 'split' || !this.sources.length) return;
|
|
|
@@ -1465,6 +1384,23 @@
|
|
|
)
|
|
|
return '目标计量数量和重量不能小于所选整包装汇总';
|
|
|
if (this.mode === 'split') {
|
|
|
+ const packedMeasureQuantity = decimal(
|
|
|
+ this.sum(this.packingRows, 'measureQuantity')
|
|
|
+ );
|
|
|
+ const looseMeasureQuantity = decimal(source.stockCountBase).minus(
|
|
|
+ packedMeasureQuantity
|
|
|
+ );
|
|
|
+ if (this.hasPacking && looseMeasureQuantity.gt(0)) {
|
|
|
+ const looseTargetCount = this.targetRows.filter((row) =>
|
|
|
+ decimal(row.measureQuantity).gt(decimal(row.packingMeasureQuantity))
|
|
|
+ ).length;
|
|
|
+ if (looseTargetCount > 1) {
|
|
|
+ return '包装外散量只能分配到一个目标批次';
|
|
|
+ }
|
|
|
+ if (Number(source.isUnpack) !== 1) {
|
|
|
+ return '来源库存存在包装外散量,物品不允许拆包';
|
|
|
+ }
|
|
|
+ }
|
|
|
if (
|
|
|
!decimal(this.sum(this.targetRows, 'measureQuantity')).eq(
|
|
|
decimal(source.stockCountBase)
|