| | |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="上传附件"> |
| | | <FileUpload |
| | | :showTip="false" |
| | | accept="*" |
| | | :autoUpload="true" |
| | | :action="action" |
| | | :headers="{ |
| | | Authorization: 'Bearer ' + getToken(), |
| | | }" |
| | | :limit="10" |
| | | @success="uploadSuccess" |
| | | @remove="removeFile" |
| | | /> |
| | | </el-form-item> |
| | | </el-col> |
| | | |
| | | </el-row> |
| | | <el-form-item label="产品信息:"> </el-form-item> |
| | |
| | | /> |
| | | <el-table-column label="本次开票数" prop="ticketsNum" width="180"> |
| | | <template #default="scope"> |
| | | <el-input-number :step="0.1" :min="0" style="width: 100%" |
| | | :precision="2" |
| | | v-model="scope.row.ticketsNum" |
| | | @change="invoiceNumBlur(scope.row)" |
| | | <el-input-number |
| | | :step="0.1" |
| | | :min="0" |
| | | :max="scope.row.tempFutureTickets || 0" |
| | | style="width: 100%" |
| | | :precision="2" |
| | | v-model="scope.row.ticketsNum" |
| | | :disabled="isProductDisabled(scope.row)" |
| | | @change="invoiceNumBlur(scope.row)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | width="180" |
| | | > |
| | | <template #default="scope"> |
| | | <el-input-number :step="0.01" :min="0" style="width: 100%" |
| | | :precision="2" |
| | | v-model="scope.row.ticketsAmount" |
| | | @change="invoiceAmountBlur(scope.row)" |
| | | <el-input-number |
| | | :step="0.01" |
| | | :min="0" |
| | | style="width: 100%" |
| | | :precision="2" |
| | | v-model="scope.row.ticketsAmount" |
| | | :disabled="isProductDisabled(scope.row)" |
| | | @change="invoiceAmountBlur(scope.row)" |
| | | /> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | import { defineEmits } from 'vue'; |
| | | import { useModal } from "@/hooks/useModal"; |
| | | import useFormData from "@/hooks/useFormData"; |
| | | import FileUpload from "@/components/Upload/FileUpload.vue"; |
| | | import { |
| | | getPurchaseNoById, |
| | | getInfo, |
| | | addOrUpdateRegistration, |
| | | } from "@/api/procurementManagement/invoiceEntry.js"; |
| | | import { getPurchaseById } from "@/api/procurementManagement/procurementLedger.js"; |
| | | import { getToken } from "@/utils/auth"; |
| | | import useUserStore from "@/store/modules/user"; |
| | | import dayjs from "dayjs"; |
| | | |
| | |
| | | }); |
| | | |
| | | const userStore = useUserStore(); |
| | | const action = import.meta.env.VITE_APP_BASE_API + "/file/upload"; |
| | | const formRef = ref(); |
| | | const { proxy } = getCurrentInstance(); |
| | | const { form } = useFormData({ |
| | |
| | | salesContractNoId: undefined, // 开票日期 |
| | | enterDate: dayjs().format("YYYY-MM-DD"), |
| | | productData: [], // 表格 |
| | | tempFileIds: [], // 文件 |
| | | }); |
| | | |
| | | const selectedContracts = ref([]); // 存储选中的合同数据 |
| | |
| | | result.data.productData.forEach(item => { |
| | | allProductData.push({ |
| | | ...item, |
| | | id: contractId, // 明确设置合同ID |
| | | purchaseLedgerId: contractId, // 添加合同ID用于筛选 |
| | | purchaseLedgerNo: contract.purchaseContractNumber, // 添加采购合同号 |
| | | supplierName: contract.supplierName, // 添加供应商名称 |
| | | projectName: contract.projectName // 添加项目名称 |
| | | // 保留产品本身的id,不覆盖 |
| | | }); |
| | | }); |
| | | } |
| | |
| | | |
| | | // 设置产品数据,并初始化开票数量和金额 |
| | | allProductData.forEach(item => { |
| | | // 本次开票数默认为总数量 |
| | | item.ticketsNum = Number(item.quantity || 0); |
| | | // 本次开票金额默认为含税总价 |
| | | item.ticketsAmount = Number(item.taxInclusiveTotalPrice || 0); |
| | | // 保存原始未来票数和金额(用于计算) |
| | | item.tempFutureTickets = Number(item.quantity || 0); |
| | | item.tempFutureTicketsAmount = Number(item.taxInclusiveTotalPrice || 0); |
| | | // 未来票数和金额初始为0(因为全部开票) |
| | | item.futureTickets = 0; |
| | | item.futureTicketsAmount = 0; |
| | | // 保存"原始未来票数/金额"(用于校验与计算) |
| | | // 优先使用后端返回的 futureTickets/futureTicketsAmount;没有则回退到 quantity/taxInclusiveTotalPrice |
| | | item.tempFutureTickets = Number( |
| | | item.futureTickets !== undefined ? item.futureTickets : (item.quantity || 0) |
| | | ); |
| | | item.tempFutureTicketsAmount = Number( |
| | | item.futureTicketsAmount !== undefined ? item.futureTicketsAmount : (item.taxInclusiveTotalPrice || 0) |
| | | ); |
| | | |
| | | // 如果未来票金额为0,则本次开票数和金额都设置为0 |
| | | if (item.tempFutureTicketsAmount <= 0) { |
| | | item.ticketsNum = 0; |
| | | item.ticketsAmount = 0; |
| | | item.futureTickets = Number(item.tempFutureTickets || 0); |
| | | item.futureTicketsAmount = 0; |
| | | } else { |
| | | // 新增时:本次开票数默认 = 未来票数(且不能大于未来票数) |
| | | item.ticketsNum = Number(item.tempFutureTickets || 0); |
| | | // 联动计算本次开票金额、未来票数、未来票金额 |
| | | const unitPrice = Number(item.taxInclusiveUnitPrice || 0); |
| | | item.ticketsAmount = Number((item.ticketsNum * unitPrice).toFixed(2)); |
| | | item.futureTickets = Number((item.tempFutureTickets - item.ticketsNum).toFixed(2)); |
| | | item.futureTicketsAmount = Number( |
| | | (item.tempFutureTicketsAmount - item.ticketsAmount).toFixed(2) |
| | | ); |
| | | } |
| | | }); |
| | | |
| | | form.productData = allProductData; |
| | | |
| | | // 计算发票金额:所有产品的含税总价之和 |
| | | // 计算发票金额:所有产品的本次开票金额之和(新增默认 0) |
| | | const totalAmount = allProductData.reduce((sum, item) => { |
| | | return sum + (Number(item.taxInclusiveTotalPrice) || 0); |
| | | return sum + (Number(item.ticketsAmount) || 0); |
| | | }, 0); |
| | | form.invoiceAmount = totalAmount.toFixed(2); |
| | | form.invoiceAmount = Number(totalAmount.toFixed(2)); |
| | | |
| | | // 存储选中的合同数据 |
| | | selectedContracts.value = selectedRows; |
| | | }); |
| | | } else if (type == "edit") { |
| | | const id = Array.isArray(selectedRows) ? selectedRows[0].id : selectedRows; |
| | | const data = await getPurchaseById({ id, type: 2 }); |
| | | form.purchaseLedgerNo = data.purchaseContractNumber; |
| | | const response = await getPurchaseById({ id, type: 2 }); |
| | | // 兼容不同的返回格式:可能是 { code, data } 或直接返回数据 |
| | | const data = response.data || response; |
| | | |
| | | // 兼容不同的字段名:purchaseContractNumber 或 purchaseLedgerNo |
| | | form.purchaseLedgerNo = data.purchaseContractNumber || data.purchaseLedgerNo || ""; |
| | | form.invoiceAmount = data.invoiceAmount; |
| | | form.invoiceNumber = data.invoiceNumber; |
| | | form.salesContractNo = data.salesContractNo; |
| | | form.projectName = data.projectName; |
| | | form.supplierName = data.supplierName; |
| | | form.entryDate = data.entryDate; |
| | | form.productData = data.productData; |
| | | form.enterDate = data.enterDate || dayjs().format("YYYY-MM-DD"); |
| | | |
| | | // 编辑时也需要初始化产品数据的 tempFutureTickets 和 tempFutureTicketsAmount |
| | | // 同时为每个产品添加合同号等信息 |
| | | const contractNumber = data.purchaseContractNumber || data.purchaseLedgerNo || ""; |
| | | if (data.productData && Array.isArray(data.productData)) { |
| | | data.productData.forEach(item => { |
| | | // 保存"原始未来票数/金额"(用于校验与计算) |
| | | // 优先使用后端返回的 futureTickets/futureTicketsAmount;没有则回退到 quantity/taxInclusiveTotalPrice |
| | | item.tempFutureTickets = Number( |
| | | item.futureTickets !== undefined ? item.futureTickets : (item.quantity || 0) |
| | | ); |
| | | item.tempFutureTicketsAmount = Number( |
| | | item.futureTicketsAmount !== undefined ? item.futureTicketsAmount : (item.taxInclusiveTotalPrice || 0) |
| | | ); |
| | | |
| | | // 确保每个产品都有合同号,用于显示在"所属合同"列 |
| | | if (!item.purchaseLedgerNo) { |
| | | item.purchaseLedgerNo = contractNumber; |
| | | } |
| | | }); |
| | | } |
| | | |
| | | form.productData = data.productData || []; |
| | | |
| | | // 编辑模式下,根据产品数据中的本次开票金额自动计算发票金额 |
| | | calculateinvoiceAmount(); |
| | | } |
| | | }; |
| | | // 子表合计方法 |
| | |
| | | row.ticketsNum = 0; |
| | | } |
| | | if (Number(row.ticketsNum) > Number(row.tempFutureTickets)) { |
| | | proxy.$modal.msgWarning("本次开票数不得大于未开票数"); |
| | | row.ticketsNum = 0; |
| | | return; |
| | | proxy.$modal.msgWarning("本次开票数不能大于未来票数"); |
| | | row.ticketsNum = Number(row.tempFutureTickets || 0); |
| | | } |
| | | // 计算本次来票金额 |
| | | row.ticketsAmount = (row.ticketsNum * row.taxInclusiveUnitPrice).toFixed(2) |
| | | row.ticketsAmount = Number((Number(row.ticketsNum) * Number(row.taxInclusiveUnitPrice || 0)).toFixed(2)); |
| | | // 计算未来票数 |
| | | row.futureTickets = (row.tempFutureTickets - row.ticketsNum).toFixed(2) |
| | | row.futureTickets = Number((Number(row.tempFutureTickets || 0) - Number(row.ticketsNum || 0)).toFixed(2)); |
| | | // 计算未来票金额 |
| | | row.futureTicketsAmount = (row.tempFutureTicketsAmount - row.ticketsAmount).toFixed(2) |
| | | row.futureTicketsAmount = Number((Number(row.tempFutureTicketsAmount || 0) - Number(row.ticketsAmount || 0)).toFixed(2)); |
| | | calculateinvoiceAmount(); |
| | | }; |
| | | |
| | |
| | | // 计算是否超过来票总金额 |
| | | if (row.ticketsAmount > row.tempFutureTicketsAmount) { |
| | | proxy.$modal.msgWarning("本次来票金额不得大于未来票金额"); |
| | | row.ticketsAmount = 0; |
| | | row.ticketsAmount = Number(row.tempFutureTicketsAmount || 0); |
| | | } |
| | | // 计算本次来票数 |
| | | row.ticketsNum = Number( |
| | | (row.ticketsAmount / row.taxInclusiveUnitPrice).toFixed(2) |
| | | ); |
| | | // 检查本次开票数是否大于未来票数 |
| | | if (Number(row.ticketsNum) > Number(row.tempFutureTickets)) { |
| | | proxy.$modal.msgWarning("本次开票数不能大于未来票数"); |
| | | row.ticketsNum = Number(row.tempFutureTickets || 0); |
| | | // 重新计算本次来票金额 |
| | | row.ticketsAmount = Number((Number(row.ticketsNum) * Number(row.taxInclusiveUnitPrice || 0)).toFixed(2)); |
| | | } |
| | | // 计算未来票数 |
| | | row.futureTickets = (row.tempFutureTickets - row.ticketsNum).toFixed(2) |
| | | row.futureTickets = Number((Number(row.tempFutureTickets || 0) - Number(row.ticketsNum || 0)).toFixed(2)); |
| | | // 计算未来票金额 |
| | | row.futureTicketsAmount = (row.tempFutureTicketsAmount - row.ticketsAmount).toFixed(2) |
| | | row.futureTicketsAmount = Number((Number(row.tempFutureTicketsAmount || 0) - Number(row.ticketsAmount || 0)).toFixed(2)); |
| | | calculateinvoiceAmount(); |
| | | }; |
| | | |
| | |
| | | invoiceAmountTotal += Number(item.ticketsAmount); |
| | | } |
| | | }); |
| | | form.invoiceAmount = invoiceAmountTotal.toFixed(2); |
| | | form.invoiceAmount = Number(invoiceAmountTotal.toFixed(2)); |
| | | }; |
| | | |
| | | // 判断产品是否可以继续来票操作:如果未来票数和未来票金额都为0或小于等于0,则禁用 |
| | | const isProductDisabled = (row) => { |
| | | // 优先使用 tempFutureTickets(原始未来票数),如果没有则使用 futureTickets |
| | | const futureTickets = Number(row.tempFutureTickets !== undefined |
| | | ? row.tempFutureTickets |
| | | : (row.futureTickets !== undefined ? row.futureTickets : 0)); |
| | | |
| | | // 优先使用 tempFutureTicketsAmount(原始未来票金额),如果没有则使用 futureTicketsAmount |
| | | const futureAmount = Number(row.tempFutureTicketsAmount !== undefined |
| | | ? row.tempFutureTicketsAmount |
| | | : (row.futureTicketsAmount !== undefined ? row.futureTicketsAmount : 0)); |
| | | |
| | | // 只有当未来票数和未来票金额都为0或小于等于0时,才禁用 |
| | | return futureTickets <= 0 && futureAmount <= 0; |
| | | }; |
| | | |
| | | const open = async (type, selectedRows) => { |
| | | visible.value = true; |
| | | |
| | | // 如果是批量操作,设置标题 |
| | | if (Array.isArray(selectedRows) && selectedRows.length > 1) { |
| | | modalOptions.title = `批量新增 (${selectedRows.length}条)`; |
| | | } else { |
| | | modalOptions.title = type === "add" ? "新增" : "编辑"; |
| | | // 确保 modalOptions.value 是对象 |
| | | if (!modalOptions.value || typeof modalOptions.value !== 'object') { |
| | | modalOptions.value = {}; |
| | | } |
| | | |
| | | // 根据操作类型和选中数据设置标题 |
| | | if (Array.isArray(selectedRows) && selectedRows.length > 1) { |
| | | // 批量操作 |
| | | modalOptions.value.title = type === "add" ? `批量新增 (${selectedRows.length}条)` : `批量编辑 (${selectedRows.length}条)`; |
| | | } else { |
| | | // 单个操作 - 明确判断 type 的值 |
| | | if (type === "add" || type === "新增") { |
| | | modalOptions.value.title = "新增"; |
| | | } else if (type === "edit" || type === "编辑") { |
| | | modalOptions.value.title = "编辑"; |
| | | } else { |
| | | modalOptions.value.title = "来票登记"; // 默认标题 |
| | | } |
| | | } |
| | | |
| | | visible.value = true; |
| | | |
| | | // 如果是单个操作,获取id |
| | | if (!Array.isArray(selectedRows) || selectedRows.length === 1) { |
| | |
| | | await getTableData(type, selectedRows); |
| | | }; |
| | | |
| | | const uploadSuccess = (response) => { |
| | | form.tempFileIds.push(response.data.tempId); |
| | | console.log(form); |
| | | }; |
| | | |
| | | const removeFile = (file) => { |
| | | const { tempId } = file.response.data; |
| | | form.tempFileIds = form.tempFileIds.filter((item) => item !== tempId); |
| | | }; |
| | | |
| | | const closeAndRefresh = () => { |
| | | closeModal(); |
| | |
| | | const batchData = selectedContracts.value.map(contract => { |
| | | // 筛选出属于当前合同的产品数据 |
| | | const contractProductData = form.productData.filter(item => |
| | | item.id === contract.id |
| | | item.purchaseLedgerId === contract.id |
| | | ); |
| | | |
| | | // 为每个采购合同创建独立的对象 |
| | | return { |
| | | // 基础表单数据 |
| | | invoiceNumber: form.invoiceNumber, |
| | | invoiceAmount: form.invoiceAmount, |
| | | entryDate: form.entryDate, |
| | | enterDate: form.enterDate, |
| | | issUerId: form.issUerId, // 录入人id |
| | | issUer: form.issUer, // 录入人 |
| | | tempFileIds: form.tempFileIds, |
| | | |
| | | // 合同实际信息 |
| | | purchaseLedgerId: contract.id, // 使用id作为字段名,值为purchaseLedgerId |
| | | // 基础表单数据 |
| | | invoiceNumber: form.invoiceNumber, |
| | | invoiceAmount: form.invoiceAmount, |
| | | entryDate: form.entryDate, |
| | | enterDate: form.enterDate, |
| | | issUerId: form.issUerId, // 录入人id |
| | | issUer: form.issUer, // 录入人 |
| | | |
| | | // 合同实际信息 |
| | | purchaseLedgerId: contract.id, // 使用id作为字段名,值为purchaseLedgerId |
| | | purchaseContractNumber: contract.purchaseContractNumber, // 使用实际的采购合同号 |
| | | salesContractNo: contract.salesContractNo, // 使用实际的销售合同号 |
| | | supplierName: contract.supplierName, // 使用实际的供应商名称 |
| | |
| | | // 单个合同提交逻辑 - 以数组格式传递 |
| | | const singleContract = selectedContracts.value[0]; |
| | | const singleFormArray = [{ |
| | | // 基础表单数据 |
| | | invoiceNumber: form.invoiceNumber, |
| | | invoiceAmount: form.invoiceAmount, |
| | | entryDate: form.entryDate, |
| | | enterDate: form.enterDate, |
| | | issUerId: form.issUerId, // 录入人id |
| | | issUer: form.issUer, // 录入人 |
| | | tempFileIds: form.tempFileIds, |
| | | |
| | | // 合同实际信息 |
| | | purchaseLedgerId: singleContract.id, // 使用id作为字段名,值为purchaseLedgerId |
| | | // 基础表单数据 |
| | | invoiceNumber: form.invoiceNumber, |
| | | invoiceAmount: form.invoiceAmount, |
| | | entryDate: form.entryDate, |
| | | enterDate: form.enterDate, |
| | | issUerId: form.issUerId, // 录入人id |
| | | issUer: form.issUer, // 录入人 |
| | | |
| | | // 合同实际信息 |
| | | purchaseLedgerId: singleContract.id, // 使用id作为字段名,值为purchaseLedgerId |
| | | purchaseContractNumber: singleContract.purchaseContractNumber, // 使用实际的采购合同号 |
| | | salesContractNo: singleContract.salesContractNo, // 使用实际的销售合同号 |
| | | supplierName: singleContract.supplierName, // 使用实际的供应商名称 |