refactor: bind quotationFormInfo & cal installment
This commit is contained in:
parent
e5b95e5cc0
commit
036f5e10a0
2 changed files with 153 additions and 71 deletions
|
|
@ -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>
|
||||||
|
|
|
||||||
|
|
@ -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')"
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue