diff --git a/src/api/oa/erp/finInvoiceInfo/index.ts b/src/api/oa/erp/finInvoiceInfo/index.ts index 62c6460..8e6c90a 100644 --- a/src/api/oa/erp/finInvoiceInfo/index.ts +++ b/src/api/oa/erp/finInvoiceInfo/index.ts @@ -96,6 +96,20 @@ export const getContractPaymentMethodList = (contractId: string | number): Axios }); }; +/** + * 按合同查询累计回款比例最大的开票单累计回款金额 + */ +export const getMaxReturnedMoneyByContractId = ( + contractId: string | number, + excludeInvoiceId?: string | number +): AxiosPromise => { + return request({ + url: '/oa/erp/finInvoiceInfo/getMaxReturnedMoneyByContractId/' + contractId, + method: 'get', + params: { excludeInvoiceId } + }); +}; + /** * 新增开票信息 * @param data diff --git a/src/views/oa/erp/finInvoiceInfo/edit.vue b/src/views/oa/erp/finInvoiceInfo/edit.vue index 15ec265..c539f3b 100644 --- a/src/views/oa/erp/finInvoiceInfo/edit.vue +++ b/src/views/oa/erp/finInvoiceInfo/edit.vue @@ -116,7 +116,14 @@ - + @@ -321,15 +328,6 @@ - - -
请根据实际项目集成部门选择组件
- -
-
请根据实际项目集成人员选择组件
@@ -358,14 +356,14 @@ import { addFinInvoiceInfo, updateFinInvoiceInfo, getContractPaymentMethodList, - updateInvoiceAttach + updateInvoiceAttach, + getMaxReturnedMoneyByContractId } from '@/api/oa/erp/finInvoiceInfo'; import { FinInvoiceInfoForm, FinInvoiceInfoQuery } from '@/api/oa/erp/finInvoiceInfo/types'; import ApprovalButton from '@/components/Process/approvalButton.vue'; import { FinInvoiceDetailForm, FinInvoiceDetailVO } from '@/api/oa/erp/finInvoiceDetail/types'; import SubmitVerify from '@/components/Process/submitVerify.vue'; import ApprovalRecord from '@/components/Process/approvalRecord.vue'; -import { DeptTreeVO } from '@/api/system/dept/types'; import { ContractPaymentMethodVO } from '@/api/oa/erp/contractPaymentMethod/types'; import { UserVO } from '@/api/system/user/types'; @@ -376,9 +374,9 @@ import { getContractInfo } from '@/api/oa/erp/contractInfo'; import { getProjectInfo, listProjectInfoByContractId } from '@/api/oa/erp/projectInfo'; const userOptions = ref([]); -const deptOptions = ref([]); -const enabledDeptOptions = ref([]); const contractPaymentMethodVoList = ref([]); +/** 同合同下历史开票单(累计回款比例最大)的累计回款金额 */ +const contractBaseReturnedMoney = ref(0); const route = useRoute(); const router = useRouter(); @@ -437,7 +435,6 @@ const isFormDisabled = computed(() => { // 弹窗可见性 const projectSelectDialogVisible = ref(false); -const deptDialogVisible = ref(false); const userDialogVisible = ref(false); // 表格选中项 @@ -563,10 +560,10 @@ const handleUserChange = async (newUserId) => { form.value.requestByName = userOption.nickName; }; -const getContractPaymentMethods = async (contractId) => { +const getContractPaymentMethods = async (contractId?: string | number) => { if (!contractId) { contractPaymentMethodVoList.value = []; - return; + return null; } const [pmRes, contractRes] = await Promise.all([ getContractPaymentMethodList(contractId), @@ -577,32 +574,13 @@ const getContractPaymentMethods = async (contractId) => { if (c?.contractCode) { form.value.contractCode = c.contractCode; } + return c; }; const paymentDescription = computed(() => { return contractPaymentMethodVoList.value.map((item) => `${item.paymentDescription}`).join(''); }); -/** 查询部门下拉树结构 */ -const getDeptTree = async () => { - const res = await api.deptTreeSelect(); - deptOptions.value = res.data; - enabledDeptOptions.value = filterDisabledDept(res.data); -}; - -/** 过滤禁用的部门 */ -const filterDisabledDept = (deptList: DeptTreeVO[]) => { - return deptList.filter((dept) => { - if (dept.disabled) { - return false; - } - if (dept.children && dept.children.length) { - dept.children = filterDisabledDept(dept.children); - } - return true; - }); -}; - // 计算合计金额 - 自动更新 issueAmount 和 issuancePercentage const totalInvoiceAmount = computed(() => { const detailList = form.value.erpFinInvoiceDetailList || []; @@ -636,24 +614,61 @@ watch( } ); -// 监听累计回款金额变化 +// 监听本次开票合计金额变化,自动重算累计回款金额 watch( - () => form.value.returnedMoney, - (newReturnedMoney) => { - // 实时计算回款比例 - calculateReturnedRate(); + () => form.value.issueAmount, + () => { + syncReturnedMoney(); + } +); + +watch( + () => form.value.contractId, + (newVal, oldVal) => { + if (newVal !== oldVal && !isFormDisabled.value) { + fetchContractBaseReturnedMoney(); + } } ); // 计算回款比例 const calculateReturnedRate = () => { - if (form.value.totalPrice && form.value.totalPrice > 0 && form.value.returnedMoney) { + if (form.value.totalPrice && form.value.totalPrice > 0 && form.value.returnedMoney != null) { form.value.returnedRate = Number(((form.value.returnedMoney / form.value.totalPrice) * 100).toFixed(2)); } else { form.value.returnedRate = undefined; } }; +/** 查询同合同下累计回款比例最大的历史累计回款金额 */ +const fetchContractBaseReturnedMoney = async () => { + if (isFormDisabled.value) { + return; + } + if (!form.value.contractId) { + contractBaseReturnedMoney.value = 0; + syncReturnedMoney(); + return; + } + try { + const res = await getMaxReturnedMoneyByContractId(form.value.contractId, form.value.invoiceId); + contractBaseReturnedMoney.value = Number(res.data ?? 0); + } catch { + contractBaseReturnedMoney.value = 0; + } + syncReturnedMoney(); +}; + +/** 累计回款金额 = 历史最大累计回款金额 + 本次开票合计金额(含税) */ +const syncReturnedMoney = () => { + if (isFormDisabled.value) { + return; + } + const currentAmount = Number(form.value.issueAmount ?? 0); + form.value.returnedMoney = Number((contractBaseReturnedMoney.value + currentAmount).toFixed(2)); + calculateReturnedRate(); +}; + const syncManagerIdFromProject = async (projectId?: string | number) => { if (!projectId) { form.value.managerId = undefined; @@ -690,6 +705,7 @@ const initByContractId = async () => { }); await getContractPaymentMethods(contractId); + initInvoiceDetailsFromContract((contract as any)?.contractMaterialList || []); }; @@ -729,13 +745,21 @@ onMounted(async () => { if (form.value.projectId) { await syncManagerIdFromProject(form.value.projectId); } + if (form.value.contractId && !isFormDisabled.value) { + await fetchContractBaseReturnedMoney(); + } } else { const res = await getBaseInfo(); Object.assign(form.value, res.data); form.value.earlyFlag = form.value.earlyFlag || EARLY_FLAG.NO; form.value.issueDate = form.value.issueDate || getTodayDateString(); await initByContractId(); - handleAddItem(); + if (!form.value.erpFinInvoiceDetailList?.length) { + handleAddItem(); + } + if (form.value.contractId) { + await fetchContractBaseReturnedMoney(); + } } // 数据加载完成后,设置初始化标志为 true await nextTick(); @@ -762,10 +786,11 @@ const handleSelectProject = () => { }; // 处理项目选择 -const handleProjectSelected = (result: any) => { +const handleProjectSelected = async (result: any) => { const project = result.project; + const contractId = result.contract?.contractId; Object.assign(form.value, { - contractId: result.contract?.contractId, + contractId, contractFlag: project.contractFlag, projectId: project.projectId, projectName: project.projectName, @@ -776,8 +801,14 @@ const handleProjectSelected = (result: any) => { customerName: result.contract?.oneCustomerName, totalPrice: result.contract?.totalPrice }); - if (project.contractFlag === CONTRACT_FLAG.YES && result.contract?.contractId) { - getContractPaymentMethods(result.contract?.contractId); + if (contractId) { + const contractDetail = await getContractPaymentMethods(contractId); + initInvoiceDetailsFromContract((contractDetail as any)?.contractMaterialList || []); + } else if (isAddMode()) { + form.value.erpFinInvoiceDetailList = []; + contractBaseReturnedMoney.value = 0; + handleAddItem(); + syncReturnedMoney(); } }; @@ -790,7 +821,7 @@ const showContractSelectDialog = () => { }; // 处理合同选择 -const handleContractSelected = (contract: any) => { +const handleContractSelected = async (contract: any) => { Object.assign(form.value, { contractId: contract.contractId, contractCode: contract.contractCode, @@ -799,18 +830,8 @@ const handleContractSelected = (contract: any) => { totalPrice: contract.totalPrice }); - getContractPaymentMethods(contract.contractId); -}; - -// 部门选择 -const handleSelectDept = () => { - deptDialogVisible.value = true; -}; - -const handleDeptSelected = () => { - // form.deptName = '财务部'; // 模拟选择 - // form.deptId = 1; - deptDialogVisible.value = false; + const contractDetail = await getContractPaymentMethods(contract.contractId); + initInvoiceDetailsFromContract((contractDetail as any)?.contractMaterialList || []); }; // 人员选择 @@ -904,6 +925,55 @@ const calculateTaxAmounts = (item: FinInvoiceDetailVO) => { item.taxPrice = roundAmount(totalPrice - (item.totalPriceNoTax || 0)) || 0; }; +const isAddMode = () => { + const type = routeParams.value.type; + return !routeParams.value.id && type !== 'update' && type !== 'view' && type !== 'approval'; +}; + +/** 合同物料 → 开票明细字段映射 */ +const mapContractMaterialToInvoiceDetail = (material: Record): FinInvoiceDetailVO => { + const quantity = toFormNumber(material.amount) ?? 0; + const unitPrice = toFormNumber(material.includingPrice); + const taxRate = toFormNumber(material.taxRate); + let totalPrice = toFormNumber(material.subtotal); + if ((totalPrice === undefined || totalPrice <= 0) && unitPrice !== undefined && quantity > 0) { + totalPrice = roundAmount(unitPrice * quantity) ?? 0; + } + const detail = { + invoiceDetailId: undefined, + invoiceId: undefined, + billingItems: material.saleMaterialName || material.materialName || material.productName || '', + specificationModel: material.specificationDescription || '', + unitName: material.unitName || '', + quantity, + taxRate, + unitPrice, + totalPrice: totalPrice ?? 0, + totalPriceNoTax: 0, + taxPrice: undefined + } as FinInvoiceDetailVO; + if (detail.totalPrice != null && detail.taxRate != null) { + calculateTaxAmounts(detail); + } else if (detail.totalPrice != null && detail.unitPrice == null && quantity > 0) { + detail.unitPrice = roundAmount(detail.totalPrice / quantity); + } + return detail; +}; + +/** 新增开票:用合同物料初始化开票明细 */ +const initInvoiceDetailsFromContract = (contractMaterialList: Record[]) => { + if (!isAddMode()) { + return; + } + const details = (contractMaterialList || []).map(mapContractMaterialToInvoiceDetail); + if (details.length > 0) { + form.value.erpFinInvoiceDetailList = details; + } else { + form.value.erpFinInvoiceDetailList = []; + handleAddItem(); + } +}; + // 通过数量和金额(含税)回算单价 const calculateItemAmount = (index: number) => { const item = form.value.erpFinInvoiceDetailList[index]; @@ -1039,6 +1109,11 @@ const handleSave = async (status: string, mode: boolean) => { return; } + if (form.value.returnedRate != null && Number(form.value.returnedRate) > 100) { + ElMessage.warning('累计回款比例不能超过100%'); + return; + } + if (form.value.returnedMoney && form.value.totalPrice && form.value.returnedMoney > form.value.totalPrice) { ElMessage.warning('累计回款金额不能大于合同金额'); return;