feat: payment view
This commit is contained in:
parent
639dc36d71
commit
d9f8d0f971
4 changed files with 53 additions and 76 deletions
|
|
@ -3,27 +3,25 @@ import { baseUrl } from 'stores/utils';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { useConfigStore } from 'stores/config';
|
import { useConfigStore } from 'stores/config';
|
||||||
import { formatNumberDecimal } from 'stores/utils';
|
import { formatNumberDecimal } from 'stores/utils';
|
||||||
import DialogForm from 'src/components/DialogForm.vue';
|
import { reactive, ref } from 'vue';
|
||||||
import { reactive, ref, watch } from 'vue';
|
|
||||||
import { useQuotationPayment } from 'src/stores/quotations';
|
import { useQuotationPayment } from 'src/stores/quotations';
|
||||||
import {
|
import {
|
||||||
PaymentPayload,
|
PaymentPayload,
|
||||||
Quotation,
|
Quotation,
|
||||||
|
QuotationFull,
|
||||||
QuotationPaymentData,
|
QuotationPaymentData,
|
||||||
} from 'src/stores/quotations/types';
|
} from 'src/stores/quotations/types';
|
||||||
import { dateFormat } from 'src/utils/datetime';
|
import { dateFormat } from 'src/utils/datetime';
|
||||||
import { QFile, QMenu } from 'quasar';
|
import { QFile, QMenu } from 'quasar';
|
||||||
import UploadFileCard from 'src/components/upload-file/UploadFileCard.vue';
|
import UploadFileCard from 'src/components/upload-file/UploadFileCard.vue';
|
||||||
|
import { onMounted } from 'vue';
|
||||||
|
|
||||||
const configStore = useConfigStore();
|
const configStore = useConfigStore();
|
||||||
const quotationPayment = useQuotationPayment();
|
const quotationPayment = useQuotationPayment();
|
||||||
const { data: config } = storeToRefs(configStore);
|
const { data: config } = storeToRefs(configStore);
|
||||||
|
|
||||||
const model = defineModel<boolean>({ default: false, required: true });
|
const prop = defineProps<{ data?: Quotation | QuotationFull }>();
|
||||||
const data = defineModel<Quotation | undefined>('data', { required: true });
|
|
||||||
|
|
||||||
const fileManager =
|
|
||||||
ref<ReturnType<typeof quotationPayment.createPaymentFileManager>>();
|
|
||||||
const refQFile = ref<InstanceType<typeof QFile>[]>([]);
|
const refQFile = ref<InstanceType<typeof QFile>[]>([]);
|
||||||
const refQMenu = ref<InstanceType<typeof QMenu>[]>([]);
|
const refQMenu = ref<InstanceType<typeof QMenu>[]>([]);
|
||||||
const paymentData = ref<QuotationPaymentData[]>([]);
|
const paymentData = ref<QuotationPaymentData[]>([]);
|
||||||
|
|
@ -51,7 +49,6 @@ const paymentStatusOpts = [
|
||||||
];
|
];
|
||||||
const slipFile = ref<
|
const slipFile = ref<
|
||||||
{
|
{
|
||||||
quotationId: string;
|
|
||||||
paymentId: string;
|
paymentId: string;
|
||||||
data: { name: string; progress: number; loaded: number; total: number }[];
|
data: { name: string; progress: number; loaded: number; total: number }[];
|
||||||
file?: File;
|
file?: File;
|
||||||
|
|
@ -74,8 +71,7 @@ function pickFile(index: number) {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function getSlipList(payment: QuotationPaymentData, index: number) {
|
async function getSlipList(payment: QuotationPaymentData, index: number) {
|
||||||
if (!fileManager.value) return;
|
const slipList = await quotationPayment.listAttachment({
|
||||||
const slipList = await fileManager.value.listAttachment({
|
|
||||||
parentId: payment.id,
|
parentId: payment.id,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -106,19 +102,18 @@ async function triggerUpload(
|
||||||
index: number,
|
index: number,
|
||||||
file?: File,
|
file?: File,
|
||||||
) {
|
) {
|
||||||
if (!data.value || !fileManager.value || !file) return;
|
if (!file) return;
|
||||||
slipFile.value[index].data.push({
|
slipFile.value[index].data.push({
|
||||||
name: file.name,
|
name: file.name,
|
||||||
progress: 0,
|
progress: 0,
|
||||||
loaded: 0,
|
loaded: 0,
|
||||||
total: 0,
|
total: 0,
|
||||||
});
|
});
|
||||||
const ret = await fileManager.value.putAttachment({
|
const ret = await quotationPayment.putAttachment({
|
||||||
parentId: payment.id,
|
parentId: payment.id,
|
||||||
name: file.name,
|
name: file.name,
|
||||||
file: file,
|
file: file,
|
||||||
onUploadProgress: (e) => {
|
onUploadProgress: (e) => {
|
||||||
console.log(e);
|
|
||||||
slipFile.value[index].data[slipFile.value[index].data.length - 1] = {
|
slipFile.value[index].data[slipFile.value[index].data.length - 1] = {
|
||||||
name: file.name,
|
name: file.name,
|
||||||
progress: e.progress || 0,
|
progress: e.progress || 0,
|
||||||
|
|
@ -136,14 +131,12 @@ async function triggerDelete(
|
||||||
name: string,
|
name: string,
|
||||||
index: number,
|
index: number,
|
||||||
) {
|
) {
|
||||||
if (!data.value || !fileManager.value) return;
|
await quotationPayment.delAttachment({ parentId: payment.id, name });
|
||||||
await fileManager.value.delAttachment({ parentId: payment.id, name });
|
|
||||||
await getSlipList(payment, index);
|
await getSlipList(payment, index);
|
||||||
}
|
}
|
||||||
|
|
||||||
function triggerViewSlip(payment: QuotationPaymentData, name: string) {
|
function triggerViewSlip(payment: QuotationPaymentData, name: string) {
|
||||||
if (!data.value || !fileManager.value) return;
|
const url = `${baseUrl}/payment/${payment.id}/attachment/${name}`;
|
||||||
const url = `${baseUrl}/quotation/${data.value.id}/payment/${payment.id}/attachment/${name}`;
|
|
||||||
window.open(url, '_blank');
|
window.open(url, '_blank');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -168,60 +161,36 @@ function selectStatus(
|
||||||
|
|
||||||
async function triggerSubmit() {
|
async function triggerSubmit() {
|
||||||
submitPaymentData.value.forEach(async (p) => {
|
submitPaymentData.value.forEach(async (p) => {
|
||||||
if (!data.value) return;
|
|
||||||
|
|
||||||
const payload: PaymentPayload = {
|
const payload: PaymentPayload = {
|
||||||
paymentStatus: p.paymentStatus,
|
paymentStatus: p.paymentStatus,
|
||||||
remark: p.remark || '',
|
|
||||||
date: new Date(p.date),
|
date: new Date(p.date),
|
||||||
amount: p.amount,
|
amount: p.amount,
|
||||||
};
|
};
|
||||||
await quotationPayment.updateQuotationPayment(data.value.id, p.id, payload);
|
await quotationPayment.updateQuotationPayment(p.id, payload);
|
||||||
});
|
});
|
||||||
model.value = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
watch(
|
onMounted(async () => {
|
||||||
() => model.value,
|
if (!prop.data) return;
|
||||||
async (open) => {
|
const ret = await quotationPayment.getQuotationPayment(prop.data.id);
|
||||||
if (!data.value) return;
|
if (ret) {
|
||||||
if (!open) {
|
paymentData.value = ret.result;
|
||||||
paymentData.value = [];
|
slipFile.value = paymentData.value.map((v) => ({
|
||||||
state.payExpansion = [];
|
paymentId: v.id,
|
||||||
slipFile.value = [];
|
data: [],
|
||||||
submitPaymentData.value = [];
|
}));
|
||||||
} else {
|
}
|
||||||
fileManager.value = quotationPayment.createPaymentFileManager(
|
});
|
||||||
data.value.id,
|
|
||||||
);
|
|
||||||
const ret = await quotationPayment.getQuotationPayment(data.value.id);
|
|
||||||
if (ret) {
|
|
||||||
paymentData.value = ret.quotationPaymentData;
|
|
||||||
slipFile.value = paymentData.value.map((v) => ({
|
|
||||||
quotationId: ret.id,
|
|
||||||
paymentId: v.id,
|
|
||||||
data: [],
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<DialogForm
|
<q-form
|
||||||
:title="$t('quotation.receipt')"
|
greedy
|
||||||
v-model:modal="model"
|
@submit.prevent
|
||||||
width="65%"
|
@validation-success="triggerSubmit"
|
||||||
:submit="() => triggerSubmit()"
|
class="column full-height"
|
||||||
|
v-if="data"
|
||||||
>
|
>
|
||||||
<div
|
<div class="col column no-wrap">
|
||||||
v-if="data"
|
|
||||||
class="col column no-wrap"
|
|
||||||
:class="{
|
|
||||||
'q-mx-lg q-my-md': $q.screen.gt.sm,
|
|
||||||
'q-mx-md q-my-sm': !$q.screen.gt.sm,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<!-- PRICE DETAIL -->
|
<!-- PRICE DETAIL -->
|
||||||
<section class="bordered rounded surface-1" style="overflow: hidden">
|
<section class="bordered rounded surface-1" style="overflow: hidden">
|
||||||
<q-expansion-item
|
<q-expansion-item
|
||||||
|
|
@ -662,7 +631,7 @@ watch(
|
||||||
</section>
|
</section>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</DialogForm>
|
</q-form>
|
||||||
</template>
|
</template>
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.bg-color-orange {
|
.bg-color-orange {
|
||||||
|
|
@ -67,6 +67,7 @@ import { precisionRound } from 'src/utils/arithmetic';
|
||||||
import { useConfigStore } from 'src/stores/config';
|
import { useConfigStore } from 'src/stores/config';
|
||||||
import QuotationFormMetadata from './QuotationFormMetadata.vue';
|
import QuotationFormMetadata from './QuotationFormMetadata.vue';
|
||||||
import BadgeComponent from 'src/components/BadgeComponent.vue';
|
import BadgeComponent from 'src/components/BadgeComponent.vue';
|
||||||
|
import PaymentForm from './PaymentForm.vue';
|
||||||
|
|
||||||
// defineProps<{
|
// defineProps<{
|
||||||
// readonly?: boolean;
|
// readonly?: boolean;
|
||||||
|
|
@ -905,7 +906,15 @@ const view = ref<View>(View.Quotation);
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<template v-if="true">
|
<template
|
||||||
|
v-if="
|
||||||
|
view === View.Quotation ||
|
||||||
|
view === View.Accepted ||
|
||||||
|
view === View.Invoice ||
|
||||||
|
view === View.Payment ||
|
||||||
|
view === View.Receipt
|
||||||
|
"
|
||||||
|
>
|
||||||
<q-expansion-item
|
<q-expansion-item
|
||||||
for="item-up"
|
for="item-up"
|
||||||
id="item-up"
|
id="item-up"
|
||||||
|
|
@ -1204,6 +1213,9 @@ const view = ref<View>(View.Quotation);
|
||||||
</div>
|
</div>
|
||||||
</q-expansion-item>
|
</q-expansion-item>
|
||||||
</template>
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<PaymentForm :data="quotationFormState.source" />
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
</article>
|
</article>
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ import {
|
||||||
QuotationStatus,
|
QuotationStatus,
|
||||||
} from './types';
|
} from './types';
|
||||||
import { PaginationResult } from 'src/types';
|
import { PaginationResult } from 'src/types';
|
||||||
import { AxiosProgressEvent } from 'axios';
|
|
||||||
|
|
||||||
import { manageAttachment } from '../utils';
|
import { manageAttachment } from '../utils';
|
||||||
|
|
||||||
|
|
@ -140,9 +139,9 @@ export const useQuotationStore = defineStore('quotation-store', () => {
|
||||||
|
|
||||||
export const useQuotationPayment = defineStore('quotation-payment', () => {
|
export const useQuotationPayment = defineStore('quotation-payment', () => {
|
||||||
async function getQuotationPayment(quotationId: string) {
|
async function getQuotationPayment(quotationId: string) {
|
||||||
const res = await api.get<
|
const res = await api.get<PaginationResult<QuotationPaymentData>>(
|
||||||
Quotation & { quotationPaymentData: QuotationPaymentData[] }
|
`/payment/${quotationId}`,
|
||||||
>(`/quotation/${quotationId}/payment`);
|
);
|
||||||
if (res.status < 400) {
|
if (res.status < 400) {
|
||||||
return res.data;
|
return res.data;
|
||||||
}
|
}
|
||||||
|
|
@ -150,12 +149,11 @@ export const useQuotationPayment = defineStore('quotation-payment', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function updateQuotationPayment(
|
async function updateQuotationPayment(
|
||||||
quotationId: string,
|
|
||||||
paymentId: string,
|
paymentId: string,
|
||||||
payload: PaymentPayload,
|
payload: PaymentPayload,
|
||||||
) {
|
) {
|
||||||
const res = await api.put<PaymentPayload & { id: string }>(
|
const res = await api.put<PaymentPayload & { id: string }>(
|
||||||
`/quotation/${quotationId}/payment/${paymentId}`,
|
`/payment/${paymentId}`,
|
||||||
payload,
|
payload,
|
||||||
);
|
);
|
||||||
if (res.status < 400) {
|
if (res.status < 400) {
|
||||||
|
|
@ -164,16 +162,12 @@ export const useQuotationPayment = defineStore('quotation-payment', () => {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createPaymentFileManager(
|
const fileManager = manageAttachment(api, `payment`);
|
||||||
quotationId: string,
|
|
||||||
opts?: { onUploadProgress: (e: AxiosProgressEvent) => void },
|
|
||||||
) {
|
|
||||||
return manageAttachment(api, `quotation/${quotationId}/payment`, opts);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getQuotationPayment,
|
getQuotationPayment,
|
||||||
createPaymentFileManager,
|
|
||||||
updateQuotationPayment,
|
updateQuotationPayment,
|
||||||
|
|
||||||
|
...fileManager,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -270,6 +270,8 @@ export type QuotationFull = {
|
||||||
id: string;
|
id: string;
|
||||||
finalPrice: number;
|
finalPrice: number;
|
||||||
vat: number;
|
vat: number;
|
||||||
|
vatExcluded: number;
|
||||||
|
discount: number;
|
||||||
totalDiscount: number;
|
totalDiscount: number;
|
||||||
totalPrice: number;
|
totalPrice: number;
|
||||||
urgent: boolean;
|
urgent: boolean;
|
||||||
|
|
@ -283,6 +285,7 @@ export type QuotationFull = {
|
||||||
contactTel: string;
|
contactTel: string;
|
||||||
contactName: string;
|
contactName: string;
|
||||||
workName: string;
|
workName: string;
|
||||||
|
workerMax: number | null;
|
||||||
code: string;
|
code: string;
|
||||||
statusOrder: number;
|
statusOrder: number;
|
||||||
status: Status;
|
status: Status;
|
||||||
|
|
@ -297,6 +300,7 @@ export type QuotationFull = {
|
||||||
| 'Canceled';
|
| 'Canceled';
|
||||||
|
|
||||||
customerBranchId: string;
|
customerBranchId: string;
|
||||||
|
customerBranch: CustomerBranchRelation;
|
||||||
registeredBranchId: string;
|
registeredBranchId: string;
|
||||||
registeredBranch: { id: string; name: string };
|
registeredBranch: { id: string; name: string };
|
||||||
|
|
||||||
|
|
@ -372,7 +376,6 @@ export type ProductGroup = {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type QuotationPaymentData = {
|
export type QuotationPaymentData = {
|
||||||
quotationId: string;
|
|
||||||
paymentStatus: string;
|
paymentStatus: string;
|
||||||
amount: number;
|
amount: number;
|
||||||
remark: string;
|
remark: string;
|
||||||
|
|
@ -393,7 +396,6 @@ export type Details = {
|
||||||
|
|
||||||
export type PaymentPayload = {
|
export type PaymentPayload = {
|
||||||
paymentStatus: string;
|
paymentStatus: string;
|
||||||
remark: string;
|
|
||||||
date: Date;
|
date: Date;
|
||||||
amount: number;
|
amount: number;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue