refactor: bind quotationFormInfo & cal installment

This commit is contained in:
puriphatt 2024-10-04 15:16:28 +07:00
parent e5b95e5cc0
commit 036f5e10a0
2 changed files with 153 additions and 71 deletions

View file

@ -73,16 +73,21 @@ const preSelectedWorker = ref<Employee[]>([]);
const selectedWorker = ref<Employee[]>([]); const selectedWorker = ref<Employee[]>([]);
const workerList = ref<Employee[]>([]); const workerList = ref<Employee[]>([]);
const documentReceivePoint = ref(''); const agentPrice = ref(false);
const paySplitCount = ref(1); const summaryPrice = ref<{
totalPrice: number;
totalDiscount: number;
vat: number;
finalPrice: number;
}>({
totalPrice: 0,
totalDiscount: 0,
vat: 0,
finalPrice: 0,
});
const quotationNo = ref(''); const quotationNo = ref('');
const workName = ref('');
const contactor = ref('');
const telephone = ref('');
const dueDate = ref('');
const payType = ref('');
const payBank = ref(''); const payBank = ref('');
const actor = ref('');
const pageState = reactive({ const pageState = reactive({
hideStat: false, hideStat: false,
@ -153,7 +158,6 @@ async function triggerSelectEmployeeDialog() {
function triggerProductServiceDialog() { function triggerProductServiceDialog() {
pageState.productServiceModal = true; pageState.productServiceModal = true;
// TODO: form and state controll
} }
function toggleDeleteProduct(index: number) { function toggleDeleteProduct(index: number) {
@ -206,29 +210,7 @@ function changeMode(mode: string) {
} }
onMounted(async () => { onMounted(async () => {
const ret = await productServiceStore.fetchListProductService({ // get language
page: 1,
pageSize: 9999,
});
if (ret) productGroup.value = ret.result;
const urlParams = new URLSearchParams(window.location.search);
const queryDate = urlParams.get('date');
date.value = queryDate && new Date(Number(queryDate));
branchId.value = urlParams.get('branchId') || '';
quotationFormData.value.customerBranchId =
urlParams.get('customerBranchId') || '';
const resultOption = await fetch('/option/option.json');
const rawOption = await resultOption.json();
if (locale.value === 'eng') optionStore.globalOption = rawOption.eng;
if (locale.value === 'tha') optionStore.globalOption = rawOption.tha;
const retEmp = await customerStore.fetchBranchEmployee(
quotationFormData.value.customerBranchId,
);
if (retEmp) workerList.value = retEmp.data.result;
const getCurLang = localStorage.getItem('currentLanguage'); const getCurLang = localStorage.getItem('currentLanguage');
if (getCurLang === 'English') { if (getCurLang === 'English') {
locale.value = 'eng'; locale.value = 'eng';
@ -239,6 +221,7 @@ onMounted(async () => {
setLocale('th'); setLocale('th');
} }
// get theme
const getCurTheme = localStorage.getItem('currentTheme'); const getCurTheme = localStorage.getItem('currentTheme');
if ( if (
getCurTheme === 'light' || getCurTheme === 'light' ||
@ -249,6 +232,35 @@ onMounted(async () => {
} else { } else {
changeMode('light'); changeMode('light');
} }
// get query string
const urlParams = new URLSearchParams(window.location.search);
branchId.value = urlParams.get('branchId') || '';
const price = urlParams.get('agentPrice');
agentPrice.value = price === 'true' ? true : false;
date.value = Date.now();
quotationFormData.value.customerBranchId =
urlParams.get('customerBranchId') || '';
// fetch option
const resultOption = await fetch('/option/option.json');
const rawOption = await resultOption.json();
if (locale.value === 'eng') optionStore.globalOption = rawOption.eng;
if (locale.value === 'tha') optionStore.globalOption = rawOption.tha;
const ret = await productServiceStore.fetchListProductService({
page: 1,
pageSize: 9999,
});
if (ret) productGroup.value = ret.result;
const retEmp = await customerStore.fetchBranchEmployee(
quotationFormData.value.customerBranchId,
);
if (retEmp) workerList.value = retEmp.data.result;
}); });
</script> </script>
@ -388,8 +400,10 @@ onMounted(async () => {
</template> </template>
<div class="surface-1 q-pa-md full-width"> <div class="surface-1 q-pa-md full-width">
<ProductItem <ProductItem
:agent-price="agentPrice"
@delete="toggleDeleteProduct" @delete="toggleDeleteProduct"
v-model:rows="productServiceList" v-model:rows="productServiceList"
v-model:summary-price="summaryPrice"
/> />
</div> </div>
</q-expansion-item> </q-expansion-item>
@ -450,17 +464,22 @@ onMounted(async () => {
}" }"
> >
<QuotationFormInfo <QuotationFormInfo
v-model:urgent="quotationFormData.urgent"
v-model:quotation-no="quotationNo" v-model:quotation-no="quotationNo"
v-model:actor="actor" v-model:actor="quotationFormData.actorName"
v-model:work-name="workName" v-model:work-name="quotationFormData.workName"
v-model:contactor="contactor" v-model:contactor="quotationFormData.contactName"
v-model:telephone="telephone" v-model:telephone="quotationFormData.contactTel"
v-model:document-receive-point="documentReceivePoint" v-model:document-receive-point="
v-model:due-date="dueDate" quotationFormData.documentReceivePoint
v-model:pay-type="payType" "
v-model:due-date="quotationFormData.dueDate"
v-model:pay-type="quotationFormData.payCondition"
v-model:pay-bank="payBank" v-model:pay-bank="payBank"
v-model:pay-split-count="paySplitCount" v-model:pay-split-count="quotationFormData.paySplitCount"
v-model:pay-split="quotationFormData.paySplit"
:readonly :readonly
v-model:summary-price="summaryPrice"
/> />
</div> </div>
</article> </article>

View file

@ -5,6 +5,8 @@ import { selectFilterOptionRefMod } from 'src/stores/utils';
import { onMounted, ref, watch } from 'vue'; import { onMounted, ref, watch } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { formatNumberDecimal } from 'stores/utils';
import AppBox from 'components/app/AppBox.vue'; import AppBox from 'components/app/AppBox.vue';
import DatePicker from 'src/components/shared/DatePicker.vue'; import DatePicker from 'src/components/shared/DatePicker.vue';
import SelectInput from 'src/components/shared/SelectInput.vue'; import SelectInput from 'src/components/shared/SelectInput.vue';
@ -24,6 +26,10 @@ defineProps<{
const { t } = useI18n(); const { t } = useI18n();
const urgent = defineModel<boolean>('urgent', {
required: true,
default: false,
});
const quotationNo = defineModel<string>('quotationNo', { required: true }); const quotationNo = defineModel<string>('quotationNo', { required: true });
const actor = defineModel<string>('actor', { required: true }); const actor = defineModel<string>('actor', { required: true });
const workName = defineModel<string>('workName', { required: true }); const workName = defineModel<string>('workName', { required: true });
@ -32,35 +38,50 @@ const telephone = defineModel<string>('telephone', { required: true });
const documentReceivePoint = defineModel<string>('documentReceivePoint', { const documentReceivePoint = defineModel<string>('documentReceivePoint', {
required: true, required: true,
}); });
const dueDate = defineModel<string>('dueDate', { required: true }); const dueDate = defineModel<Date | string>('dueDate', { required: true });
const payType = defineModel<string>('payType', { required: true }); const payType = defineModel<'Full' | 'Split' | 'BillFull' | 'BillSplit'>(
const paySplitCount = defineModel<number>('paySplitCount', { 'payType',
required: true, { required: true },
);
const paySplitCount = defineModel<number | null>('paySplitCount', {
default: 1, default: 1,
}); });
const payBank = defineModel<string>('payBank', { required: true }); const payBank = defineModel<string>('payBank', { required: true });
const paySplit = defineModel<
{ no: number; date: string | Date | null; amount: number }[]
>('paySplit', { required: true });
const summaryPrice = defineModel<{
totalPrice: number;
totalDiscount: number;
vat: number;
finalPrice: number;
}>('summaryPrice', {
required: true,
default: {
totalPrice: 0,
totalDiscount: 0,
vat: 0,
finalPrice: 0,
},
});
const optionStore = useOptionStore(); const optionStore = useOptionStore();
// fullAmountCash
// installmentsCash
// fullAmountBill
// installmentsBill
const payTypeOpion = ref([ const payTypeOpion = ref([
{ {
value: 'fullAmountCash', value: 'Full',
label: t('quotation.type.fullAmountCash'), label: t('quotation.type.fullAmountCash'),
}, },
{ {
value: 'installmentsCash', value: 'Split',
label: t('quotation.type.installmentsCash'), label: t('quotation.type.installmentsCash'),
}, },
{ {
value: 'fullAmountBill', value: 'BillFull',
label: t('quotation.type.fullAmountBill'), label: t('quotation.type.fullAmountBill'),
}, },
{ {
value: 'installmentsBill', value: 'BillSplit',
label: t('quotation.type.installmentsBill'), label: t('quotation.type.installmentsBill'),
}, },
]); ]);
@ -91,6 +112,50 @@ watch(
); );
}, },
); );
watch(
() => payType.value,
(v) => {
if (v === 'Split' || v === 'BillSplit') {
paySplitCount.value = 1;
} else {
paySplitCount.value = 0;
}
},
);
watch(
() => paySplitCount.value,
(newCount, oldCount) => {
if (newCount !== null && oldCount !== null) {
const totalAmount = summaryPrice.value.finalPrice;
if (newCount > oldCount) {
const installmentAmount = totalAmount / newCount;
paySplit.value.forEach((payment, index) => {
payment.amount = installmentAmount;
});
for (let i = oldCount; i < newCount; i++) {
paySplit.value.push({
no: i + 1,
date: null,
amount: installmentAmount,
});
}
} else if (newCount < oldCount) {
paySplit.value.splice(newCount, oldCount - newCount);
const installmentAmount = totalAmount / newCount;
paySplit.value.forEach((payment) => {
payment.amount = installmentAmount;
});
}
}
},
{ deep: true },
);
</script> </script>
<template> <template>
@ -104,6 +169,13 @@ watch(
<span class="text-weight-bold"> <span class="text-weight-bold">
{{ $t('general.information', { msg: $t('general.attachment') }) }} {{ $t('general.information', { msg: $t('general.attachment') }) }}
</span> </span>
<q-checkbox
v-model="urgent"
class="q-ml-auto"
size="xs"
:label="$t('general.urgent')"
/>
</div> </div>
<div class="q-pa-sm"> <div class="q-pa-sm">
@ -198,9 +270,7 @@ watch(
dense dense
outlined outlined
class="col-12" class="col-12"
v-if=" v-if="payType === 'Split' || payType === 'BillSplit'"
payType === 'installmentsCash' || payType === 'installmentsBill'
"
> >
<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">
@ -219,27 +289,27 @@ watch(
dense dense
outlined outlined
min="1" min="1"
@update:model-value="(i) => (paySplitCount = Number(i))"
/> />
</div> </div>
</q-field> </q-field>
<section <section
v-if=" v-if="payType === 'Split' || payType === 'BillSplit'"
payType === 'installmentsCash' || payType === 'installmentsBill'
"
class="col-12 text-caption" class="col-12 text-caption"
style="padding-left: 20px" style="padding-left: 20px"
> >
<template v-for="n in Number(paySplitCount)" :key="n"> <template v-for="(period, i) in paySplit" :key="period.no">
<div <div
class="row app-text-muted items-center" class="row app-text-muted items-center"
:class="{ 'q-mb-sm': n !== Number(paySplitCount) }" :class="{ 'q-mb-sm': i !== paySplit.length }"
> >
{{ `${$t('quotation.periodNo')} ${n}` }} {{ `${$t('quotation.periodNo')} ${period.no}` }}
<q-input <q-input
class="col q-mx-sm" class="col q-mx-sm"
:label="$t('quotation.amount')" :label="$t('quotation.amount')"
model-value="5,000" :model-value="formatNumberDecimal(period.amount, 2)"
dense dense
readonly
outlined outlined
bg-color="input-border" bg-color="input-border"
> >
@ -248,24 +318,17 @@ watch(
</template> </template>
</q-input> </q-input>
<DatePicker <DatePicker
v-model="period.date"
class="col-5" class="col-5"
:label="$t('quotation.payDueDate')" :label="$t('quotation.payDueDate')"
bg-color="input-border" bg-color="input-border"
/> />
<!-- <q-input
class="col"
:label="$t('quotation.amount')"
model-value="5,000"
dense
outlined
bg-color="input-border"
/> -->
</div> </div>
</template> </template>
</section> </section>
<DatePicker <DatePicker
v-if="payType === 'fullAmountBill'" v-if="payType === 'BillFull'"
class="col-12" class="col-12"
:label="$t('quotation.callDueDate')" :label="$t('quotation.callDueDate')"
/> />