Merge pull request #33 from Frappet/feat/custom-installments
feat: custom installment
This commit is contained in:
commit
3cace45c5e
6 changed files with 70 additions and 18 deletions
|
|
@ -13,6 +13,7 @@ import { useConfigStore } from 'stores/config';
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
agentPrice: boolean;
|
agentPrice: boolean;
|
||||||
|
installmentInput?: boolean;
|
||||||
employeeRows?: {
|
employeeRows?: {
|
||||||
foreignRefNo: string;
|
foreignRefNo: string;
|
||||||
employeeName: string;
|
employeeName: string;
|
||||||
|
|
@ -228,7 +229,20 @@ watch(
|
||||||
flat
|
flat
|
||||||
bordered
|
bordered
|
||||||
hide-pagination
|
hide-pagination
|
||||||
:columns="columns"
|
:columns="
|
||||||
|
installmentInput
|
||||||
|
? [
|
||||||
|
...columns.slice(0, 1),
|
||||||
|
{
|
||||||
|
name: 'periodNo',
|
||||||
|
align: 'left',
|
||||||
|
label: 'quotation.periodNo',
|
||||||
|
field: (v) => v.product.code,
|
||||||
|
},
|
||||||
|
...columns.slice(1),
|
||||||
|
]
|
||||||
|
: columns
|
||||||
|
"
|
||||||
:rows="item.product"
|
:rows="item.product"
|
||||||
class="full-width"
|
class="full-width"
|
||||||
:no-data-label="$t('general.noDataTable')"
|
:no-data-label="$t('general.noDataTable')"
|
||||||
|
|
@ -248,6 +262,19 @@ watch(
|
||||||
<template #body="props">
|
<template #body="props">
|
||||||
<q-tr>
|
<q-tr>
|
||||||
<q-td class="text-center">{{ props.rowIndex + 1 }}</q-td>
|
<q-td class="text-center">{{ props.rowIndex + 1 }}</q-td>
|
||||||
|
<q-td v-if="installmentInput">
|
||||||
|
<q-input
|
||||||
|
v-model="props.row.installmentNo"
|
||||||
|
:readonly
|
||||||
|
:bg-color="readonly ? 'transparent' : ''"
|
||||||
|
dense
|
||||||
|
min="0"
|
||||||
|
outlined
|
||||||
|
input-class="text-right"
|
||||||
|
type="number"
|
||||||
|
style="width: 60px"
|
||||||
|
></q-input>
|
||||||
|
</q-td>
|
||||||
<q-td>{{ props.row.product.code }}</q-td>
|
<q-td>{{ props.row.product.code }}</q-td>
|
||||||
<q-td style="width: 100%">
|
<q-td style="width: 100%">
|
||||||
<q-avatar class="q-mr-sm" size="md">
|
<q-avatar class="q-mr-sm" size="md">
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,7 @@ const { data: config } = storeToRefs(configStore);
|
||||||
const refSelectZoneEmployee = ref<InstanceType<typeof SelectZone>>();
|
const refSelectZoneEmployee = ref<InstanceType<typeof SelectZone>>();
|
||||||
const mrz = ref<Awaited<ReturnType<typeof parseResultMRZ>>>();
|
const mrz = ref<Awaited<ReturnType<typeof parseResultMRZ>>>();
|
||||||
const toggleWorker = ref(true);
|
const toggleWorker = ref(true);
|
||||||
|
const tempPaySplitCount = ref(0);
|
||||||
const currentQuotationId = ref<string | undefined>(undefined);
|
const currentQuotationId = ref<string | undefined>(undefined);
|
||||||
const date = ref();
|
const date = ref();
|
||||||
const preSelectedWorker = ref<Employee[]>([]);
|
const preSelectedWorker = ref<Employee[]>([]);
|
||||||
|
|
@ -598,6 +599,7 @@ function convertToTable(nodes: Node[]) {
|
||||||
quotationFormData.value.paySplitCount = Math.max(
|
quotationFormData.value.paySplitCount = Math.max(
|
||||||
...list.map((v) => v.installmentNo || 0),
|
...list.map((v) => v.installmentNo || 0),
|
||||||
);
|
);
|
||||||
|
tempPaySplitCount.value = quotationFormData.value.paySplitCount;
|
||||||
|
|
||||||
list.forEach((v) => {
|
list.forEach((v) => {
|
||||||
v.amount = Math.max(selectedWorker.value.length, 1);
|
v.amount = Math.max(selectedWorker.value.length, 1);
|
||||||
|
|
@ -1168,6 +1170,9 @@ const view = ref<View>(View.Quotation);
|
||||||
</template>
|
</template>
|
||||||
<div class="surface-1 q-pa-md full-width">
|
<div class="surface-1 q-pa-md full-width">
|
||||||
<ProductItem
|
<ProductItem
|
||||||
|
:installment-input="
|
||||||
|
quotationFormData.payCondition === 'SplitCustom'
|
||||||
|
"
|
||||||
:readonly="readonly"
|
:readonly="readonly"
|
||||||
:agent-price="agentPrice"
|
:agent-price="agentPrice"
|
||||||
:employee-rows="
|
:employee-rows="
|
||||||
|
|
@ -1223,6 +1228,7 @@ const view = ref<View>(View.Quotation);
|
||||||
<QuotationFormInfo
|
<QuotationFormInfo
|
||||||
:view="view"
|
:view="view"
|
||||||
:installment-no="selectedInstallmentNo"
|
:installment-no="selectedInstallmentNo"
|
||||||
|
:pay-split-fixed="tempPaySplitCount"
|
||||||
v-model:pay-type="quotationFormData.payCondition"
|
v-model:pay-type="quotationFormData.payCondition"
|
||||||
v-model:pay-bank="payBank"
|
v-model:pay-bank="payBank"
|
||||||
v-model:pay-split-count="quotationFormData.paySplitCount"
|
v-model:pay-split-count="quotationFormData.paySplitCount"
|
||||||
|
|
|
||||||
|
|
@ -25,10 +25,11 @@ enum View {
|
||||||
Complete,
|
Complete,
|
||||||
}
|
}
|
||||||
|
|
||||||
defineProps<{
|
const props = defineProps<{
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
quotationNo?: string;
|
quotationNo?: string;
|
||||||
installmentNo?: number[];
|
installmentNo?: number[];
|
||||||
|
paySplitFixed?: number;
|
||||||
view?: View;
|
view?: View;
|
||||||
data?: {
|
data?: {
|
||||||
total: number;
|
total: number;
|
||||||
|
|
@ -56,9 +57,10 @@ const payType = defineModel<
|
||||||
const paySplitCount = defineModel<number | null>('paySplitCount', {
|
const paySplitCount = defineModel<number | null>('paySplitCount', {
|
||||||
default: 1,
|
default: 1,
|
||||||
});
|
});
|
||||||
const paySplit = defineModel<{ no: number; amount: number }[]>('paySplit', {
|
const paySplit = defineModel<{ no: number; name?: string; amount: number }[]>(
|
||||||
required: true,
|
'paySplit',
|
||||||
});
|
{ required: true },
|
||||||
|
);
|
||||||
const summaryPrice = defineModel<{
|
const summaryPrice = defineModel<{
|
||||||
totalPrice: number;
|
totalPrice: number;
|
||||||
totalDiscount: number;
|
totalDiscount: number;
|
||||||
|
|
@ -87,6 +89,10 @@ const payTypeOption = computed(() => [
|
||||||
value: 'Split',
|
value: 'Split',
|
||||||
label: t('quotation.type.installmentsCash'),
|
label: t('quotation.type.installmentsCash'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
value: 'SplitCustom',
|
||||||
|
label: t('quotation.type.installmentsCustomCash'),
|
||||||
|
},
|
||||||
]);
|
]);
|
||||||
const amount4Show = ref<string[]>([]);
|
const amount4Show = ref<string[]>([]);
|
||||||
|
|
||||||
|
|
@ -160,12 +166,8 @@ function calculateInstallments(param: {
|
||||||
watch(
|
watch(
|
||||||
() => payType.value,
|
() => payType.value,
|
||||||
(v) => {
|
(v) => {
|
||||||
if (!payType.value) return;
|
if (v === 'Split' && props.paySplitFixed) {
|
||||||
if (v === 'Split' || v === 'BillSplit') {
|
paySplitCount.value = props.paySplitFixed;
|
||||||
if (paySplitCount.value === 0) paySplitCount.value = 1;
|
|
||||||
} else {
|
|
||||||
paySplitCount.value = 0;
|
|
||||||
paySplit.value = [];
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -177,8 +179,9 @@ watch(
|
||||||
paySplitCount.value === 0 ||
|
paySplitCount.value === 0 ||
|
||||||
!paySplitCount.value ||
|
!paySplitCount.value ||
|
||||||
!summaryPrice.value.finalPrice
|
!summaryPrice.value.finalPrice
|
||||||
)
|
) {
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
calculateInstallments({ newCount: newCount || 0, oldCount: oldCount || 0 });
|
calculateInstallments({ newCount: newCount || 0, oldCount: oldCount || 0 });
|
||||||
},
|
},
|
||||||
{ deep: true },
|
{ deep: true },
|
||||||
|
|
@ -218,7 +221,7 @@ watch(
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="col-6"
|
class="col-6"
|
||||||
v-if="payType === 'Split' || payType === 'BillSplit'"
|
v-if="payType === 'Split' || payType === 'SplitCustom'"
|
||||||
>
|
>
|
||||||
<div class="row full-width items-center justify-between q-py-xs">
|
<div class="row full-width items-center justify-between q-py-xs">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
|
|
@ -239,7 +242,7 @@ watch(
|
||||||
</div>
|
</div>
|
||||||
<q-input
|
<q-input
|
||||||
v-model="paySplitCount"
|
v-model="paySplitCount"
|
||||||
:readonly
|
:readonly="readonly || payType === 'Split'"
|
||||||
class="col-3"
|
class="col-3"
|
||||||
type="number"
|
type="number"
|
||||||
dense
|
dense
|
||||||
|
|
@ -252,12 +255,12 @@ watch(
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section
|
<section
|
||||||
v-if="payType === 'Split' || payType === 'BillSplit'"
|
v-if="payType === 'Split' || payType === 'SplitCustom'"
|
||||||
class="col-12 text-caption"
|
class="col-12 text-caption"
|
||||||
style="padding-left: 20px"
|
style="padding-left: 20px"
|
||||||
>
|
>
|
||||||
<template
|
<template
|
||||||
v-for="(period, i) in !!installmentNo
|
v-for="(period, i) in installmentNo?.length > 0
|
||||||
? paySplit.filter((value) => installmentNo?.includes(value.no))
|
? paySplit.filter((value) => installmentNo?.includes(value.no))
|
||||||
: paySplit"
|
: paySplit"
|
||||||
:key="period.no"
|
:key="period.no"
|
||||||
|
|
@ -268,7 +271,16 @@ watch(
|
||||||
>
|
>
|
||||||
{{ `${$t('quotation.periodNo')} ${i + 1}` }}
|
{{ `${$t('quotation.periodNo')} ${i + 1}` }}
|
||||||
<q-input
|
<q-input
|
||||||
:readonly
|
:readonly="readonly"
|
||||||
|
:label="$t('general.name')"
|
||||||
|
v-if="payType === 'SplitCustom'"
|
||||||
|
v-model="period.name"
|
||||||
|
class="col q-mx-sm"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
/>
|
||||||
|
<q-input
|
||||||
|
:readonly="readonly || payType === 'Split'"
|
||||||
class="col q-mx-sm"
|
class="col q-mx-sm"
|
||||||
:label="$t('quotation.amount')"
|
:label="$t('quotation.amount')"
|
||||||
:model-value="commaInput(period.amount.toString())"
|
:model-value="commaInput(period.amount.toString())"
|
||||||
|
|
@ -323,6 +335,7 @@ watch(
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col">
|
<div class="col">
|
||||||
<div class="bordered-b q-px-md q-py-sm row bg-color-light items-center">
|
<div class="bordered-b q-px-md q-py-sm row bg-color-light items-center">
|
||||||
<div class="icon-wrapper bg-color q-mr-sm">
|
<div class="icon-wrapper bg-color q-mr-sm">
|
||||||
|
|
|
||||||
|
|
@ -136,6 +136,7 @@ export const useQuotationForm = defineStore('form-quotation', () => {
|
||||||
paySplit: data.paySplit.map((p, index) => ({
|
paySplit: data.paySplit.map((p, index) => ({
|
||||||
no: index + 1,
|
no: index + 1,
|
||||||
invoiceId: p.invoiceId,
|
invoiceId: p.invoiceId,
|
||||||
|
name: p.name,
|
||||||
amount: p.amount,
|
amount: p.amount,
|
||||||
})),
|
})),
|
||||||
worker: data.worker.map((v) =>
|
worker: data.worker.map((v) =>
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,7 @@ export const useQuotationStore = defineStore('quotation-store', () => {
|
||||||
const res = await api.post('/quotation', {
|
const res = await api.post('/quotation', {
|
||||||
...payload,
|
...payload,
|
||||||
paySplit: data.paySplit.map((v) => ({
|
paySplit: data.paySplit.map((v) => ({
|
||||||
|
name: v.name,
|
||||||
amount: v.amount,
|
amount: v.amount,
|
||||||
})),
|
})),
|
||||||
productServiceList: data.productServiceList.map((v) => ({
|
productServiceList: data.productServiceList.map((v) => ({
|
||||||
|
|
@ -108,6 +109,7 @@ export const useQuotationStore = defineStore('quotation-store', () => {
|
||||||
const res = await api.put(`/quotation/${data.id}`, {
|
const res = await api.put(`/quotation/${data.id}`, {
|
||||||
...payload,
|
...payload,
|
||||||
paySplit: data.paySplit.map((v) => ({
|
paySplit: data.paySplit.map((v) => ({
|
||||||
|
name: v.name,
|
||||||
amount: v.amount,
|
amount: v.amount,
|
||||||
})),
|
})),
|
||||||
productServiceList: payload.productServiceList.map((v) => ({
|
productServiceList: payload.productServiceList.map((v) => ({
|
||||||
|
|
@ -119,6 +121,7 @@ export const useQuotationStore = defineStore('quotation-store', () => {
|
||||||
workId: v.work?.id,
|
workId: v.work?.id,
|
||||||
serviceId: v.service?.id,
|
serviceId: v.service?.id,
|
||||||
workerIndex: v.workerIndex,
|
workerIndex: v.workerIndex,
|
||||||
|
installmentNo: v.installmentNo,
|
||||||
})),
|
})),
|
||||||
id: undefined,
|
id: undefined,
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -207,6 +207,7 @@ export type Quotation = {
|
||||||
no: number;
|
no: number;
|
||||||
invoiceId: string;
|
invoiceId: string;
|
||||||
amount: number;
|
amount: number;
|
||||||
|
name?: string;
|
||||||
}[];
|
}[];
|
||||||
payCondition:
|
payCondition:
|
||||||
| 'Full'
|
| 'Full'
|
||||||
|
|
@ -290,6 +291,7 @@ export type QuotationFull = {
|
||||||
paySplit: {
|
paySplit: {
|
||||||
no: number;
|
no: number;
|
||||||
amount: number;
|
amount: number;
|
||||||
|
name?: string;
|
||||||
invoice: Invoice;
|
invoice: Invoice;
|
||||||
invoiceId: string;
|
invoiceId: string;
|
||||||
}[];
|
}[];
|
||||||
|
|
@ -352,7 +354,7 @@ export type QuotationPayload = {
|
||||||
_count: { worker: number };
|
_count: { worker: number };
|
||||||
discount?: number;
|
discount?: number;
|
||||||
payBillDate?: Date | null;
|
payBillDate?: Date | null;
|
||||||
paySplit: { no: number; amount: number; invoice?: boolean }[];
|
paySplit: { no: number; amount: number; name?: string; invoice?: boolean }[];
|
||||||
paySplitCount?: number | null; // int
|
paySplitCount?: number | null; // int
|
||||||
payCondition:
|
payCondition:
|
||||||
| 'Full'
|
| 'Full'
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue