Quellcode durchsuchen

Merge branch 'test' of http://110.41.163.243:9980/kd-aiot/kd-aiot-frontend-wt into test

yusheng vor 6 Tagen
Ursprung
Commit
a4278681a3

+ 4 - 0
src/BIZComponents/processSubmitDialog/components/applicationComponent.vue

@@ -240,6 +240,10 @@ export default {
                 {
                     value: '笔记本套装',
                     label: '笔记本套装'
+                },
+                {
+                    value: '医用透明质酸钠护理贴',
+                    label: '医用透明质酸钠护理贴'
                 }
             ],
         }

+ 13 - 0
src/api/bpm/components/purchasingManage/purchaseorderreceive.js

@@ -22,6 +22,19 @@ export async function getReceiveSaleOrderrecordDetail(id) {
   return Promise.reject(new Error(res.data.message));
 }
 
+/**
+ * 获取当前仓管审批任务可访问的收货明细
+ */
+export async function getReceiveTaskDetail(id, taskId) {
+  const res = await request.get(`/eom/purchaseorderreceive/task-detail/${id}`, {
+    params: { taskId }
+  });
+  if (res.data.code == 0) {
+    return res.data.data;
+  }
+  return Promise.reject(new Error(res.data.message));
+}
+
 /**
  * 更新信息
  */

+ 17 - 0
src/api/warehouseManagement/index.js

@@ -138,6 +138,23 @@ export default {
     }
     return Promise.reject(new Error(res.data.message));
   },
+  // 获取同一来源单下指定仓库的入库单
+  getInfoBySourceBizNoAndWarehouse: async (id, warehouseId) => {
+    const res = await request.get(
+      `/wms/outintwo/getInfoBySourceBizNoAll/${id}`
+    );
+    if (res.data.code == 0) {
+      const list = Array.isArray(res.data.data) ? res.data.data : [];
+      return (
+        list.find((item) =>
+          (item.outInDetailList || []).some(
+            (detail) => `${detail.warehouseId}` === `${warehouseId}`
+          )
+        ) || {}
+      );
+    }
+    return Promise.reject(new Error(res.data.message));
+  },
   // 质检保存
   qualityInspection: async (data) => {
     const res = await request.post(`/wms/outintwo/qualityInspection`, data);

+ 7 - 0
src/views/bpm/done/index.vue

@@ -184,6 +184,13 @@
             showOverflowTooltip: true,
             minWidth: 120
           },
+          {
+            prop: 'businessScope.warehouseName',
+            label: '审批仓库',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 120
+          },
           {
             prop: 'businessType',
             label: '单据类型',

+ 22 - 1
src/views/bpm/handleTask/components/materialPlan/detailDialog.vue

@@ -113,6 +113,11 @@
   import { getById } from '@/api/bpm/components/materialPlan/index';
   import { getFile } from '@/api/system/file';
   import timeDialog from './timeDialog';
+  import {
+    getBomPageCategoryId,
+    contactList,
+    getBomGetById
+  } from '@/api/bpm/components/bomApprover';
 
   export default {
     components: {
@@ -129,6 +134,7 @@
       return {
         visible: false,
         title: '采购配料计划',
+        gysList: [],
 
         // 表格列配置
         columns: [
@@ -287,7 +293,10 @@
             label: '供应商',
             slot: 'supplierName',
             action: 'supplierName',
-            align: 'center'
+            align: 'center',
+            formatter: (row) => {
+              return this.gysList.find((item) => item.id === row.supplierId)?.name || '-';
+            }
           },
 
           {
@@ -317,9 +326,21 @@
       };
     },
     created() {
+      this.getContactList();
       this.getDetailData(this.businessId);
     },
     methods: {
+      getContactList() {
+        let param = {
+          pageNum: 1,
+          type: 2,
+          size: -1,
+          status: 1
+        };
+        contactList(param).then((res) => {
+          this.gysList = res.list;
+        });
+      },
       downloadFile(file) {
         getFile({ objectName: file.storePath }, file.name);
       },

+ 12 - 3
src/views/bpm/handleTask/components/purchaseOrder/invoice/detailDialog.vue

@@ -61,7 +61,10 @@
 <script>
   import dictMixins from '@/mixins/dictMixins';
 
-  import { getReceiveSaleOrderrecordDetail } from '@/api/bpm/components/purchasingManage/purchaseorderreceive';
+  import {
+    getReceiveSaleOrderrecordDetail,
+    getReceiveTaskDetail
+  } from '@/api/bpm/components/purchasingManage/purchaseorderreceive';
 
   import detailDialog from '@/views/bpm/stockManagement/details.vue';
 
@@ -122,7 +125,10 @@
       await this.getDetailData(this.businessId);
       // getOutInBySourceBizNoOrError(this.form.receiveNo)
       await storageApi
-        .getInfoBySourceBizNo(this.form.receiveNo)
+        .getInfoBySourceBizNoAndWarehouse(
+          this.form.receiveNo,
+          this.form.productList[0]?.warehouseId
+        )
         .then((res) => {
           if (JSON.stringify(res) == '{}') {
             // 未查询到相关信息(未入库)
@@ -237,7 +243,10 @@
       },
       async getDetailData(id) {
         this.loading = true;
-        const data = await getReceiveSaleOrderrecordDetail(id);
+        const data =
+          this.taskDefinitionKey == 'storeManagerApprove'
+            ? await getReceiveTaskDetail(id, this.taskId)
+            : await getReceiveSaleOrderrecordDetail(id);
         this.loading = false;
         if (data) {
           console.log(data, '1111111111111111111111');

+ 5 - 3
src/views/bpm/handleTask/components/purchaseOrder/invoice/submit.vue

@@ -131,6 +131,7 @@
   import {
     UpdateReceiveInformation,
     getReceiveSaleOrderrecordDetail,
+    getReceiveTaskDetail,
     cancel
   } from '@/api/bpm/components/purchasingManage/purchaseorderreceive';
   import { approveTaskWithVariables, rejectTask,cancelTask } from '@/api/bpm/task';
@@ -187,10 +188,11 @@
         this.userOptions.push(...data);
       });
       if (this.taskDefinitionKey == 'storeManagerApprove') {
-        let data = await getReceiveSaleOrderrecordDetail(this.businessId);
+        let data = await getReceiveTaskDetail(this.businessId, this.taskId);
         try {
-          this.outInData = await storageApi.getInfoBySourceBizNo(
-            data.receiveNo
+          this.outInData = await storageApi.getInfoBySourceBizNoAndWarehouse(
+            data.receiveNo,
+            data.productList[0]?.warehouseId
           );
           console.log('outInData----------', this.outInData);
         } catch (error) {}

+ 735 - 0
src/views/bpm/handleTask/components/saleOrder/accountstatement/accountDetailTable.vue

@@ -0,0 +1,735 @@
+<template>
+  <div class="account-detail-table">
+    <el-form ref="detailForm" :model="{ flatList }">
+      <ele-pro-table
+        ref="detailTable"
+        row-key="id"
+        :needPage="false"
+        :columns="columns"
+        max-height="500px"
+        :datasource="flatList"
+        :cache-key="cacheKeyUrl"
+        class="time-form"
+      >
+        <template v-slot:toolbar>
+          <div class="detail-toolbar">
+            <el-button
+              v-if="!isView"
+              type="primary"
+              size="small"
+              @click="batchSetTaxRate"
+              >批量设置税率</el-button
+            >
+            <el-button
+              v-if="!isView"
+              type="primary"
+              size="small"
+              @click="batchSetPrice"
+              >批量设置单价</el-button
+            >
+            <el-button
+              v-if="!isView"
+              type="primary"
+              size="small"
+              @click="batchSetDiscount"
+              >批量设置折让比例</el-button
+            >
+            <!-- <div class="summary-info">
+              <span
+                >本次总对账金额:<b>{{ totalStatementAmount }}</b> 元</span
+              >
+              <span
+                >本次总对账数量:<b>{{ totalStatementCount }}</b></span
+              >
+            </div> -->
+          </div>
+        </template>
+        <template v-slot:statementCount="scope">
+          <el-form-item
+            :prop="'flatList.' + scope.$index + '.statementCount'"
+            :rules="{
+              validator: (rule, value, cb) =>
+                validateStatementCount(rule, value, cb, scope.row),
+              trigger: 'change'
+            }"
+          >
+            <el-input
+              v-model="scope.row.statementCount"
+              type="number"
+              :disabled="isView || queryDimension == 1"
+              @input="handleCountChange(scope.row, scope.$index)"
+            />
+          </el-form-item>
+        </template>
+
+        <template v-slot:technologyRouteName="scope">
+          <el-input
+            v-model="scope.row.technologyRouteName"
+            placeholder="请选择"
+            :disabled="isView"
+            @click.native="openVersion(scope.$index)"
+          />
+        </template>
+
+        <template v-slot:singlePrice="scope">
+          <el-form-item
+            :prop="'flatList.' + scope.$index + '.singlePrice'"
+            :rules="{
+              required: true,
+              message: '请输入单价',
+              trigger: 'change'
+            }"
+          >
+            <el-input
+              v-model="scope.row.singlePrice"
+              type="number"
+              :disabled="isView"
+              @input="handlePriceChange(scope.row, scope.$index)"
+            />
+            <!-- <div v-if="isPriceInvalid(scope.row)" class="price-warning">
+              单价无效,请核对商品价格
+            </div> -->
+          </el-form-item>
+        </template>
+
+        <template v-slot:taxRate="scope">
+          <el-form-item
+            :prop="'flatList.' + scope.$index + '.taxRate'"
+            :rules="{
+              required: isTaxRate == 1,
+              message: '请输入税率',
+              trigger: 'change'
+            }"
+          >
+            <el-input
+              v-model="scope.row.taxRate"
+              type="number"
+              :disabled="isView"
+              @input="handleTaxChange(scope.row, scope.$index)"
+            />
+          </el-form-item>
+        </template>
+
+        <template v-slot:discountRatio="scope">
+          <el-form-item
+            :prop="'flatList.' + scope.$index + '.discountRatio'"
+            :rules="{
+              required: true,
+              message: '请输入折让比例',
+              trigger: 'change'
+            }"
+          >
+            <el-input
+              v-model="scope.row.discountRatio"
+              type="number"
+              :min="0"
+              :max="100"
+              :disabled="isView"
+              @input="handleDiscountChange(scope.row, scope.$index)"
+            />
+          </el-form-item>
+        </template>
+
+        <template v-slot:statementAmount="scope">
+          <span>{{ scope.row.statementAmount }}</span>
+        </template>
+
+        <template v-slot:headerRequired="{ column }">
+              <span class="is-required">{{ column.label }}</span>
+            </template>
+
+        <template v-slot:action="scope">
+          <el-link
+            v-if="!isView && queryDimension == 2"
+            type="danger"
+            :underline="false"
+            icon="el-icon-delete"
+            @click="removeRow(scope.$index)"
+            >删除</el-link
+          >
+          <span v-else>--</span>
+        </template>
+      </ele-pro-table>
+    </el-form>
+
+    <!-- 批量设置弹窗 -->
+    <el-dialog
+      :visible.sync="batchDialogVisible"
+      title="批量设置"
+      width="400px"
+      append-to-body
+    >
+      <el-form label-width="100px">
+        <el-form-item :label="batchLabel">
+          <el-input v-model="batchValue" type="number" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer">
+        <el-button @click="batchDialogVisible = false">取消</el-button>
+        <el-button type="primary" @click="confirmBatchSet">确定</el-button>
+      </div>
+    </el-dialog>
+
+    <!-- 工艺路线选择弹窗 -->
+    <ProductionVersion
+      ref="versionRefs"
+      @changeProduct="changeProduct"
+    ></ProductionVersion>
+  </div>
+</template>
+
+<script>
+  import { parameterGetByCode } from '@/api/main/index.js';
+  import { pricingWayList } from '@/enum/dict.js';
+  import ProductionVersion from '@/components/ProductionVersion2/index.vue';
+
+  export default {
+    name: 'accountDetailTable',
+    components: {
+      ProductionVersion
+    },
+    props: {
+      datasource: {
+        type: Array,
+        default: () => []
+      },
+      dialogType: {
+        type: String,
+        default: 'add'
+      },
+      queryDimension: {
+        type: Number,
+        default: 1
+      }
+    },
+    data() {
+      return {
+        cacheKeyUrl: 'eom-202608171423-account-detail-table',
+        isTaxRate: 0,
+        flatList: [],
+        totalStatementAmount: 0,
+        totalStatementCount: 0,
+        batchDialogVisible: false,
+        batchType: '',
+        batchValue: '',
+        batchLabel: '',
+        columnsVersion: 1,
+      };
+    },
+    computed: {
+      isView() {
+        return this.dialogType === 'view';
+      },
+      columns() {
+        let columnsVersion = this.columnsVersion;
+        return [
+          {
+            width: 60,
+            label: '序号',
+            type: 'index',
+            columnKey: 'index',
+            align: 'center',
+            fixed: 'left'
+          },
+          {
+            minWidth: 140,
+            prop: 'orderNo',
+            label: '销售订单编码',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 140,
+            prop: 'statementSubOrderNo',
+            label: this.queryDimension == 1 ? '发货单编码' : '调拨单编码',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 120,
+            prop: 'productOperateTime',
+            label: this.queryDimension == 1 ? '发货日期' : '调拨单日期',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 120,
+            prop: 'productCode',
+            label: '产品编码',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 140,
+            prop: 'productName',
+            label: '产品名称',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 120,
+            prop: 'batchNo',
+            label: '批次号',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          
+          {
+            minWidth: 110,
+            prop: 'historicalStatementCount',
+            label: '已对账数量',
+            align: 'center'
+          },
+          {
+            minWidth: 140,
+            headerSlot: 'headerRequired',
+            prop: 'statementCount',
+            label: '本次对账数量',
+            align: 'center',
+            slot: 'statementCount'
+          },
+          {
+            minWidth: 120,
+            headerSlot: 'headerRequired',
+            prop: 'statementAmount',
+            label: '本次对账金额',
+            align: 'center',
+            slot: 'statementAmount'
+          },
+          {
+            minWidth: 120,
+            prop: 'totalCount',
+            label: this.queryDimension == 1 ? '发货数量' : '调拨数量',
+            align: 'center',
+          },
+          
+          {
+            minWidth: 140,
+            prop: 'technologyRouteName',
+            label: '工艺路线',
+            align: 'center',
+            slot: 'technologyRouteName',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 120,
+            prop: 'singlePrice',
+            label: '单价',
+            align: 'center',
+            slot: 'singlePrice'
+          },
+          {
+            minWidth: 100,
+            prop: 'taxRate',
+            label: '税率',
+            align: 'center',
+            slot: 'taxRate'
+          },
+          {
+            minWidth: 100,
+            prop: 'noTaxSinglePrice',
+            label: '不含税单价',
+            align: 'center',
+          },
+          {
+            minWidth: 100,
+            prop: 'totalPrice',
+            label: '合计',
+            align: 'center',
+          },
+          {
+            minWidth: 120,
+            headerSlot: 'headerRequired',
+            prop: 'discountRatio',
+            label: '折让比例',
+            align: 'center',
+            slot: 'discountRatio'
+          },
+          {
+            minWidth: 120,
+            prop: 'discountSinglePrice',
+            label: '折让单价',
+            align: 'center',
+          },
+          {
+            minWidth: 120,
+            prop: 'discountTotalPrice',
+            label: '折让合计',
+            align: 'center',
+          },
+          {
+            minWidth: 100,
+            prop: 'specification',
+            label: '规格',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 100,
+            prop: 'modelType',
+            label: '型号',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 80,
+            prop: 'color',
+            label: '颜色',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 120,
+            prop: 'outInCount',
+            label: '出库数量',
+            align: 'center',
+          },
+          {
+            minWidth: 120,
+            prop: 'measuringUnit',
+            label: '计量单位',
+            align: 'center',
+          },
+          {
+            minWidth: 120,
+            prop: 'sendTotalWeight',
+            label: '出库重量',
+            align: 'center',
+          },
+          {
+            minWidth: 120,
+            prop: 'weightUnit',
+            label: '重量单位',
+            align: 'center',
+          },
+          {
+            minWidth: 120,
+            prop: 'pricingWay',
+            label: '计价方式',
+            align: 'center',
+            formatter: (row, column) => {
+              return pricingWayList.find((item) => item.id == row.pricingWay)
+                ?.name;
+            }
+          },
+        ]
+      }
+    },
+    watch: {
+      datasource: {
+        handler(newVal, oldVal) {
+          this.$nextTick(() => {
+            // 直接基于原始 productList 扁平行初始化派生字段(单价/税率/折让比例默认值及本次对账金额)
+            const list = (newVal || []).map((item) => this.buildRow(item, item.orderNo || '', false));
+            this.flatList = list;
+            console.log(this.flatList)
+            this.calcTotals();
+          });
+        },
+        immediate: true
+      },
+      queryDimension() {
+        // this.buildFlatList();
+        this.calcTotals();
+      }
+    },
+    created() {
+      parameterGetByCode({ code: 'eom_saleOrder_order-taxRate' }).then(
+        (res) => {
+          this.isTaxRate = res.value;
+        }
+      );
+    },
+    methods: {
+      buildRow(item, orderNo, isReturn) {
+        const outboundCount = +item.totalCount || 0;
+        // 退货单数量为负
+        const count = isReturn ? -Math.abs(outboundCount) : outboundCount;
+        const statemented = +item.statementedCount || 0;
+        // 发货单:本次对账数量=出库数量(不可改);调拨单:默认=出库数量(可改)
+        let statementCount = item.statementCount;
+        if (
+          statementCount === undefined ||
+          statementCount === null ||
+          statementCount === ''
+        ) {
+          statementCount = count;
+        }
+        const singlePrice = +item.singlePrice || 0;
+        const discountRatio =
+          item.discountRatio === undefined ||
+          item.discountRatio === null ||
+          item.discountRatio === ''
+            ? 100
+            : +item.discountRatio;
+        const taxRate =
+          item.taxRate === undefined || item.taxRate === null
+            ? 0
+            : +item.taxRate;
+        const amount =
+          Math.round(
+            statementCount * singlePrice * (discountRatio / 100) * 100
+          ) / 100;
+        const noTaxSinglePrice =
+          singlePrice && taxRate
+            ? +((singlePrice / (1 + taxRate / 100)).toFixed(4))
+            : '';
+        const totalPrice = Math.round(statementCount * singlePrice * 100) / 100;
+        const discountSinglePrice =
+          Math.round(singlePrice * (discountRatio / 100) * 100) / 100;
+        const discountTotalPrice =
+          Math.round(totalPrice * (discountRatio / 100) * 100) / 100;
+        // 直接修改原 item,保证编辑同步回 datasource
+        // this.$set(item, 'id', item.id || Math.random().toString(36).slice(2));
+        this.$set(item, 'saleOrderNo', orderNo);
+        this.$set(
+          item,
+          'statementSubOrderNo',
+          item.statementSubOrderNo || item.subOrderNo || ''
+        );
+        this.$set(
+          item,
+          'productOperateTime',
+          item.productOperateTime || item.deliveryDate || ''
+        );
+        this.$set(item, 'outboundCount', count);
+        this.$set(item, 'statementedCount', statemented);
+        this.$set(item, 'statementCount', statementCount);
+        this.$set(item, 'singlePrice', singlePrice);
+        this.$set(item, 'taxRate', taxRate);
+        this.$set(item, 'discountRatio', discountRatio);
+        this.$set(item, 'statementAmount', amount);
+        this.$set(item, 'noTaxSinglePrice', noTaxSinglePrice);
+        this.$set(item, 'totalPrice', totalPrice);
+        this.$set(item, 'discountSinglePrice', discountSinglePrice);
+        this.$set(item, 'discountTotalPrice', discountTotalPrice);
+        this.$set(item, 'isReturn', isReturn);
+        this.$set(item, 'processRouteName', item.processRouteName || '');
+        return item;
+      },
+      initDefaultValues() {
+        this.datasource.forEach((item) => {
+            if (item.singlePrice === undefined)
+              this.$set(item, 'singlePrice', 0);
+            if (item.taxRate === undefined) this.$set(item, 'taxRate', 0);
+            if (item.discountRatio === undefined)
+              this.$set(item, 'discountRatio', 100);
+        });
+      },
+      buildFlatList() {
+        const list = [];
+        // this.datasource.forEach((order) => {
+        //   const orderNo = order.orderNo || '';
+        //   (order.deliveryProducts || []).forEach((item) => {
+        //     list.push(this.buildRow(item, orderNo, false));
+        //   });
+        //   (order.returnProducts || []).forEach((item) => {
+        //     list.push(this.buildRow(item, orderNo, true));
+        //   });
+        // });
+        // this.flatList = list;
+      },
+      calcTotals() {
+        const amount =
+          this.flatList.reduce(
+            (pre, cur) => pre + Math.round((+cur.statementAmount || 0) * 100),
+            0
+          ) / 100;
+        const count = this.flatList.reduce(
+          (pre, cur) => pre + (+cur.statementCount || 0),
+          0
+        );
+        this.totalStatementAmount = amount;
+        this.totalStatementCount = count;
+        this.$emit('totalChange', amount);
+        this.$emit('countChange', count);
+        // 按销售订单编码(saleOrderNo)汇总本次对账金额,供应收信息同步
+        this.emitStatementAmountChange();
+      },
+      // 按销售订单编码汇总本次对账金额并抛出
+      emitStatementAmountChange() {
+        const summaryMap = {};
+        this.flatList.forEach((row) => {
+          const key = row.saleOrderNo;
+          if (!key) return;
+          summaryMap[key] =
+            Math.round(
+              ((summaryMap[key] || 0) + (+row.statementAmount || 0)) * 100
+            ) / 100;
+        });
+        this.$emit('statementAmountChange', summaryMap);
+      },
+      // 重算某一行金额(参考 inventoryTable.vue 价格计算链)
+      calcRow(row) {
+        const count = +row.statementCount || 0;
+        const price = +row.singlePrice || 0;
+        // 折让比例未填写时按 100(不打折)计,避免金额被算成 0
+        const ratio = row.discountRatio === '' ||
+          row.discountRatio === null ||
+          row.discountRatio === undefined ||
+          isNaN(+row.discountRatio)
+          ? 100
+          : +row.discountRatio;
+        const taxRate = +row.taxRate || 0;
+        // 本次对账金额
+        const amount = Math.round(count * price * (ratio / 100) * 100) / 100;
+        this.$set(row, 'statementAmount', amount);
+        // 不含税单价 = 单价 / (1 + 税率/100)  (与 inventoryTable.getNotaxSinglePrice 一致)
+        if (price && taxRate) {
+          this.$set(
+            row,
+            'noTaxSinglePrice',
+            +((price / (1 + taxRate / 100)).toFixed(4))
+          );
+        } else {
+          this.$set(row, 'noTaxSinglePrice', '');
+        }
+        // 合计 = 对账数量 * 单价
+        this.$set(row, 'totalPrice', Math.round(count * price * 100) / 100);
+        // 折让单价 = 单价 * 折让比例/100
+        this.$set(
+          row,
+          'discountSinglePrice',
+          Math.round(price * (ratio / 100) * 100) / 100
+        );
+        // 折让合计 = 合计 * 折让比例/100
+        this.$set(
+          row,
+          'discountTotalPrice',
+          Math.round(row.totalPrice * (ratio / 100) * 100) / 100
+        );
+      },
+      handleCountChange(row, index) {
+        this.calcRow(row);
+        this.calcTotals();
+        this.emitChange();
+      },
+      handlePriceChange(row, index) {
+        this.calcRow(row);
+        this.calcTotals();
+        this.emitChange();
+      },
+      handleTaxChange(row, index) {
+        // 税率变化需重算不含税单价、合计及折让相关字段
+        this.calcRow(row);
+        this.calcTotals();
+        this.emitChange();
+      },
+      handleDiscountChange(row, index) {
+        this.calcRow(row);
+        this.calcTotals();
+        this.emitChange();
+      },
+      emitChange() {
+        // 表格实际编辑的是 flatList,直接以其为新数据源回写父组件
+        this.$emit('update:datasource', [...this.flatList]);
+      },
+      // 单价校验:单价缺失或为 0 视为无效(需结合商品价目表有效期进一步校验)
+      isPriceInvalid(row) {
+        const price = +row.singlePrice || 0;
+        return price <= 0;
+      },
+      validateStatementCount(rule, value, callback, row) {
+        const val = +value || 0;
+        const max = Math.abs(row.outboundCount || 0);
+        if (Math.abs(val) > max) {
+          callback(new Error('不能超出库数量'));
+        } else if (this.queryDimension == 1 && val != row.outboundCount) {
+          callback(new Error('按发货单查询时不可修改'));
+        } else {
+          callback();
+        }
+      },
+      // 批量设置
+      batchSetTaxRate() {
+        this.batchType = 'taxRate';
+        this.batchLabel = '税率';
+        this.batchValue = '';
+        this.batchDialogVisible = true;
+      },
+      batchSetPrice() {
+        this.batchType = 'singlePrice';
+        this.batchLabel = '单价';
+        this.batchValue = '';
+        this.batchDialogVisible = true;
+      },
+      batchSetDiscount() {
+        this.batchType = 'discountRatio';
+        this.batchLabel = '折让比例';
+        this.batchValue = '';
+        this.batchDialogVisible = true;
+      },
+      confirmBatchSet() {
+        // flatList 为表格实际渲染的扁平行数据,直接逐行设置并重新计算
+        this.flatList.forEach((item) => {
+          this.$set(item, this.batchType, +this.batchValue);
+          this.calcRow(item);
+        });
+        this.calcTotals();
+        this.batchDialogVisible = false;
+        this.emitChange();
+      },
+      removeRow(index) {
+        // 调拨单模式下允许删除行,基于扁平行数组操作,保持数据格式一致
+        if (index < 0 || index >= this.flatList.length) return;
+        const list = [...this.flatList];
+        list.splice(index, 1);
+        this.flatList = list;
+        this.calcTotals();
+        this.emitChange();
+      },
+      getValidForm() {
+        return new Promise((resolve, reject) => {
+          this.$refs.detailForm.validate((valid) => {
+            if (valid) resolve(true);
+            else {
+              this.$message.error('对账明细未填写完整');
+              reject(false);
+            }
+          });
+        });
+      },
+      // 打开工艺路线选择弹窗(参考 inventoryTable.openVersion)
+      openVersion(index) {
+        this.$refs.versionRefs.open(index);
+      },
+      // 工艺路线选择回调(参考 inventoryTable.changeProduct)
+      changeProduct(data, index) {
+        if (this.flatList[index]) {
+          this.$set(this.flatList[index], 'technologyRouteName', data.name);
+          this.$set(this.flatList[index], 'technologyRouteId', data.id);
+          this.$set(this.flatList[index], 'technologyRouteVersion', data.version);
+          this.emitChange();
+        }
+      }
+    }
+  };
+</script>
+
+<style scoped lang="scss">
+  .account-detail-table {
+    .detail-toolbar {
+      display: flex;
+      justify-content: flex-start;
+      align-items: center;
+      margin-bottom: 10px;
+      .summary-info {
+        span {
+          margin-left: 20px;
+          b {
+            color: #f56c6c;
+          }
+        }
+      }
+    }
+    .time-form .el-form-item {
+      margin-bottom: 0 !important;
+    }
+    .price-warning {
+      color: #f56c6c;
+      font-size: 12px;
+      line-height: 1.4;
+      margin-top: 2px;
+    }
+  }
+</style>

+ 53 - 2
src/views/bpm/handleTask/components/saleOrder/accountstatement/page.vue

@@ -32,6 +32,25 @@
     <inventoryTable ref="inventoryTableref" :priceObj="priceObj" :dataForm="dataForm" :datasource.sync="datasource"
                     :dialogType="permissionType"
                     :taskDefinitionKey="taskDefinitionKey"></inventoryTable>
+    <!-- <headerTitle title="应收信息" style="margin-top: 30px"></headerTitle>
+      <receivable-info
+        :dataForm.sync="dataForm"
+        :receivableList="receivableList"
+        :dialogType="permissionType"
+        @addAdvanceReceipt="addAdvanceReceipt"
+        ref="receivableInfoRef"
+      ></receivable-info>
+
+      <headerTitle title="对账明细" style="margin-top: 30px"></headerTitle>
+      <account-detail-table
+        ref="accountDetailTableRef"
+        :datasource.sync="datasource"
+        :dialogType="permissionType"
+        :queryDimension="dataForm.queryDimension"
+        @totalChange="handleTotalChange"
+        @countChange="handleCountChange"
+        @statementAmountChange="handleStatementAmountChange"
+      ></account-detail-table> -->
   </div>
 </template>
 
@@ -42,6 +61,8 @@ import InventoryTable from "./inventoryTable.vue";
 import saleForm from "./saleForm.vue";
 import {getSearchMergeListByTypeAPI, infoAccountStatementAPI, accountstatementInfoAPI} from "@/api/bpm/components/saleManage/saleorder";
 import recorpayTableList from './recorpayTableList.vue';
+import AccountDetailTable from './accountDetailTable.vue';
+import ReceivableInfo from './receivableInfo.vue';
 
 export default {
   name: 'page',
@@ -49,11 +70,15 @@ export default {
   components: {
     InventoryTable,
     saleForm,
-    recorpayTableList
+    recorpayTableList,
+    AccountDetailTable,
+    ReceivableInfo
   },
   data() {
     return {
       datasource: [],
+      receivableList: [],
+      statReceipt: null,
       dataForm: {
         sourceType: '',
         dateType: '',
@@ -121,18 +146,44 @@ export default {
     this.getInfo(this.businessId)
   },
   methods: {
+    handleTotalChange(total) {
+        this.dataForm.amountTotalPrice = total;
+      },
+      handleCountChange(count) {
+        this.dataForm.totalStatementCount = count;
+      },
+      // 明细表本次对账金额变化时,按销售单号同步到应收信息对应行
+      handleStatementAmountChange(summaryMap) {
+        if (this.$refs.receivableInfoRef) {
+          this.$refs.receivableInfoRef.syncStatementAmount(summaryMap);
+        }
+      },
+      // 新增预收款:打开预收款选择弹窗
+      addAdvanceReceipt() {
+        this.$refs.advanceReceiptSelectRef.open();
+      },
 
     //获取对账单详情
     async getInfo(id) {
       let data = await accountstatementInfoAPI(id)
       this.datasource = data?.orderList || []
-      // this.recorpayList=data?.recorpayList||[]
+      this.recorpayList=data?.recorpayList||[]
       data.orderTotalAmount = data.orderList.reduce((pre, cur) => pre + Math.round(+cur.orderAmount * 100), 0) / 100;
       data.amountTotalPrice = data.orderList.reduce((pre, cur) => pre + Math.round(+cur.statementAmount * 100), 0) / 100;
       data.amountCompletePrice = data.orderList.reduce((pre, cur) => pre + Math.round(+cur.statementedAmount * 100), 0) / 100;
       data.amountUnCompletePrice = data.orderList.reduce((pre, cur) => pre + Math.round(+cur.unStatementAmount * 100), 0) / 100;
       this.dataForm = data
       //this.priceObj.previousPeriodEndDebt = data.previousPeriodEndDebt
+
+      // this.datasource = data.productList || [];
+      //   this.receivableList =
+      //     (data.statReceipt && data.statReceipt.statReceiptDetailList) || [];
+      //   // 保存查询到的完整 statReceipt 对象,作为提交入参
+      //   this.statReceipt = data.statReceipt || null;
+      //   this.dataForm = {
+      //     ...this.dataForm,
+      //     ...data,
+      //   };
       switch (this.dataForm.dateType) {
         case 1:
           this.dataForm.year = this.dataForm.dateValue

+ 406 - 0
src/views/bpm/handleTask/components/saleOrder/accountstatement/receivableInfo.vue

@@ -0,0 +1,406 @@
+<template>
+  <div class="receivable-info">
+    <el-form ref="form" :model="dataForm" :rules="rules" label-width="160px">
+      <el-row>
+        <el-col :span="8">
+          <el-form-item label="总对账预收款确收金额">
+            <el-input disabled v-model="statementAdvanceTotalPrice">
+              <template slot="append">元</template>
+            </el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="8">
+          <el-form-item label="总应收金额">
+            <el-input disabled v-model="receivedTotalPrice">
+              <template slot="append">元</template>
+            </el-input>
+          </el-form-item>
+        </el-col>
+      </el-row>
+    </el-form>
+
+    <el-form :model="{ receivableList: localList }" ref="tableForm">
+      <ele-pro-table
+        ref="receivableTable"
+        row-key="id"
+        :needPage="false"
+        :columns="columns"
+        max-height="400px"
+        :datasource="localList"
+        :cache-key="cacheKeyUrl"
+        class="time-form"
+      >
+        <template v-slot:fundType="scope">
+          <el-select
+            v-model="scope.row.fundType"
+            placeholder="请选择"
+            class="ele-block"
+            :disabled="isView"
+            @change="(val) => updateRowField(scope.$index, 'fundType', val)"
+          >
+            <el-option
+              v-for="item in paymentTypeOp"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            />
+          </el-select>
+        </template>
+
+        <template v-slot:transactionMode="scope">
+          <el-select
+            v-model="scope.row.transactionMode"
+            placeholder="请选择"
+            class="ele-block"
+            :disabled="isView"
+            @change="
+              (val) => updateRowField(scope.$index, 'transactionMode', val)
+            "
+          >
+            <el-option
+              v-for="item in transactionMethodsOp"
+              :key="item.value"
+              :label="item.label"
+              :value="item.value"
+            />
+          </el-select>
+        </template>
+
+        <template v-slot:collectionConditionName="scope">
+          <el-input
+            clearable
+            :value="scope.row.collectionConditionName"
+            @click.native="openReceiveTerms(scope.$index)"
+            :placeholder="isView ? '' : '请选择'"
+            :disabled="isView"
+            readonly
+          />
+        </template>
+
+        <template v-slot:statementAdvanceConfirmAmount="scope">
+          <el-form-item
+            :prop="'receivableList.' + scope.$index + '.statementAdvanceConfirmAmount'"
+            :rules="{
+              validator: (rule, value, cb) =>
+                validateConfirmAmount(rule, value, cb, scope.row),
+              trigger: 'change'
+            }"
+          >
+            <el-input
+              v-model="scope.row.statementAdvanceConfirmAmount"
+              type="number"
+              :disabled="isView"
+              @input="handleConfirmAmountChange(scope.row, scope.$index)"
+            />
+          </el-form-item>
+        </template>
+
+        <template v-slot:action="scope">
+          <el-link
+            v-if="!isView"
+            type="danger"
+            :underline="false"
+            icon="el-icon-delete"
+            @click="removeRow(scope.$index)"
+            >删除</el-link
+          >
+        </template>
+      </ele-pro-table>
+    </el-form>
+    <!-- 收款条件 -->
+    <!-- <receiveTermsDialog
+      ref="receiveTermsDialogRef"
+      conditionType="rules"
+      @changeParent="changeReceiveTerms"
+    ></receiveTermsDialog> -->
+  </div>
+</template>
+
+<script>
+  import {
+    paymentTypeOp,
+    transactionMethodsOp,
+    paymentStatus
+  } from '@/enum/dict';
+  // import receiveTermsDialog from '@/views/financialManage/advanceReceipt/components/receiveTermsDialog.vue';
+
+  export default {
+    name: 'receivableInfo',
+    components: {
+      // receiveTermsDialog
+    },
+    props: {
+      dataForm: {
+        type: Object,
+        default: () => ({})
+      },
+      // 父组件传入的初始应收明细,仅作为初始化值,修改不再回写父
+      receivableList: {
+        type: Array,
+        default: () => []
+      },
+      dialogType: {
+        type: String,
+        default: 'add'
+      }
+    },
+    data() {
+      return {
+        cacheKeyUrl: 'eom-202608171425-account-receivable-table',
+        paymentTypeOp,
+        transactionMethodsOp,
+        paymentStatusOp: paymentStatus,
+        rules: {},
+        // 本地维护的应收明细,避免每次编辑都同步回父组件
+        localList: [],
+        columns: [
+          {
+            width: 60,
+            label: '序号',
+            type: 'index',
+            columnKey: 'index',
+            align: 'center',
+            fixed: 'left'
+          },
+          {
+            minWidth: 140,
+            prop: 'salesCode',
+            label: '销售订单编码',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 140,
+            prop: 'advanceCode',
+            label: '预收编码',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            prop: 'fundType',
+            minWidth: 140,
+            label: '款项类型',
+            align: 'center',
+            slot: 'fundType'
+          },
+          {
+            prop: 'transactionMode',
+            minWidth: 140,
+            label: '交易方式',
+            align: 'center',
+            slot: 'transactionMode'
+          },
+          {
+            prop: 'collectionConditionName',
+            minWidth: 160,
+            label: '收款条件',
+            align: 'center',
+            slot: 'collectionConditionName'
+          },
+          {
+            minWidth: 140,
+            prop: 'unreconciledAdvanceConfirmAmount',
+            label: '未对账预收款确收金额',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 160,
+            prop: 'statementAdvanceConfirmAmount',
+            label: '本次对账预收款确收金额',
+            align: 'center'
+          },
+          {
+            minWidth: 120,
+            prop: 'statementAmount',
+            label: '本次对账金额',
+            align: 'center',
+            showOverflowTooltip: true
+          },
+          {
+            minWidth: 120,
+            prop: 'receivedPrice',
+            label: '本次应收金额',
+            align: 'center',
+            showOverflowTooltip: true
+          }
+        ]
+      };
+    },
+    computed: {
+      isView() {
+        return this.dialogType === 'view';
+      },
+      statementAdvanceTotalPrice() {
+        return (
+          this.localList.reduce(
+            (pre, cur) =>
+              pre + Math.round((+cur.statementAdvanceConfirmAmount || 0) * 100),
+            0
+          ) / 100
+        );
+      },
+      receivedTotalPrice() {
+        return (
+          Math.round(
+            ((+this.dataForm.amountTotalPrice || 0) -
+              this.statementAdvanceTotalPrice) *
+              100
+          ) / 100
+        );
+      }
+    },
+    watch: {
+      // 父传入初始值(查询/编辑回填)时同步到本地副本
+      receivableList: {
+        handler(val) {
+          this.localList = (val || []).map((item) => ({ ...item }));
+          this.recalcAllRows();
+        },
+        immediate: true
+      },
+      'dataForm.amountTotalPrice': {
+        handler() {
+          this.recalcAllRows();
+        },
+        immediate: true
+      }
+    },
+    methods: {
+      openReceiveTerms(index) {
+        this.currentRowIndex = index;
+        this.$refs.receiveTermsDialogRef.open();
+      },
+
+      changeReceiveTerms(row, index) {
+        const targetIndex = index != null ? index : this.currentRowIndex;
+        if (targetIndex == null) return;
+        this.updateRowField(targetIndex, 'collectionConditionId', row.id);
+        this.updateRowField(targetIndex, 'collectionConditionName', row.name);
+      },
+      // 行内字段修改:仅更新本地副本
+      updateRowField(index, key, val) {
+        const item = this.localList[index];
+        if (!item) return;
+        this.localList[index] = { ...item, [key]: val };
+        this.recalcRow(item);
+      },
+      // 新增一行预收款(空白行,供手动填写)
+      addRow() {
+        this.localList.push({
+          id: 'local_' + Date.now() + '_' + this.localList.length,
+          salesCode: '',
+          advanceCode: '',
+          fundType: '',
+          transactionMode: '',
+          collectionConditionId: '',
+          collectionConditionName: '',
+          unreconciledAdvanceConfirmAmount: 0,
+          statementAdvanceConfirmAmount: 0,
+          statementAmount: 0,
+          receivedPrice: 0
+        });
+      },
+      // 由父组件预收款选择器回调调用:追加一行已映射好的应收明细(本地维护)
+      addReceivableRow(row) {
+        this.localList.push({ ...row });
+      },
+      removeRow(index) {
+        if (index < 0 || index >= this.localList.length) return;
+        this.localList.splice(index, 1);
+      },
+      // 本次对账预收款确收金额上限 = min(未对账预收款确收金额, 本次对账金额)
+      getMaxConfirmAmount(row) {
+        return Math.min(
+          +row.unreconciledAdvanceConfirmAmount || 0,
+          +row.statementAmount || 0
+        );
+      },
+      handleConfirmAmountChange(row, index) {
+        // 限制本次确收金额不能超过未对账金额和本次对账金额
+        let val = +row.statementAdvanceConfirmAmount || 0;
+        const max = this.getMaxConfirmAmount(row);
+        if (val > max) {
+          val = max;
+          this.$set(row, 'statementAdvanceConfirmAmount', val);
+          this.$message.warning(
+            '本次对账预收款确收金额不能超过未对账金额和本次对账金额'
+          );
+        }
+        this.recalcRow(row);
+      },
+      // 根据本次对账金额重算单行:确收金额 = min(未对账预收款确收金额, 本次对账金额);应收金额 = 本次对账金额 - 确收金额
+      recalcRow(row) {
+        const statementAmount = +row.statementAmount || 0;
+        let val = +row.statementAdvanceConfirmAmount || 0;
+        const max = this.getMaxConfirmAmount(row);
+        if (val > max) val = max;
+        this.$set(row, 'statementAdvanceConfirmAmount', val);
+        this.$set(
+          row,
+          'receivedPrice',
+          Math.round((statementAmount - val) * 100) / 100
+        );
+      },
+      // 明细表按销售单号汇总本次对账金额后,同步到对应销售单号的应收行
+      syncStatementAmount(summaryMap) {
+        if (!summaryMap || !Object.keys(summaryMap).length) return;
+        this.localList.forEach((row) => {
+          const amount = summaryMap[row.salesCode];
+          if (amount !== undefined) {
+            this.$set(row, 'statementAmount', amount);
+            this.recalcRow(row);
+          }
+        });
+      },
+      recalcAllRows() {
+        this.localList.forEach((row) => {
+          this.recalcRow(row);
+        });
+      },
+      validateConfirmAmount(rule, value, callback, row) {
+        const val = +value || 0;
+        const max = Math.min(
+          +row.unreconciledAdvanceConfirmAmount || 0,
+          +row.statementAmount || 0
+        );
+        if (val < 0) {
+          callback(new Error('不能小于0'));
+        } else if (val > max) {
+          callback(new Error(`不能超过${max}`));
+        } else {
+          callback();
+        }
+      },
+      // 提交时由父组件调用,一次性取回整个应收表单(本地明细 + 汇总)
+      getSubmitData() {
+        return {
+          receivableList: this.localList.map((item) => ({ ...item })),
+          statementAdvanceTotalPrice: this.statementAdvanceTotalPrice,
+          receivedTotalPrice: this.receivedTotalPrice
+        };
+      },
+      validate() {
+        return new Promise((resolve, reject) => {
+          const promises = [this.$refs.form.validate()];
+          if (this.$refs.tableForm) {
+            promises.push(this.$refs.tableForm.validate());
+          }
+          Promise.all(promises)
+            .then(() => resolve(true))
+            .catch(() => reject(false));
+        });
+      }
+    }
+  };
+</script>
+
+<style scoped lang="scss">
+  .receivable-info {
+    .receivable-table-header {
+      display: flex;
+      justify-content: flex-end;
+      margin-bottom: 10px;
+    }
+  }
+</style>

+ 5 - 2
src/views/bpm/handleTask/components/saleOrder/accountstatement/saleForm.vue

@@ -330,11 +330,14 @@
       },
       //计算未对账金额
       accountStatementPrice() {
-        return this.datasource.reduce((pre, cur) => pre + +cur.amountTotalPrice, 0);
+        return this.datasource.reduce(
+          (pre, cur) => pre + +cur.statementAmount,
+          0
+        );
       },
       //计算相关订单号
       relatedOrderNumber() {
-        return this.datasource.map((item) => item.orderNo).join(',');
+        return [...new Set(this.datasource.map((item) => item.orderNo))].join(',');
       }
     },
     methods: {

+ 10 - 2
src/views/bpm/handleTask/components/saleOrder/detailDialog.vue

@@ -15,7 +15,7 @@
       </div>
     </div>
     <div v-show="activeComp == 'main'">
-      <el-form ref="form" :model="form" :rules="rules" label-width="120px">
+      <el-form ref="form" :model="form" :rules="rules" label-width="140px">
         <headerTitle title="订单信息"> </headerTitle>
         <el-row>
           <el-col :span="8">
@@ -87,6 +87,9 @@
             <el-form-item label="业务员:" prop="salesmanName">
               {{ form.salesmanName }}
             </el-form-item>
+            <el-form-item prop="collectionConditionName" label="收款条件:">
+              {{ form.collectionConditionName }}
+            </el-form-item>
             <el-form-item label="订单总金额:" prop="totalAmount">
               {{ form.totalAmount }}元
             </el-form-item>
@@ -114,6 +117,9 @@
               {{ form.totalPlanNum }}
               {{ form.planNumUnit }}
             </el-form-item>
+            <el-form-item prop="isGenerateReceiptPlan" label="生成收款计划表:">
+              {{ form.isGenerateReceiptPlan == 1 ? '是' : '否' }}
+            </el-form-item>
             <el-form-item label="交易方式:" prop="transactionMode">
               {{ transactionMethodsOp.find(item => item.value == form.transactionMode)?.label }}
             </el-form-item>
@@ -134,11 +140,13 @@
               每月{{ form.receiptDate }}日
             </el-form-item>
             <el-form-item
+              v-if="form.isGenerateReceiptPlan != 0"
               label="期数:"
               prop="issueNumber"
             >
               {{ form.issueNumber }}期
             </el-form-item>
+
             <el-form-item label="订单备注:" prop="remark">
               {{ form.remark }}
             </el-form-item>
@@ -249,7 +257,7 @@
         type="view"
       ></typeList>
 
-      <div v-if="form.needProduce != 2 && form.needProduce != 4" style="margin-top: 30px">
+      <div v-if="form.needProduce != 2 && form.needProduce != 4 && form.isGenerateReceiptPlan != 0" style="margin-top: 30px">
         <headerTitle
           title="收款计划"
         ></headerTitle>

+ 7 - 0
src/views/bpm/todo/index.vue

@@ -224,6 +224,13 @@
             showOverflowTooltip: true,
             minWidth: 120
           },
+          {
+            prop: 'businessScope.warehouseName',
+            label: '审批仓库',
+            align: 'center',
+            showOverflowTooltip: true,
+            minWidth: 120
+          },
           {
             prop: 'businessType',
             label: '单据类型',

+ 2 - 2
vue.config.js

@@ -38,11 +38,11 @@ module.exports = {
         // target: 'http://124.71.68.31:50001', // 测试环境
         // target: 'http://124.71.68.31:50001',
         // target: 'http://192.168.1.105:18086',
-        // target: 'http://localhost:18086',
+        target: 'http://localhost:18086',
         // target: 'http://192.168.1.116:18086',
         // target: 'http://192.168.1.3:18086',
         // target: 'http://192.168.1.251:18186',
-        target: 'http://192.168.1.251:18086',
+        // target: 'http://192.168.1.251:18086',
         // target: 'http://192.168.1.3:18086',
         // target: 'http://aiot.zoomwin.com.cn:51001/api',
         // target: 'http://192.168.1.3:18086',