|
|
@@ -0,0 +1,334 @@
|
|
|
+<!-- 处置入库单 入库执行弹窗 -->
|
|
|
+<template>
|
|
|
+ <el-dialog
|
|
|
+ title="处置入库执行"
|
|
|
+ :visible.sync="visible"
|
|
|
+ width="1100px"
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ append-to-body
|
|
|
+ @closed="handleClosed"
|
|
|
+ >
|
|
|
+ <div v-loading="loading">
|
|
|
+ <el-descriptions :column="3" border size="small" class="doc-info">
|
|
|
+ <el-descriptions-item label="单号">{{ row.code }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="申请类型">{{ disposalTypeText }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="入库仓库">{{ row.warehouseName }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="物品编码">{{ row.categoryCode }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="物品名称">{{ row.categoryName }}</el-descriptions-item>
|
|
|
+ <el-descriptions-item label="规格型号">
|
|
|
+ {{ row.specification }} / {{ row.modelType }}
|
|
|
+ </el-descriptions-item>
|
|
|
+ </el-descriptions>
|
|
|
+
|
|
|
+ <el-alert
|
|
|
+ class="batch-tip"
|
|
|
+ type="info"
|
|
|
+ show-icon
|
|
|
+ :closable="false"
|
|
|
+ title="批次号已按处置来源批次预填,入库前请核对;批次号为必填项,修改后将以填写的批次号写入库存台账。"
|
|
|
+ />
|
|
|
+
|
|
|
+ <el-table :data="detailList" border size="small" class="detail-table">
|
|
|
+ <el-table-column type="index" label="序号" width="55" align="center" />
|
|
|
+ <el-table-column prop="categoryName" label="物品名称" min-width="120" show-overflow-tooltip />
|
|
|
+ <el-table-column label="批次号" width="180">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <el-input
|
|
|
+ v-model="scope.row.batchNo"
|
|
|
+ size="mini"
|
|
|
+ placeholder="请输入批次号"
|
|
|
+ maxlength="50"
|
|
|
+ />
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="systemBatchNo" label="系统批次号" width="130" show-overflow-tooltip />
|
|
|
+ <el-table-column label="包装数量" width="90" align="center">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{ scope.row.packingCount }} {{ scope.row.packingUnit }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="计量数量" width="110" align="center">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{ scope.row.quantity }} {{ scope.row.measuringUnit }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="重量" width="110" align="center">
|
|
|
+ <template slot-scope="scope">
|
|
|
+ {{ scope.row.packingWeight }} {{ scope.row.weightUnit }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="warehouseName" label="入库仓库" width="110" show-overflow-tooltip />
|
|
|
+ <el-table-column label="库位" width="230" required>
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <div class="house-cell" @click="chooseHouse(scope.$index)">
|
|
|
+ <span v-if="scope.row.house">{{ scope.row.house.position }}</span>
|
|
|
+ <span v-else class="house-required">---(必填)</span>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+
|
|
|
+ <el-form :model="form" label-width="80px" size="small" class="remark-form">
|
|
|
+ <el-form-item label="备注:">
|
|
|
+ <el-input
|
|
|
+ v-model="form.remark"
|
|
|
+ type="textarea"
|
|
|
+ :rows="2"
|
|
|
+ maxlength="200"
|
|
|
+ show-word-limit
|
|
|
+ placeholder="请输入入库备注"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <wareHouseDialog ref="wareHouseDialogRef" @confirm="onHouseConfirm" />
|
|
|
+
|
|
|
+ <div slot="footer">
|
|
|
+ <el-button @click="visible = false">取消</el-button>
|
|
|
+ <el-button type="primary" :loading="isLoading" @click="submit">确认入库</el-button>
|
|
|
+ </div>
|
|
|
+ </el-dialog>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+ import storageApi from '@/api/warehouseManagement';
|
|
|
+ import { getDisposalStorageById, getInStorageBySourceBizNo, ledgerCallback } from '@/api/disposalStorage';
|
|
|
+ import { disposalTypeLabel } from '@/utils/dict/disposalStorage';
|
|
|
+ import wareHouseDialog from './wareHouseDialog.vue';
|
|
|
+
|
|
|
+ export default {
|
|
|
+ name: 'disposalStorageExecution',
|
|
|
+ components: { wareHouseDialog },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ visible: false,
|
|
|
+ loading: false,
|
|
|
+ isLoading: false,
|
|
|
+ row: {},
|
|
|
+ detailList: [],
|
|
|
+ form: {
|
|
|
+ remark: ''
|
|
|
+ }
|
|
|
+ };
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ disposalTypeText() {
|
|
|
+ return disposalTypeLabel(this.row.disposalType);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ /**
|
|
|
+ * 打开入库弹窗
|
|
|
+ * @param {Object} row 列表行数据
|
|
|
+ */
|
|
|
+ async open(row) {
|
|
|
+ this.row = row || {};
|
|
|
+ this.detailList = (this.row.detailList || []).map((item) => ({ ...item, house: null }));
|
|
|
+ this.form.remark = '';
|
|
|
+ this.visible = true;
|
|
|
+ this.loading = true;
|
|
|
+ try {
|
|
|
+ const detail = await getDisposalStorageById(this.row.id);
|
|
|
+ if (detail) {
|
|
|
+ this.row = { ...this.row, ...detail };
|
|
|
+ this.detailList = (detail.detailList || []).map((item) => ({ ...item, house: null }));
|
|
|
+ }
|
|
|
+ } catch (e) {
|
|
|
+ this.$message.error(e.message || '获取处置入库单明细失败');
|
|
|
+ } finally {
|
|
|
+ this.loading = false;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ /**
|
|
|
+ * 组装库存入库报文
|
|
|
+ */
|
|
|
+ buildPayload() {
|
|
|
+ const detailList = this.detailList.map((item) => {
|
|
|
+ const batchNo = (item.batchNo || '').trim();
|
|
|
+ const measureQuantity = Number(item.quantity || 0);
|
|
|
+ const packingQuantity = Number(item.packingCount || 0);
|
|
|
+ const weight = Number(item.packingWeight || 0);
|
|
|
+ // 库位在入库弹窗中手工选择,优先取所选库位对应的仓库
|
|
|
+ const house = item.house || {};
|
|
|
+ const warehouseId = house.warehouseId || item.warehouseId || this.row.warehouseId;
|
|
|
+ const warehouseName = house.warehouseName || item.warehouseName || this.row.warehouseName;
|
|
|
+ const record = {
|
|
|
+ id: 0,
|
|
|
+ outInId: 0,
|
|
|
+ outInDetailId: 0,
|
|
|
+ outType: 1,
|
|
|
+ categoryId: item.categoryId,
|
|
|
+ batchNo,
|
|
|
+ systemBatchNo: item.systemBatchNo,
|
|
|
+ packingQuantity,
|
|
|
+ packingUnit: item.packingUnit,
|
|
|
+ measureQuantity,
|
|
|
+ measureUnit: item.measuringUnit,
|
|
|
+ weight,
|
|
|
+ weightUnit: item.weightUnit,
|
|
|
+ warehouseId,
|
|
|
+ warehouseName,
|
|
|
+ // 库区/货架/货位,缺失会导致入库台账无库位
|
|
|
+ areaId: house.areaId,
|
|
|
+ areaName: house.areaName,
|
|
|
+ goodsShelfId: house.shelfId,
|
|
|
+ goodsShelfName: house.shelfCode,
|
|
|
+ goodsAllocationId: house.cargoSpaceId,
|
|
|
+ goodsAllocationName: house.cargoSpaceCode,
|
|
|
+ supplierCode: item.supplierCode,
|
|
|
+ result: 2,
|
|
|
+ status: 2
|
|
|
+ };
|
|
|
+ return {
|
|
|
+ id: 0,
|
|
|
+ outInId: 0,
|
|
|
+ outType: 1,
|
|
|
+ categoryId: item.categoryId,
|
|
|
+ categoryCode: item.categoryCode,
|
|
|
+ categoryName: item.categoryName,
|
|
|
+ categoryModel: item.modelType || item.specification || '-',
|
|
|
+ batchNo,
|
|
|
+ systemBatchNo: item.systemBatchNo,
|
|
|
+ minPackingQuantity: packingQuantity,
|
|
|
+ packingQuantity,
|
|
|
+ packingUnit: item.packingUnit,
|
|
|
+ measureQuantity,
|
|
|
+ measureUnit: item.measuringUnit,
|
|
|
+ singleWeight: packingQuantity > 0 ? weight / packingQuantity : weight,
|
|
|
+ weight,
|
|
|
+ weightUnit: item.weightUnit,
|
|
|
+ warehouseId,
|
|
|
+ warehouseName,
|
|
|
+ supplierId: item.supplierId,
|
|
|
+ supplierCode: item.supplierCode,
|
|
|
+ supplierName: item.supplierName,
|
|
|
+ qualityControl: 2,
|
|
|
+ result: 2,
|
|
|
+ outInDetailRecordRequestList: [record]
|
|
|
+ };
|
|
|
+ });
|
|
|
+ const warehouseIds = [];
|
|
|
+ detailList.forEach((item) => {
|
|
|
+ if (item.warehouseId && warehouseIds.indexOf(item.warehouseId) === -1) {
|
|
|
+ warehouseIds.push(item.warehouseId);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return {
|
|
|
+ type: 1,
|
|
|
+ // 生产入库
|
|
|
+ bizType: 1,
|
|
|
+ sourceBizNo: this.row.code,
|
|
|
+ // 处置入库单已在生产入库申请审批流中审批通过,跳过仓储内部审批直接增加库存
|
|
|
+ isSkip: 1,
|
|
|
+ remark: this.form.remark,
|
|
|
+ extInfo: { assetType: this.row.rootCategoryLevelId },
|
|
|
+ warehouseIds,
|
|
|
+ outInDetailList: detailList
|
|
|
+ };
|
|
|
+ },
|
|
|
+ /** 选择库位 */
|
|
|
+ chooseHouse(index) {
|
|
|
+ this.$refs.wareHouseDialogRef.open(index, this.detailList[index]);
|
|
|
+ },
|
|
|
+ /** 库位选择回填 */
|
|
|
+ onHouseConfirm(house, index) {
|
|
|
+ const row = this.detailList[index];
|
|
|
+ this.$set(row, 'house', house);
|
|
|
+ // 仓库以所选库位为准,同步回表格避免展示与实际入库仓库不一致
|
|
|
+ this.$set(row, 'warehouseId', house.warehouseId);
|
|
|
+ this.$set(row, 'warehouseName', house.warehouseName);
|
|
|
+ },
|
|
|
+ /** 入库校验 */
|
|
|
+ validate() {
|
|
|
+ if (!this.detailList || this.detailList.length === 0) {
|
|
|
+ this.$message.error('该单据没有可入库的明细!');
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ for (let i = 0; i < this.detailList.length; i++) {
|
|
|
+ const item = this.detailList[i];
|
|
|
+ if (!(item.batchNo || '').trim()) {
|
|
|
+ this.$message.error(`第 ${i + 1} 行明细的批次号不能为空!`);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (!(Number(item.quantity || 0) > 0)) {
|
|
|
+ this.$message.error(`第 ${i + 1} 行明细的计量数量必须大于 0!`);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (!(item.warehouseId || this.row.warehouseId)) {
|
|
|
+ this.$message.error(`第 ${i + 1} 行明细未指定入库仓库!`);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ if (!item.house || !item.house.cargoSpaceId) {
|
|
|
+ this.$message.error(`第 ${i + 1} 行明细未选择库位!`);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true;
|
|
|
+ },
|
|
|
+ /** 提交入库 */
|
|
|
+ async submit() {
|
|
|
+ if (!this.validate()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ this.isLoading = true;
|
|
|
+ try {
|
|
|
+ // 入库前复核单据状态:已入库的不再重复入库,只补做台账回写,避免重复增加库存
|
|
|
+ const latest = await getDisposalStorageById(this.row.id);
|
|
|
+ let outInId = latest ? latest.outInId : this.row.outInId;
|
|
|
+ const stored = latest ? latest.status === 1 : this.row.status === 1;
|
|
|
+ if (!stored) {
|
|
|
+ // 1. 写入库存台账,返回实际入库单ID;同一业务单号重复提交时库存侧幂等,可能返回空
|
|
|
+ const inStorageResult = await storageApi.storage(this.buildPayload());
|
|
|
+ const outInIds = inStorageResult ? inStorageResult.data : null;
|
|
|
+ if (Array.isArray(outInIds) && outInIds.length > 0) {
|
|
|
+ outInId = outInIds[0];
|
|
|
+ }
|
|
|
+ // 库存侧已存在同来源单的入库记录时补齐入库单ID,避免本次入库被幂等拦截后无法回写台账
|
|
|
+ if (!outInId) {
|
|
|
+ const existed = await getInStorageBySourceBizNo(this.row.code);
|
|
|
+ outInId = existed ? existed.id : undefined;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 2. 更新单据状态为已入库并回写处置台账(报废品/试销品),服务端会校验实际入库单号
|
|
|
+ await ledgerCallback(this.row.id, outInId);
|
|
|
+ this.$message.success('入库成功!');
|
|
|
+ this.visible = false;
|
|
|
+ this.$emit('success');
|
|
|
+ } catch (e) {
|
|
|
+ this.$message.error(e.message || '入库失败');
|
|
|
+ } finally {
|
|
|
+ this.isLoading = false;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handleClosed() {
|
|
|
+ this.form.remark = '';
|
|
|
+ }
|
|
|
+ }
|
|
|
+ };
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+ .doc-info {
|
|
|
+ margin-bottom: 12px;
|
|
|
+ }
|
|
|
+ .batch-tip {
|
|
|
+ margin-bottom: 12px;
|
|
|
+ }
|
|
|
+ .detail-table {
|
|
|
+ margin-bottom: 12px;
|
|
|
+ }
|
|
|
+ .remark-form {
|
|
|
+ margin-bottom: 0;
|
|
|
+ }
|
|
|
+ .house-cell {
|
|
|
+ cursor: pointer;
|
|
|
+ color: #606266;
|
|
|
+ &:hover {
|
|
|
+ color: #409eff;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .house-required {
|
|
|
+ color: #ff4949;
|
|
|
+ }
|
|
|
+</style>
|