Quellcode durchsuchen

Merge branch 'dev' into test

mlchen vor 6 Tagen
Ursprung
Commit
91dfc42b52

+ 187 - 97
src/BIZComponents/paymentCollectionPlan/Index.vue

@@ -11,24 +11,28 @@
       <!-- 表头工具栏 -->
       <template v-slot:toolbar>
         <div class="headbox">
-            <div>
-              <el-button
-                size="small"
-                type="primary"
-                icon="el-icon-plus"
-                class="ele-btn-icon"
-                @click="handlAdd"
-                v-if="showAddBtn"
-              >
-                新增
-              </el-button>
-            </div>
-
-            <div class="pricebox">
-                <span class="amount">比例合计:{{ allRatio }}%</span>
-                <span class="amount">计划{{ menu === 'purchase' ? '付款' : '收款' }}金额合计:{{ allPrice }}元</span>
-
-                <!-- <el-form-item
+          <div>
+            <el-button
+              size="small"
+              type="primary"
+              icon="el-icon-plus"
+              class="ele-btn-icon"
+              @click="handlAdd"
+              v-if="showAddBtn"
+            >
+              新增
+            </el-button>
+          </div>
+
+          <div class="pricebox">
+            <span class="amount">比例合计:{{ allRatio }}%</span>
+            <span class="amount"
+              >计划{{ menu === 'purchase' ? '付款' : '收款' }}金额合计:{{
+                allPrice
+              }}元</span
+            >
+
+            <!-- <el-form-item
                     style="width: 300px"
                     label="优惠后总金额:"
                     prop="discountTotalPrice"
@@ -51,13 +55,13 @@
                     <template slot="append">元</template>
                     </el-input>
                 </el-form-item> -->
-            </div>
+          </div>
         </div>
       </template>
 
       <template v-slot:headerIssueNumber="{ column }">
         <span class="is-required">{{ column.label }}</span>
-      </template> 
+      </template>
 
       <template v-slot:headerType="{ column }">
         <span class="is-required">{{ column.label }}</span>
@@ -80,8 +84,7 @@
           <el-input
             v-model="scope.row.moneyName"
             placeholder="请输入"
-            :disabled="type=='view'"
-
+            :disabled="type == 'view'"
           ></el-input>
         </el-form-item>
       </template>
@@ -102,7 +105,7 @@
           <el-input
             v-model="scope.row.ratio"
             placeholder="请输入"
-            :disabled="type=='view'"
+            :disabled="type == 'view'"
             @input="(val) => ratioInput(val, scope.$index)"
           >
             <template slot="append">%</template>
@@ -123,7 +126,7 @@
             v-model="scope.row.type"
             placeholder="请选择"
             style="width: 100%"
-            :disabled="type=='view'"
+            :disabled="type == 'view'"
             @change="(val) => typeChange(val, scope.$index)"
           >
             <el-option
@@ -136,6 +139,29 @@
           </el-select>
         </el-form-item>
       </template>
+      <template v-slot:subType="scope">
+        <el-form-item
+          v-if="scope.row.type == 1"
+          style="margin-bottom: 20px"
+          :prop="'datasource.' + scope.$index + '.subType'"
+        >
+          <el-select
+            v-model="scope.row.subType"
+            placeholder="请选择款项子类型"
+            style="width: 100%"
+            :disabled="type == 'view'"
+            @change="(val) => subTypeChange(val, scope.$index)"
+          >
+            <el-option
+              v-for="item in subTypeOp"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            >
+            </el-option>
+          </el-select>
+        </el-form-item>
+      </template>
       <template v-slot:headerRatio="{ column }">
         <span class="is-required">{{ column.label }}</span>
       </template>
@@ -154,16 +180,16 @@
             trigger: 'change'
           }"
         >
-            <el-input
-                type="number"
-                :min="0"
-                disabled
-                v-model="scope.row.price"
-                style="width: 100%"
-                placeholder="请输入"
-            >
-                <template slot="append">元</template>
-            </el-input>
+          <el-input
+            type="number"
+            :min="0"
+            disabled
+            v-model="scope.row.price"
+            style="width: 100%"
+            placeholder="请输入"
+          >
+            <template slot="append">元</template>
+          </el-input>
         </el-form-item>
       </template>
       <template v-slot:deadLine="scope">
@@ -198,7 +224,7 @@
             v-model="scope.row.remark"
             type="textarea"
             placeholder="请输入"
-            :disabled="type=='view'"
+            :disabled="type == 'view'"
           ></el-input>
         </el-form-item>
       </template>
@@ -273,7 +299,9 @@
       showAddBtn() {
         // console.log('showAddBtn~~~~', this.type, this.info);
         // console.log('showAddBtn!!!!',!['1', '2'].includes(this.info.settlementMode) || this.type != 'view');
-        return !(['1', '2'].includes(this.info?.settlementMode)) && this.type != 'view' 
+        return (
+          !['1', '2'].includes(this.info?.settlementMode) && this.type != 'view'
+        );
       },
       columns() {
         return [
@@ -300,12 +328,21 @@
             headerSlot: 'headerType',
             align: 'center'
           },
+          {
+            width: 150,
+            prop: 'subType',
+            label: '款项子类型',
+            slot: 'subType',
+            align: 'center',
+            // 没有任何一行的 type 为预付款(value=1)时,整列隐藏
+            show: this.showSubTypeColumn
+          },
           {
             prop: 'moneyName',
             label: '款项名称',
             slot: 'moneyName',
             // headerSlot: 'headerMoneyName',
-            align: 'center',
+            align: 'center'
             // width: 170
           },
           {
@@ -321,7 +358,7 @@
             prop: 'price',
             label: this.menu === 'purchase' ? '计划付款金额' : '计划收款金额',
             slot: 'price',
-            align: 'center',
+            align: 'center'
             // headerSlot: 'headerPrice',
           },
 
@@ -352,12 +389,30 @@
         ];
       },
       allRatio() {
-        return formatPrice(this.form.datasource.reduce((acc, cur) => acc + Number(cur.ratio), 0));
+        return formatPrice(
+          this.form.datasource.reduce((acc, cur) => acc + Number(cur.ratio), 0)
+        );
       },
       allPrice() {
         // 使用分进行计算,避免浮点数精度问题
-        return formatPrice(this.form.datasource.reduce((acc, cur) => acc + Math.round(Number(cur.price) * 100), 0) / 100);
+        return formatPrice(
+          this.form.datasource.reduce(
+            (acc, cur) => acc + Math.round(Number(cur.price) * 100),
+            0
+          ) / 100
+        );
       },
+      // 任何一行款项类型是『预付款』(value=1) 时才显示子类型列
+      showSubTypeColumn() {
+        return this.form.datasource.some((item) => item.type == 1);
+      },
+      subTypeOp() {
+        return [
+          { value: 'PREPAY', label: '预付款' },
+          { value: 'ADVANCE', label: '预收款' },
+          { value: 'DEPOSIT', label: '定金' }
+        ];
+      }
     },
     watch: {
       discountAmount(newval) {
@@ -391,7 +446,11 @@
       typeChange(val, index = null) {
         // console.log(val, index, '55555');
         if (val) {
-          this.$set(this.form.datasource[index], 'typeName', this.paymentTypeOp.find(item => item.value === val).label);
+          this.$set(
+            this.form.datasource[index],
+            'typeName',
+            this.paymentTypeOp.find((item) => item.value === val).label
+          );
         }
       },
       //输入比例更新金额
@@ -453,11 +512,11 @@
         if (this.isLoadingFromApi) {
           return;
         }
-        
+
         // 创建一个新的数组,避免直接修改原数组
         let newData = JSON.parse(JSON.stringify(this.form.datasource));
         console.log('newData after copy:', newData);
-        
+
         // 先根据比例计算所有价格
         for (let i = 0; i < newData.length; i++) {
           const r = newData[i];
@@ -472,29 +531,56 @@
             };
           }
         }
-        
+
         // 验证并调整最后一行价格,确保总和等于优惠后的总金额
         // 使用分进行计算,避免浮点数精度问题
-        const allPriceSum = newData.reduce((acc, cur) => acc + Math.round(Number(cur.price || 0) * 100), 0) / 100;
+        const allPriceSum =
+          newData.reduce(
+            (acc, cur) => acc + Math.round(Number(cur.price || 0) * 100),
+            0
+          ) / 100;
         const discountAmount = Number(this.discountAmount);
         // 直接计算比例总和,避免字符串转换问题
-        const allRatio = newData.reduce((acc, cur) => acc + Number(cur.ratio || 0), 0);
-        
-        console.log('allPriceSum:', allPriceSum, 'discountAmount:', discountAmount, 'allRatio:', allRatio);
-        
+        const allRatio = newData.reduce(
+          (acc, cur) => acc + Number(cur.ratio || 0),
+          0
+        );
+
+        console.log(
+          'allPriceSum:',
+          allPriceSum,
+          'discountAmount:',
+          discountAmount,
+          'allRatio:',
+          allRatio
+        );
+
         // 检查是否所有数据都有price且比例合计为100%
-        const allHavePrice = newData.every(item => item.price != null && item.price !== '');
-        
+        const allHavePrice = newData.every(
+          (item) => item.price != null && item.price !== ''
+        );
+
         console.log('allHavePrice:', allHavePrice);
-        
-        if (allHavePrice && Math.abs(allRatio - 100) < 0.01 && Math.abs(allPriceSum - discountAmount) >= 0.01) {
+
+        if (
+          allHavePrice &&
+          Math.abs(allRatio - 100) < 0.01 &&
+          Math.abs(allPriceSum - discountAmount) >= 0.01
+        ) {
           // 调整最后一行的价格
           if (newData.length > 0) {
             const lastIndex = newData.length - 1;
             // 使用分进行计算,避免浮点数精度问题
-            const otherPriceSum = newData.slice(0, lastIndex).reduce((acc, cur) => acc + Math.round(Number(cur.price || 0) * 100), 0) / 100;
+            const otherPriceSum =
+              newData
+                .slice(0, lastIndex)
+                .reduce(
+                  (acc, cur) => acc + Math.round(Number(cur.price || 0) * 100),
+                  0
+                ) / 100;
             // 计算最后一行应该有的价格
-            const lastPrice = Math.round((discountAmount - otherPriceSum) * 100) / 100;
+            const lastPrice =
+              Math.round((discountAmount - otherPriceSum) * 100) / 100;
             newData[lastIndex] = {
               ...newData[lastIndex],
               price: formatPrice(lastPrice)
@@ -502,9 +588,9 @@
             console.log('adjusted last item price:', lastPrice);
           }
         }
-        
+
         console.log('newData before update:', newData);
-        
+
         // 确保更新this.form.datasource,触发Vue的响应式更新
         this.form.datasource = newData;
         console.log('after update, datasource:', this.form.datasource);
@@ -544,21 +630,22 @@
       },
       defaultList(method, issueNumber, dateRange) {
         // console.log('method, issueNumber', method, issueNumber, dateRange);
-        const tempList = []
-        if(dateRange) {
+        const tempList = [];
+        if (dateRange) {
           this.setDefaultList(dateRange, issueNumber);
-          return
+          return;
         }
-        if(method == 3) {
+        if (method == 3) {
           let params = ['预付款', '交货款'];
-          for(let i = 0; i < issueNumber; i++) {
+          for (let i = 0; i < issueNumber; i++) {
             let item = JSON.parse(JSON.stringify(this.defaultForm));
-              item.moneyName = i < params.length ? params[i] : '';
-              item.type = i < params.length ? this.paymentTypeOp[i].value : '';
-              item.typeName = i < params.length ? this.paymentTypeOp[i].label : '';
-              item.key = i + 1;
-              item.issueNumber = i + 1;
-              tempList.push(item);
+            item.moneyName = i < params.length ? params[i] : '';
+            item.type = i < params.length ? this.paymentTypeOp[i].value : '';
+            item.typeName =
+              i < params.length ? this.paymentTypeOp[i].label : '';
+            item.key = i + 1;
+            item.issueNumber = i + 1;
+            tempList.push(item);
           }
           // params.forEach((item, index) => {
           //     let i = JSON.parse(JSON.stringify(this.defaultForm));
@@ -577,9 +664,9 @@
           //     i.issueNumber = index + 1;
           //     tempList.push(i);
           // });
-          for(let i = 0; i < issueNumber; i++) {
+          for (let i = 0; i < issueNumber; i++) {
             let item = JSON.parse(JSON.stringify(this.defaultForm));
-            if(issueNumber == 1) {
+            if (issueNumber == 1) {
               item.moneyName = '';
               item.type = 2;
               item.typeName = '交货款';
@@ -613,26 +700,27 @@
         const startDate = new Date(date[0]);
         const endDate = new Date(date[1]);
         // 计算毫秒差并转换为天数,使用Math.ceil确保结果为整数
-        const days = Math.ceil((endDate - startDate) / (1000 * 60 * 60 * 24)) + 1;
+        const days =
+          Math.ceil((endDate - startDate) / (1000 * 60 * 60 * 24)) + 1;
         console.log('包含两头的天数:', days);
 
         // 生成包括头尾在内的所有日期
         const allDates = [];
         const current = new Date(startDate);
-        
+
         // 遍历从开始日期到结束日期的所有天数
         while (current <= endDate) {
           // 格式化日期为YYYY-MM-dd
           const year = current.getFullYear();
           const month = current.getMonth() + 1; // 月份从0开始,需要+1
           const day = current.getDate();
-          
+
           const formattedMonth = String(month).padStart(2, '0');
           const formattedDay = String(day).padStart(2, '0');
           const formattedDateStr = `${year}-${formattedMonth}-${formattedDay}`;
-          
+
           allDates.push(formattedDateStr);
-          
+
           // 移动到下一天
           current.setDate(current.getDate() + 1);
         }
@@ -644,27 +732,29 @@
         const parseMonthDate = (dateStr) => {
           return dateStr instanceof Date ? dateStr : new Date(dateStr + '-01');
         };
-        
+
         const start = parseMonthDate(date[0]);
         const end = parseMonthDate(date[1]);
-        
+
         // 先生成所有月份的日期数组
         const allMonthDates = [];
         const currentDate = new Date(start);
-        
+
         // 遍历从开始月份到结束月份的所有月份
         while (currentDate <= end) {
           const year = currentDate.getFullYear();
           const month = currentDate.getMonth() + 1; // 月份从0开始,需要+1
           const receiptDate = day;
-          
+
           // 格式化日期为YYYY-MM-dd
           const formattedMonth = String(month).padStart(2, '0');
           const formattedDate = String(receiptDate).padStart(2, '0');
-          const deadLine = receiptDate ? `${year}-${formattedMonth}-${formattedDate}` : '';
-          
+          const deadLine = receiptDate
+            ? `${year}-${formattedMonth}-${formattedDate}`
+            : '';
+
           allMonthDates.push(deadLine);
-          
+
           // 使用Date对象的setMonth方法正确移动到下一个月(自动处理年份变化)
           currentDate.setMonth(currentDate.getMonth() + 1);
         }
@@ -677,13 +767,13 @@
         // 计算基本比例
         const basicRatio = 100 / tempPeriod;
         let totalRatio = 0;
-        
+
         // 保存原有数据
         const existingData = [...this.form.datasource];
-        
+
         // 生成付款计划列表项
         const tempList = [];
-        
+
         // 根据传入的period参数生成付款计划
         for (let i = 0; i < tempPeriod; i++) {
           let item = JSON.parse(JSON.stringify(this.defaultForm));
@@ -694,7 +784,7 @@
           // } else {
           //   item = JSON.parse(JSON.stringify(this.defaultForm));
           // }
-          
+
           // 获取日期:如果i小于日期数组长度则使用对应日期,否则设为空
           const deadLine = i < dateRange.length ? dateRange[i] : '';
           const ratio = parseFloat(basicRatio.toFixed(2));
@@ -706,7 +796,7 @@
           tempList.push(item);
           // totalRatio += ratio;
         }
-        
+
         // 调整最后一项的比例,确保总和为100
         // if (tempList.length > 0) {
         //   const difference = 100 - totalRatio;
@@ -714,7 +804,7 @@
         //     tempList[tempList.length - 1].ratio = parseFloat((tempList[tempList.length - 1].ratio + difference).toFixed(2));
         //   }
         // }
-        
+
         console.log('付款计划列表:~~', tempList);
         // this.$set(this.form, 'datasource', tempList);
         console.log('付款计划列表:', this.form.datasource);
@@ -752,7 +842,7 @@
           }
           callback(valid);
         });
-      },
+      }
     }
   };
 </script>
@@ -774,12 +864,12 @@
   }
 
   .pricebox {
-      display: flex;
-      justify-content: flex-start;
-      align-items: center;
-      font-weight: bold;
-    }
-    :deep(.el-input-group__append) {
-      padding: 0;
-    }
+    display: flex;
+    justify-content: flex-start;
+    align-items: center;
+    font-weight: bold;
+  }
+  :deep(.el-input-group__append) {
+    padding: 0;
+  }
 </style>

+ 18 - 3
src/components/addDoc/file-edit.vue

@@ -209,14 +209,17 @@
     },
     methods: {
       handleClick() {
-        this.form.directoryId = '';
-        this.form.businessCodeId = '';
-        this.form.codeType = '';
         if (this.activeName == 2) {
           this.folderList = this.myFolderList;
         } else {
           this.folderList = this.allFolderList;
         }
+        // 切换 tab 时使用新 tab 的默认值(文档位置 / 编码分类 / 编码方案)联动更新
+        // 其他字段(type / 备注 / 已上传文件等)保留不动
+        if (this.folderList && this.folderList.length > 0) {
+          this.form.directoryId = this.folderList[0].id;
+          this.initFileCode();
+        }
       },
       async getTreeCode() {
         let nodeData = {};
@@ -242,10 +245,22 @@
         }
       },
       async open() {
+        // 每次重新打开都回到"文档工作区"标签
+        this.activeName = '1';
+        this.folderList = this.allFolderList;
         this.showEditFlag = true;
         this.$nextTick(async () => {
           this.list = await selectTreeList();
+          // 强制使用 文档工作区 的默认值(避免 cancel() 保留的旧 directoryId 导致默认值缺失)
+          if (this.folderList && this.folderList.length > 0) {
+            this.form.directoryId = this.folderList[0].id;
+          }
+          // 触发 initFileCode,根据选中的文档位置默认填充 编码分类 / 编码方案
           await this.initFileCode();
+          // 文档类型默认值
+          if (!this.form.type) {
+            this.form.type = '1';
+          }
           this.setTree(this.list);
         });
       },

+ 1 - 0
src/views/bpm/handleTask/components/financialManage/invoiceManage/components/addOrEditDialog.vue

@@ -535,6 +535,7 @@
       //获取详情
       async getInfo(id) {
         this.form = await invoiceApplyInfoV2API(id);
+        this.form.relatedList = [this.form.relatedVO];
         this.$nextTick(() => {
           // 根据来源类型设置数据源:99 用 tableInfo(setValue),非 99 用 tableInfoNew(putValue)
           if (this.form.sourceType == 99) {

+ 1 - 0
src/views/bpm/handleTask/components/financialManage/invoiceManage/components/detailDialog.vue

@@ -415,6 +415,7 @@
       //获取详情
       async getInfo(id) {
         this.form = await invoiceApplyInfoV2API(id);
+        this.form.relatedList = [this.form.relatedVO];
         this.form.category = Number(this.form.category) || '';
         this.$nextTick(() => {
           // 根据来源类型设置数据源:99 用 tableInfo(setValue),非 99 用 tableInfoNew(putValue)

+ 2 - 0
src/views/bpm/handleTask/components/purchaseOrder/accountstatement/page.vue

@@ -43,6 +43,8 @@
         ref="accountDetailTableRef"
         :datasource.sync="datasource"
         dialogType="view"
+        tableTitle="采购订单编码"
+        tableReceiptTitle="收货单编码"
       ></AccountDetailTable>
   </div>
 </template>

+ 1 - 0
src/views/bpm/handleTask/components/purchaseOrder/returnGoods/detailDialog.vue

@@ -171,6 +171,7 @@
     <detailDialog
       type="sourceBizNo"
       :businessId="form.returnNo"
+      :purchaseReturn="true"
       ref="outgoingDetai"
       v-if="taskDefinitionKey == 'purchaserUploadReceipt' || !taskDefinitionKey"
     ></detailDialog>

+ 13 - 3
src/views/bpm/handleTask/components/purchaseOrder/returnGoods/submit.vue

@@ -9,6 +9,7 @@
         />
       </el-form-item>
     </el-form>
+    <el-alert v-if="outInQueryError" :title="outInQueryError" type="error" :closable="false" />
     <div style="margin-left: 10%; margin-bottom: 20px; font-size: 14px">
       <el-button
         icon="el-icon-edit-outline"
@@ -18,6 +19,7 @@
         v-if="
           taskDefinitionKey != 'storemanApprove' ||
           (taskDefinitionKey == 'storemanApprove' &&
+            !outInQueryLoading && !outInQueryError &&
             outInData.verifyStatus == 2)
         "
         @click="handleAudit(1)"
@@ -32,6 +34,7 @@
         @click="storemanApprove"
         v-if="
           ['storemanApprove'].includes(taskDefinitionKey) &&
+          !outInQueryLoading && !outInQueryError &&
           ([0, 3].includes(outInData.verifyStatus) || !outInData.verifyStatus)
         "
         >申请出库
@@ -129,6 +132,8 @@
         outInData: {
           verifyStatus: ''
         },
+        outInQueryError: '',
+        outInQueryLoading: true,
         rules: {
           reason: [
             { required: true, message: '请输入审批建议', trigger: 'blur' }
@@ -138,16 +143,21 @@
     },
     async created() {
       if (this.taskDefinitionKey == 'storemanApprove') {
-        let data = await getReturnSaleOrderrecordDetail(this.businessId);
         try {
-          this.outInData = await getOutInBySourceBizNo(data.returnNo);
+          let data = await getReturnSaleOrderrecordDetail(this.businessId);
+          this.outInData = (await getOutInBySourceBizNo(data.returnNo)) || { verifyStatus: 0 };
         } catch (error) {
-          this.outInData.verifyStatus = 0;
+          this.outInQueryError = '退货出库单查询失败,请核对单据后重试';
+        } finally {
+          this.outInQueryLoading = false;
         }
       }
     },
     methods: {
       async storemanApprove() {
+        if (this.outInQueryLoading || this.outInQueryError) {
+          return this.$message.error(this.outInQueryError || '退货出库单查询中,请稍后重试');
+        }
         this.$refs.formRef.validate(async (valid) => {
           if (valid) {
             let res = await this.getTableValue();

+ 16 - 8
src/views/bpm/handleTask/components/saleOrder/accountstatement/accountDetailTable.vue

@@ -200,6 +200,14 @@
       queryDimension: {
         type: Number,
         default: 1
+      },
+      tableTitle: {
+        type: String,
+        default: ''
+      },
+      tableReceiptTitle: {
+        type: String,
+        default: ''
       }
     },
     data() {
@@ -234,14 +242,14 @@
           {
             minWidth: 140,
             prop: 'orderNo',
-            label: '销售订单编码',
+            label: this.tableTitle || '销售订单编码',
             align: 'center',
             showOverflowTooltip: true
           },
           {
             minWidth: 140,
             prop: this.queryDimension == 1 ? 'statementSubOrderCode' : 'statementSubOrderNo',
-            label: this.queryDimension == 1 ? '发货单编码' : '调拨单编码',
+            label: this.queryDimension == 1 ? this.tableReceiptTitle || '发货单编码' : '调拨单编码',
             align: 'center',
             showOverflowTooltip: true,
           },
@@ -275,12 +283,12 @@
             showOverflowTooltip: true
           },
           
-          {
-            minWidth: 110,
-            prop: 'historicalStatementCount',
-            label: '已对账数量',
-            align: 'center'
-          },
+          // {
+          //   minWidth: 110,
+          //   prop: 'historicalStatementCount',
+          //   label: '已对账数量',
+          //   align: 'center'
+          // },
           {
             minWidth: 140,
             headerSlot: 'headerRequired',

+ 171 - 16
src/views/bpm/outgoingManagement/components/AssetsDialog.vue

@@ -14,6 +14,7 @@
                 style="width: 100%"
                 @change="changeDimensionHandler"
                 v-model="dimension"
+                :disabled="bizType === 10 && !hasMixedReturnSources()"
                 placeholder="请选择"
               >
                 <!-- <el-option label="物料维度" value="4"> </el-option> -->
@@ -195,14 +196,7 @@
             width="55"
             align="center"
             fixed="left"
-            :selectable="
-              (row) => {
-                return (
-                  !this.productLists.some((it) => it.id == row.id) &&
-                  this.getAvailableQuantity(row) > 0
-                );
-              }
-            "
+            :selectable="(row) => this.canSelectBatch(row)"
           >
           </el-table-column>
           <el-table-column label="序号" type="index" width="50" fixed="left">
@@ -416,7 +410,9 @@
   export default {
     components: { AssetTree },
     props: {
-      treeIds: { type: Array, default: () => [] }
+      treeIds: { type: Array, default: () => [] },
+      returnProductList: { type: Array, default: () => [] },
+      bizType: { type: Number, default: null }
     },
     data() {
       return {
@@ -498,6 +494,48 @@
           Math.round((inventoryBalance - lockQuantity) * 100) / 100
         );
       },
+      canSelectBatch(row) {
+        if (this.bizType !== 10) {
+          return (
+            !this.productLists.some((item) => item.id == row.id) &&
+            this.getAvailableQuantity(row) > 0
+          );
+        }
+        if (this.dimension == 3) {
+          return this.returnPackages().some((source) => this.matchesReturnPackage(source, row))
+            && this.getAvailableQuantity(row) > 0
+            && !this.productLists.some((item) => (item.outInDetailRecordRequestList || [])
+              .some((record) => record.packageNo === row.packageNo));
+        }
+        const source = this.returnProductList.find((item) =>
+          !(item.packageSources || []).length && this.matchesReturnProduct(item, row));
+        return !!source && this.getAvailableQuantity(row) > 0;
+      },
+      hasMixedReturnSources() {
+        return this.returnPackages().length > 0 && this.returnProductList.some(
+          (product) => !(product.packageSources || []).length
+        );
+      },
+      returnPackages() {
+        return this.returnProductList.flatMap((product) =>
+          (product.packageSources || []).map((source) => ({ ...source, product }))
+        );
+      },
+      matchesReturnPackage(source, row) {
+        return String(source.packageNo) === String(row.packageNo)
+          && String(source.product.productId) === String(row.categoryId)
+          && String(source.product.receiveProductId) === String(row.sourceReceiveProductId)
+          && String(source.product.warehouseId) === String(row.warehouseId)
+          && String(source.product.batchNo || '') === String(row.batchNo || '')
+          && String(source.product.systemBatchNo || '') === String(row.systemBatchNo || '');
+      },
+      matchesReturnProduct(product, row) {
+        return String(product.productId) === String(row.categoryId)
+          && String(product.receiveProductId) === String(row.sourceReceiveProductId)
+          && String(product.warehouseId) === String(row.warehouseId)
+          && String(product.batchNo || '') === String(row.batchNo || '')
+          && String(product.systemBatchNo || '') === String(row.systemBatchNo || '');
+      },
       formatMeasureQuantity(row) {
         return this.dimension == 2
           ? this.getAvailableQuantity(row)
@@ -591,7 +629,18 @@
           this.total = data.count;
         } else if (this.dimension == 2) {
           // 批次维度
-          const data = await storageApi.getBatchList(this.searchForm);
+          const legacyProducts = this.returnProductList.filter((item) => !(item.packageSources || []).length);
+          const receiveProductIds = legacyProducts.map((item) => item.receiveProductId).filter(Boolean);
+          if (this.bizType === 10 && receiveProductIds.length !== legacyProducts.length) {
+            this.tableData = [];
+            this.total = 0;
+            this.$message.error('退货产品缺少采购收货明细,无法查询不良库存');
+            return;
+          }
+          const params = this.bizType === 10
+            ? { ...this.searchForm, sourceReceiveProductIds: receiveProductIds.join(','), result: 2 }
+            : this.searchForm;
+          const data = await storageApi.getBatchList(params);
           for (let i = 0; i < data.list.length; i++) {
             data.list[i].outboundNum = this.getAvailableQuantity(data.list[i]);
           }
@@ -604,11 +653,32 @@
           this.total = data.count;
         } else {
           // 包装维度
-          const data = await storageApi.getPackingList(this.searchForm);
-          for (let i = 0; i < data.list.length; i++) {
-            data.list[i].outboundNum = data.list[i].packingQuantity;
+          const packageSources = this.returnPackages();
+          const receiveProductIds = this.returnProductList.map((product) => product.receiveProductId).filter(Boolean);
+          if (this.bizType === 10 && receiveProductIds.length !== this.returnProductList.length) {
+            this.tableData = [];
+            this.total = 0;
+            this.$message.error('退货产品缺少采购收货明细,无法查询不良包装');
+            return;
           }
-          this.tableData = data.list;
+          const params = this.bizType === 10
+            ? { ...this.searchForm,
+                sourceReceiveProductIds: [...new Set(receiveProductIds)].join(','),
+                ...(packageSources.length ? {
+                  packageNos: [...new Set(packageSources.map((source) => source.packageNo))].join(',')
+                } : {}),
+                result: 2 }
+            : this.searchForm;
+          const data = await storageApi.getPackingList(params);
+          const rows = this.bizType === 10
+            ? data.list.filter((row) => packageSources.length
+              ? packageSources.some((source) => this.matchesReturnPackage(source, row))
+              : this.returnProductList.some((product) => this.matchesReturnProduct(product, row)))
+            : data.list;
+          for (let i = 0; i < rows.length; i++) {
+            rows[i].outboundNum = rows[i].measureQuantity;
+          }
+          this.tableData = rows;
           this.total = data.count;
         }
         this.updateProductOutboundNums(this.tableData);
@@ -638,6 +708,20 @@
         } else {
           for (let i = 0; i < list.length; i++) {
             const item = list[i];
+            if (this.dimension == 2 && this.bizType === 10) {
+              const returnProduct = this.returnProductList.find((product) => this.matchesReturnProduct(product, item));
+              if (returnProduct) {
+                this.$set(
+                  item,
+                  'outboundNum',
+                  Math.min(
+                    Number(returnProduct.totalCount) || 0,
+                    this.getAvailableQuantity(item)
+                  )
+                );
+                continue;
+              }
+            }
             const matchedProduct = this.productLists.find((product) => {
               const isMatch = product.id === item.id;
               return isMatch;
@@ -657,6 +741,9 @@
       },
       open(val) {
         this.productLists = val || [];
+        if (this.bizType === 10) {
+          this.dimension = this.returnPackages().length ? '3' : '2';
+        }
         this.visible = true;
         this.$nextTick(() => {
           this.$refs.treeList.getTreeData().then(async (data) => {
@@ -676,16 +763,74 @@
             return;
           }
         }
+        if (this.bizType === 10) {
+          if (!this.hasMixedReturnSources() && this.dimension != (this.returnPackages().length ? 3 : 2)) {
+            this.$message.error('采购退货库存选择维度不正确!');
+            return;
+          }
+          const invalidBatch = this.selectionList.some((item) =>
+            this.dimension == 3
+              ? !this.returnPackages().some((source) => this.matchesReturnPackage(source, item))
+              : !this.returnProductList.some((source) =>
+                  !(source.packageSources || []).length && this.matchesReturnProduct(source, item)));
+          if (invalidBatch) {
+            this.$message.error('所选批次与采购退货来源批次不一致!');
+            return;
+          }
+          const exceededReturn = this.dimension == 3
+            ? this.selectionList.some((item) => {
+                const quantity = this.returnPackages()
+                  .filter((source) => this.matchesReturnPackage(source, item))
+                  .reduce((sum, source) => sum + Number(source.measureQuantity || 0), 0);
+                return quantity <= 0 || quantity > this.getAvailableQuantity(item);
+              })
+            : this.selectionList.some((item) => {
+            const source = this.returnProductList.find((product) =>
+              !(product.packageSources || []).length && this.matchesReturnProduct(product, item));
+            return (
+              !source ||
+              Number(item.outboundNum) <= 0 ||
+              Number(item.outboundNum) > Number(source.totalCount) ||
+              Number(item.outboundNum) > this.getAvailableQuantity(item)
+            );
+          });
+          if (exceededReturn) {
+            this.$message.error('出库数量不能超过采购退货数量!');
+            return;
+          }
+        }
         console.log(this.selectionList);
         let data = null;
         if (this.dimension == 3) {
           data = await storageApi.getHierarchyList({
             ids: this.selectionList.map((item) => item.id).join(','),
-            type: this.dimension
+            type: this.dimension,
+            result: this.bizType === 10 ? 2 : null
           });
+          if (this.bizType === 10) {
+            data.forEach((detail) => {
+              detail.outInDetailRecordRequestList.forEach((record) => {
+                const sources = this.returnPackages().filter((source) =>
+                  this.matchesReturnPackage(source, { ...detail, ...record,
+                    sourceReceiveProductId: detail.sourceReceiveProductId }));
+                const quantity = sources.reduce((sum, source) => sum + Number(source.measureQuantity || 0), 0);
+                const originalQuantity = Number(record.measureQuantity);
+                record.measureQuantity = quantity;
+                record.weight = originalQuantity > 0
+                  ? Number(record.weight || 0) * quantity / originalQuantity
+                  : 0;
+              });
+              detail.measureQuantity = detail.outInDetailRecordRequestList
+                .reduce((sum, record) => sum + Number(record.measureQuantity), 0);
+              detail.weight = detail.outInDetailRecordRequestList
+                .reduce((sum, record) => sum + Number(record.weight || 0), 0);
+            });
+          }
         } else if (this.dimension == 2) {
+          const returnProducts = this.returnProductList;
           data = await storageApi.getHierarchyFifo({
             type: this.dimension,
+            result: this.bizType === 10 ? 2 : null,
             ids: this.selectionList.map((item) => item.id),
             builders: this.selectionList.map((item) => {
               return {
@@ -693,7 +838,17 @@
                 batchNo: item.batchNo,
                 // 历史数据保持空值,由后端按外部批次号兼容匹配
                 systemBatchNo: item.systemBatchNo || '',
-                num: item.outboundNum,
+                warehouseId: item.warehouseId,
+                num:
+                  this.bizType === 10
+                    ? Math.min(
+                        Number(item.outboundNum),
+                        Number(
+                          returnProducts.find((product) =>
+                            !(product.packageSources || []).length && this.matchesReturnProduct(product, item))?.totalCount || 0
+                        )
+                      )
+                    : item.outboundNum,
                 id: item.id
               };
             })

+ 20 - 3
src/views/bpm/outgoingManagement/details.vue

@@ -374,6 +374,11 @@
         type: Boolean,
         default: false
       },
+      /** 采购退货详情按来源单号汇总全部有效出库单。 */
+      purchaseReturn: {
+        type: Boolean,
+        default: false
+      },
       XTBG: {
         //协同办公进来的
         type: Boolean,
@@ -783,14 +788,26 @@
         } else if (this.isIds) {
           resAll = await storageApi.getInboundDetailsByIds(id);
         } else {
-          res = await storageApi.getInfoBySourceBizNoAll(id);
-          res = res[0] || {};
+          const sourceOrders = await storageApi.getInfoBySourceBizNoAll(id) || [];
+          if (this.purchaseReturn) {
+            resAll = sourceOrders.filter((item) =>
+              Number(item.type) === 2 &&
+              Number(item.bizType) === 10 &&
+              Number(item.verifyStatus) !== 3
+            );
+          } else {
+            res = sourceOrders[0] || {};
+          }
+        }
+        if (this.purchaseReturn && !resAll.length) {
+          this.$message.error('未找到采购退货出库单');
+          return;
         }
         console.log(
           '===============================================================',
           res
         );
-        if (this.isIds) {
+        if (this.isIds || this.purchaseReturn) {
           res = JSON.parse(JSON.stringify(resAll[0]));
           this.extInfo = resAll[0].extInfo;
           res['outInDetailList'] = [];

+ 6 - 1
src/views/bpm/outgoingManagement/outbound.vue

@@ -393,7 +393,8 @@
       ref="assetsDialogRef"
       :treeIds="formData.extInfo?.assetType"
       @detailData="detailData"
-      :productLists="this.productList"
+      :returnProductList="bizType == 10 ? form.productList || [] : []"
+      :bizType="bizType"
     />
   </div>
 </template>
@@ -1287,6 +1288,10 @@
             });
           return;
         }
+        //采购退货按收货明细选择不良库存,不使用普通物品先进先出预填。
+        if (this.bizType == 10) {
+          return;
+        }
         //采购委外
         if (this.form.productList?.length > 0) {
           this.getStaffList({ id: this.form.deptId }).then(() => {