1.1.67 新增开票时合同物料自动映射到开票明细,开票页面实时更新累计回款金额与比例。

dev
yinq 1 day ago
parent 57b270df36
commit 73c3319512

@ -96,6 +96,20 @@ export const getContractPaymentMethodList = (contractId: string | number): Axios
});
};
/**
*
*/
export const getMaxReturnedMoneyByContractId = (
contractId: string | number,
excludeInvoiceId?: string | number
): AxiosPromise<number> => {
return request({
url: '/oa/erp/finInvoiceInfo/getMaxReturnedMoneyByContractId/' + contractId,
method: 'get',
params: { excludeInvoiceId }
});
};
/**
*
* @param data

@ -116,7 +116,14 @@
</el-col>
<el-col :span="8">
<el-form-item label="累计回款金额" prop="returnedMoney">
<el-input-number v-model="form.returnedMoney" :precision="2" placeholder="请输入累计回款金额" style="width: 210px"></el-input-number>
<el-input-number
v-model="form.returnedMoney"
:precision="2"
:controls="false"
disabled
placeholder="累计回款金额"
style="width: 210px"
/>
</el-form-item>
</el-col>
<el-col :span="8">
@ -321,15 +328,6 @@
<!-- 合同选择弹窗组件 -->
<ContractSelectDialog v-model:visible="contractSelectDialogVisible" @contract-selected="handleContractSelected" />
<!-- 部门选择弹窗 (可根据实际组件调整) -->
<el-dialog v-model="deptDialogVisible" title="选择部门" width="600px" append-to-body>
<div class="p-3 text-center text-gray-500">请根据实际项目集成部门选择组件</div>
<template #footer>
<el-button @click="deptDialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleDeptSelected"></el-button>
</template>
</el-dialog>
<!-- 人员选择弹窗 (可根据实际组件调整) -->
<el-dialog v-model="userDialogVisible" title="选择人员" width="600px" append-to-body>
<div class="p-3 text-center text-gray-500">请根据实际项目集成人员选择组件</div>
@ -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<UserVO[]>([]);
const deptOptions = ref<DeptTreeVO[]>([]);
const enabledDeptOptions = ref<DeptTreeVO[]>([]);
const contractPaymentMethodVoList = ref<ContractPaymentMethodVO[]>([]);
/** 同合同下历史开票单(累计回款比例最大)的累计回款金额 */
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<string, any>): 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<string, any>[]) => {
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;

Loading…
Cancel
Save