1.1.28 开票信息通过金额生成单价,添加金额(含税) 可以修改、金额(不含税)列

dev
yinq 2 months ago
parent 564653e1e0
commit 306a55df29

@ -94,6 +94,11 @@ export interface ContractInfoVO {
*/
oneCustomerId: string | number;
/**
*
*/
oneCustomerName?: string;
/**
*
*/

@ -39,6 +39,11 @@ export interface FinInvoiceDetailVO {
*/
totalPrice: number;
/**
*
*/
totalPriceNoTax: number;
/**
* /13%
*/
@ -92,6 +97,11 @@ export interface FinInvoiceDetailForm extends BaseEntity {
*/
totalPrice?: number;
/**
*
*/
totalPriceNoTax?: number;
/**
* /13%
*/
@ -141,6 +151,11 @@ export interface FinInvoiceDetailQuery extends PageQuery {
*/
totalPrice?: number;
/**
*
*/
totalPriceNoTax?: number;
/**
* /13%
*/

@ -89,7 +89,7 @@ export function getErpFinInvoiceInfoList (query) {
*
* @param invoiceId
*/
export const getContractPaymentMethodList = (contractId: string | number): AxiosPromise<ContractPaymentMethodVO> => {
export const getContractPaymentMethodList = (contractId: string | number): AxiosPromise<ContractPaymentMethodVO[]> => {
return request({
url: '/oa/erp/finInvoiceInfo/getContractPaymentMethodList/' + contractId,
method: 'get'

@ -141,6 +141,11 @@ export interface FinInvoiceInfoVO {
*/
remark: string;
/**
* ID,
*/
ossId?: string;
/**
*
*/
@ -170,6 +175,21 @@ export interface FinInvoiceInfoVO {
*
*/
issueDate?: string;
/**
*
*/
contractCode?: string;
/**
* 1 2
*/
contractFlag?: string;
/**
*
*/
erpFinInvoiceDetailVoList?: Array<FinInvoiceDetailVO>;
}
export interface FinInvoiceInfoForm extends BaseEntity {
@ -313,6 +333,11 @@ export interface FinInvoiceInfoForm extends BaseEntity {
*/
remark?: string;
/**
* ID,
*/
ossId?: string;
/**
*
*/
@ -343,6 +368,16 @@ export interface FinInvoiceInfoForm extends BaseEntity {
*/
issueDate?: string;
/**
*
*/
contractCode?: string;
/**
* 1 2
*/
contractFlag?: string;
/**
*
*/

@ -181,7 +181,7 @@
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" v-if="form.contractTerritorialFlag === '2'">
<el-col :span="12" >
<el-form-item label="对人民币汇率" prop="rmbExchangeRate">
<el-input-number
v-model="form.rmbExchangeRate"
@ -191,7 +191,7 @@
controls-position="right"
placeholder="请输入对人民币汇率"
style="width: 100%"
:disabled="isFormDisabled"
:disabled="isFormDisabled || form.contractTerritorialFlag === '1'"
/>
</el-form-item>
</el-col>

@ -18,7 +18,7 @@
</el-card>
<el-card shadow="never" class="mb-[15px]" header="开票信息">
<el-form ref="formRef" :model="form" :rules="rules" label-width="130px" status-icon>
<el-form ref="formRef" :model="form" :rules="rules" label-width="130px" status-icon :disabled="isFormDisabled">
<el-row :gutter="24">
<!-- 第一行 -->
<el-col :span="8">
@ -166,7 +166,6 @@
prop="ossId"
v-if="routeParams.type === 'update' || routeParams.type === 'view' || routeParams.type === 'approval'"
>
<!-- <el-button type="primary" plain icon="Upload" @click="handleFile"></el-button>-->
<FileUpload
v-model="ossIdString"
:limit="5"
@ -186,15 +185,29 @@
<div class="flex justify-between items-center">
<span class="font-medium">开票明细</span>
<div>
<el-button type="primary" plain size="small" icon="Plus" @click="handleAddItem"></el-button>
<el-button type="danger" plain size="small" icon="Delete" @click="handleDeleteItems" :disabled="selectedItems.length === 0"
<el-button type="primary" plain size="small" icon="Plus" @click="handleAddItem" :disabled="isFormDisabled">新增明细</el-button>
<el-button
type="danger"
plain
size="small"
icon="Delete"
@click="handleDeleteItems"
:disabled="isFormDisabled || selectedItems.length === 0"
>删除
</el-button>
</div>
</div>
</template>
<el-table :data="form.erpFinInvoiceDetailList" border stripe @selection-change="handleSelectionChange" max-height="400">
<el-table
:data="form.erpFinInvoiceDetailList"
border
stripe
@selection-change="handleSelectionChange"
max-height="400"
:header-cell-style="{ textAlign: 'center' }"
:cell-style="{ textAlign: 'center' }"
>
<el-table-column type="selection" width="55" align="center" />
<el-table-column label="序号" type="index" width="60" align="center" />
<el-table-column label="开票内容" prop="billingItems" min-width="160">
@ -224,30 +237,53 @@
/>
</template>
</el-table-column>
<el-table-column label="税率(%)" prop="taxRate" width="145">
<el-table-column label="税率(%)" prop="taxRate" width="110">
<template #default="{ row, $index }">
<el-input-number v-model="row.taxRate" :min="0" :max="100" :precision="2" controls-position="right" style="width: 120px" />
<el-select
v-model="row.taxRate"
filterable
allow-create
default-first-option
clearable
placeholder="税率"
style="width: 80px"
@change="calculateItemAmount($index)"
>
<el-option v-for="rate in taxRateOptions" :key="rate" :label="`${rate}`" :value="rate" />
</el-select>
</template>
</el-table-column>
<el-table-column label="单价(含税)" prop="unitPrice" width="145">
<el-table-column label="单价(含税)" prop="unitPrice" width="100">
<template #default="{ row }">
<span class="text-red-500 font-medium">{{ formatAmount(row.unitPrice) }}</span>
</template>
</el-table-column>
<el-table-column label="金额(含税)" prop="totalPrice" width="145">
<template #default="{ row, $index }">
<el-input-number
v-model="row.unitPrice"
v-model="row.totalPrice"
:min="0"
:precision="2"
controls-position="right"
@change="calculateItemAmount($index)"
@change="calculateItemAmountByTotal($index)"
style="width: 120px"
/>
</template>
</el-table-column>
<el-table-column label="金额(含税)" prop="totalPrice" width="140">
<template #default="{ row }">
<span class="text-red-500 font-medium">{{ row.totalPrice?.toFixed(2) || '0.00' }}</span>
<el-table-column label="金额(不含税)" prop="totalPriceNoTax" width="150">
<template #default="{ row, $index }">
<el-input-number
v-model="row.totalPriceNoTax"
:min="0"
:precision="2"
controls-position="right"
@change="calculateItemAmountByNoTax($index)"
style="width: 120px"
/>
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="80" align="center">
<el-table-column v-if="!isFormDisabled" label="操作" fixed="right" width="80" align="center">
<template #default="{ row, $index }">
<el-button link type="danger" icon="Delete" @click="handleRemoveItem($index, row)" />
</template>
@ -311,7 +347,7 @@ import {
} from '@/api/oa/erp/finInvoiceInfo';
import { FinInvoiceInfoForm, FinInvoiceInfoQuery } from '@/api/oa/erp/finInvoiceInfo/types';
import ApprovalButton from '@/components/Process/approvalButton.vue';
import { FinInvoiceDetailVO } from '@/api/oa/erp/finInvoiceDetail/types';
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';
@ -350,13 +386,20 @@ const EARLY_FLAG = reactive({
NO: '0' //
});
const FLOW_STATUS = reactive({
DRAFT: 'draft',
WAITING: 'waiting'
});
const CONTRACT_FLAG = {
YES: '1',
NO: '2'
};
const hasInvoiceAttachPer = checkPermi(['oa/erp:finInvoiceInfo:invoiceAttach']);
console.log(hasInvoiceAttachPer);
const isFormDisabled = computed(() => {
return routeParams.value.type === 'view' || routeParams.value.type === 'approval' || form.value.flowStatus === FLOW_STATUS.WAITING;
});
//
const projectSelectDialogVisible = ref(false);
@ -367,11 +410,19 @@ const userDialogVisible = ref(false);
const selectedItems = ref<FinInvoiceDetailVO[]>([]);
const toDeletedInvoiceDetailIdList = ref([]);
const taxRateOptions = [0, 3, 9, 13];
const getTodayDateString = () => {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
const day = String(now.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
};
const initFormData: FinInvoiceInfoForm = {
invoiceId: undefined,
invoiceCode: undefined,
earlyFlag: undefined,
earlyFlag: EARLY_FLAG.NO,
invoiceType: undefined,
issueAmount: undefined,
issuancePercentage: undefined,
@ -398,12 +449,15 @@ const initFormData: FinInvoiceInfoForm = {
invoiceStatus: undefined,
remark: undefined,
earlyReason: undefined,
issueDate: getTodayDateString(),
erpFinInvoiceDetailList: []
};
const data = reactive<PageData<FinInvoiceInfoForm, FinInvoiceInfoQuery>>({
form: { ...initFormData },
queryParams: {
pageNum: undefined,
pageSize: undefined,
params: {}
},
rules: {
@ -434,16 +488,12 @@ watch(
// 使 ?? null undefined 'empty'
// ossId ()
if (isInitialized.value) {
if (isInitialized.value && form.value.invoiceId) {
const normalizedOld = oldVal ?? 'empty';
const normalizedNew = newVal ?? 'empty';
console.log(normalizedOld + '---' + normalizedNew);
if (normalizedOld !== normalizedNew) {
try {
buttonLoading.value = true;
console.log('附件发生变化,旧值:', oldVal, '新值:', newVal);
//
const invoiceAttachForm = {
invoiceId: form.value.invoiceId,
@ -460,13 +510,19 @@ watch(
);
const getUserList = async () => {
const query = {};
const query = {} as any;
const res = await api.getUserList(query);
userOptions.value = res.data;
};
const handleUserChange = async (newUserId) => {
const userOption = userOptions.value.find((item) => item.userId == newUserId);
if (!userOption) {
form.value.requestDeptName = undefined;
form.value.requestDept = undefined;
form.value.requestByName = undefined;
return;
}
form.value.requestDeptName = userOption.deptName;
form.value.requestDept = userOption.deptId;
form.value.requestByName = userOption.nickName;
@ -478,7 +534,7 @@ const getContractPaymentMethods = async (contractId) => {
};
const paymentDescription = computed(() => {
return contractPaymentMethodVoList.value.map((item) => `${item.paymentDescription}`).join('.'); // 使.
return contractPaymentMethodVoList.value.map((item) => `${item.paymentDescription}`).join('');
});
/** 查询部门下拉树结构 */
@ -503,7 +559,7 @@ const filterDisabledDept = (deptList: DeptTreeVO[]) => {
// - issueAmount issuancePercentage
const totalInvoiceAmount = computed(() => {
const total = form.value.erpFinInvoiceDetailList.reduce((sum, item) => sum + (item.totalPrice || 0), 0).toFixed(2);
const total = Number(form.value.erpFinInvoiceDetailList.reduce((sum, item) => sum + (item.totalPrice || 0), 0).toFixed(2));
//
form.value.issueAmount = total;
@ -583,21 +639,22 @@ const initByContractId = async () => {
const isInitialized = ref(false);
onMounted(async () => {
nextTick(async () => {
getUserList(); //
await getUserList(); //
//
routeParams.value = route.query;
console.log(route.query);
const id = routeParams.value.id as string | number;
try {
proxy?.$modal.loading('正在加载数据,请稍后...');
if (id && (routeParams.value.type === 'update' || routeParams.value.type === 'view' || routeParams.value.type === 'approval')) {
const res = await getFinInvoiceInfo(id);
console.log(res);
Object.assign(form.value, res.data);
Object.assign(form.value.erpFinInvoiceDetailList, res.data.erpFinInvoiceDetailVoList);
form.value.erpFinInvoiceDetailList = res.data.erpFinInvoiceDetailVoList || [];
form.value.erpFinInvoiceDetailList.forEach((_, index) => calculateItemAmountByTotal(index));
} 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();
}
@ -690,14 +747,18 @@ const handleUserSelected = () => {
//
const handleAddItem = () => {
form.value.erpFinInvoiceDetailList.push({
invoiceDetailId: undefined,
invoiceId: undefined,
billingItems: '',
specificationModel: '',
unitName: '',
quantity: undefined,
taxRate: undefined,
unitPrice: undefined,
totalPrice: 0
});
totalPrice: 0,
totalPriceNoTax: 0,
taxPrice: undefined
} as FinInvoiceDetailForm as FinInvoiceDetailVO);
};
const handleRemoveItem = (index: number, row: FinInvoiceDetailVO) => {
@ -725,14 +786,57 @@ const handleSelectionChange = (selection: FinInvoiceDetailVO[]) => {
selectedItems.value = selection;
};
//
const formatAmount = (value?: number) => {
return value !== undefined && value !== null ? Number(value).toFixed(2) : '0.00';
};
const roundAmount = (value?: number) => {
if (value === undefined || value === null || Number.isNaN(Number(value))) {
return undefined;
}
return Number(Number(value).toFixed(2));
};
const calculateTaxAmounts = (item: FinInvoiceDetailVO) => {
const totalPrice = roundAmount(item.totalPrice) || 0;
const taxRate = Number(item.taxRate || 0);
const divisor = 1 + taxRate / 100;
item.totalPriceNoTax = roundAmount(totalPrice / divisor) || 0;
item.taxPrice = roundAmount(totalPrice - (item.totalPriceNoTax || 0)) || 0;
};
// ()
const calculateItemAmount = (index: number) => {
const item = form.value.erpFinInvoiceDetailList[index];
if (item.quantity && item.unitPrice) {
item.totalPrice = Number((item.quantity * item.unitPrice).toFixed(2));
if (item.quantity && item.totalPrice !== undefined && item.totalPrice !== null) {
item.unitPrice = roundAmount(item.totalPrice / item.quantity);
} else {
item.totalPrice = 0;
item.unitPrice = undefined;
}
calculateTaxAmounts(item);
};
const calculateItemAmountByTotal = (index: number) => {
calculateItemAmount(index);
};
const calculateItemAmountByNoTax = (index: number) => {
const item = form.value.erpFinInvoiceDetailList[index];
const noTaxAmount = roundAmount(item.totalPriceNoTax);
if (noTaxAmount === undefined) {
item.totalPrice = 0;
item.unitPrice = undefined;
item.taxPrice = 0;
return;
}
const taxRate = Number(item.taxRate || 0);
item.totalPrice = roundAmount(noTaxAmount * (1 + taxRate / 100)) || 0;
if (item.quantity && item.quantity > 0) {
item.unitPrice = roundAmount((item.totalPrice || 0) / item.quantity);
} else {
item.unitPrice = undefined;
}
item.taxPrice = roundAmount((item.totalPrice || 0) - noTaxAmount) || 0;
};
//
@ -740,6 +844,50 @@ const handleItemChange = (index: number) => {
calculateItemAmount(index);
};
const buildInvoiceDetailError = (): string | null => {
if (!form.value.erpFinInvoiceDetailList.length) {
return '请至少添加一条开票明细';
}
for (let index = 0; index < form.value.erpFinInvoiceDetailList.length; index++) {
const item = form.value.erpFinInvoiceDetailList[index];
const rowNo = index + 1;
if (!item.billingItems) {
return `${rowNo}行开票内容不能为空`;
}
if (!item.unitName) {
return `${rowNo}行单位不能为空`;
}
if (item.taxRate === undefined || item.taxRate === null) {
return `${rowNo}行税率不能为空`;
}
if (!item.quantity || item.quantity <= 0) {
return `${rowNo}行数量必须大于0`;
}
if (!item.totalPrice || item.totalPrice <= 0) {
return `${rowNo}行金额(含税)必须大于0`;
}
}
return null;
};
const normalizeInvoiceForm = (): FinInvoiceInfoForm => {
const invoiceForm: FinInvoiceInfoForm = {
...form.value,
invoiceStatus: getInvoiceStatus(buttonStatus.value),
flowStatus: getFlowStatus(buttonStatus.value),
toDeletedInvoiceDetailIdList: [...toDeletedInvoiceDetailIdList.value]
};
if (invoiceForm.earlyFlag === EARLY_FLAG.YES) {
invoiceForm.acceptanceDate = undefined;
invoiceForm.deliveryDate = undefined;
} else {
invoiceForm.earlyReason = undefined;
}
return invoiceForm;
};
const buttonStatus = ref('draft');
//
const submitVerifyRef = ref<InstanceType<typeof SubmitVerify>>();
const approvalRecordRef = ref<InstanceType<typeof ApprovalRecord>>();
@ -763,59 +911,35 @@ const approvalVerifyOpen = async () => {
//
const handleSave = async (status: string, mode: boolean) => {
if (!formRef.value) return;
buttonStatus.value = status;
try {
await formRef.value.validate();
} catch {
ElMessage.warning('请先完善必填信息后再提交');
return;
}
const detailError = buildInvoiceDetailError();
if (detailError) {
ElMessage.warning(detailError);
return;
}
if (form.value.returnedMoney && form.value.totalPrice && form.value.returnedMoney > form.value.totalPrice) {
ElMessage.warning('累计回款金额不能大于合同金额');
return;
}
const invoiceForm = normalizeInvoiceForm();
try {
buttonLoading.value = true;
await formRef.value.validate(async (valid) => {
if (valid) {
if (form.value.erpFinInvoiceDetailList.length === 0) {
ElMessage.warning('请至少添加一条开票明细');
return;
}
//
const hasEmpty = form.value.erpFinInvoiceDetailList.some(
(item) => !item.billingItems || !item.unitName || !item.taxRate || item.quantity <= 0 || item.unitPrice <= 0
);
if (hasEmpty) {
ElMessage.warning('请填写完整的开票明细信息');
return;
}
//
if (form.value.returnedMoney && form.value.totalPrice && form.value.returnedMoney > form.value.totalPrice) {
ElMessage.warning('累计回款金额不能大于合同金额');
return;
}
if (form.value.earlyFlag === EARLY_FLAG.YES) {
form.value.acceptanceDate = undefined;
form.value.deliveryDate = undefined;
} else {
form.value.earlyReason = undefined;
}
const invoiceForm = {
...form.value,
//
invoiceStatus: getInvoiceStatus(status),
flowStatus: getFlowStatus(status),
};
//
if (form.value.invoiceId) {
form.value.toDeletedInvoiceDetailIdList = toDeletedInvoiceDetailIdList.value;
await updateFinInvoiceInfo(invoiceForm);
} else {
await addFinInvoiceInfo(invoiceForm);
}
ElMessage.success('保存成功');
//
goBack();
}
});
if (form.value.invoiceId) {
await updateFinInvoiceInfo(invoiceForm);
} else {
await addFinInvoiceInfo(invoiceForm);
}
ElMessage.success(status === 'draft' ? '暂存成功' : '提交成功');
goBack();
} finally {
buttonLoading.value = false;
}

Loading…
Cancel
Save