feat: price summary

This commit is contained in:
Methapon Metanipat 2024-10-10 09:24:02 +07:00
parent 47272f46de
commit b292e00380
4 changed files with 85 additions and 32 deletions

View file

@ -48,6 +48,8 @@ const groupList = ref<
}[] }[]
>([]); >([]);
const finalDiscount = defineModel<number>('finalDiscount', { default: 0 });
const summaryPrice = defineModel<{ const summaryPrice = defineModel<{
totalPrice: number; totalPrice: number;
totalDiscount: number; totalDiscount: number;
@ -66,14 +68,13 @@ const summaryPrice = defineModel<{
const currentBtnOpen = ref<{ title: string; opened: boolean[] }[]>([ const currentBtnOpen = ref<{ title: string; opened: boolean[] }[]>([
{ title: '', opened: [] }, { title: '', opened: [] },
]); ]);
const finalDiscount = ref<number>(0);
const finalDiscount4Show = ref<string>(finalDiscount.value.toString()); const finalDiscount4Show = ref<string>(finalDiscount.value.toString());
function calcPrice(c: (typeof rows.value)[number]) { function calcPrice(c: (typeof rows.value)[number]) {
return precisionRound( return precisionRound(
c.pricePerUnit * c.amount - c.pricePerUnit * c.amount -
c.discount + c.discount +
c.pricePerUnit * (config.value?.vat || 0.07) * c.amount, (c.pricePerUnit * c.amount - c.discount) * (config.value?.vat || 0.07),
); );
} }
@ -88,7 +89,9 @@ const summary = computed(() =>
a.totalPrice = precisionRound(a.totalPrice + price); a.totalPrice = precisionRound(a.totalPrice + price);
a.totalDiscount = precisionRound(a.totalDiscount + Number(c.discount)); a.totalDiscount = precisionRound(a.totalDiscount + Number(c.discount));
a.vat = precisionRound(a.vat + vat); a.vat = precisionRound(a.vat + vat);
a.finalPrice = precisionRound(a.finalPrice + price + vat); a.finalPrice = precisionRound(
a.totalPrice - a.totalDiscount + a.vat - Number(finalDiscount.value),
);
return a; return a;
}, },
@ -96,7 +99,7 @@ const summary = computed(() =>
totalPrice: 0, totalPrice: 0,
totalDiscount: 0, totalDiscount: 0,
vat: 0, vat: 0,
finalPrice: -(+finalDiscount.value || 0), finalPrice: 0,
}, },
), ),
); );

View file

@ -200,7 +200,6 @@ function mapNode() {
opened: v.work.length > 0, opened: v.work.length > 0,
checked: true, checked: true,
icon: 'mdi-server-outline', icon: 'mdi-server-outline',
attributes: v.attributes,
bg: 'hsla(var(--orange-5-hsl)/0.1)', bg: 'hsla(var(--orange-5-hsl)/0.1)',
fg: 'var(--orange-5)', fg: 'var(--orange-5)',
children: (function () { children: (function () {
@ -240,13 +239,11 @@ function mapNode() {
const withNameObjects = v.work const withNameObjects = v.work
.filter((w: Work) => w.name) .filter((w: Work) => w.name)
.flatMap((w: Work) => ({ .flatMap((w: Work) => ({
type: 'work',
id: w.id, id: w.id,
title: w.name, title: w.name,
subtitle: ' ', subtitle: ' ',
opened: w.productOnWork.length > 0, opened: w.productOnWork.length > 0,
checked: true, checked: true,
attributes: w.attributes,
children: w.productOnWork.map((p) => { children: w.productOnWork.map((p) => {
const price = prop.agentPrice const price = prop.agentPrice
? p.product.agentPrice ? p.product.agentPrice
@ -259,8 +256,6 @@ function mapNode() {
title: p.product.name, title: p.product.name,
subtitle: p.product.code || ' ', subtitle: p.product.code || ' ',
checked: true, checked: true,
detail: p.product.detail,
remark: p.product.remark,
value: { value: {
vat: 0, vat: 0,
pricePerUnit, pricePerUnit,
@ -270,7 +265,6 @@ function mapNode() {
work: w, work: w,
product: p.product, product: p.product,
}, },
type: 'product',
}; };
}), }),
})); }));
@ -286,8 +280,6 @@ function mapNode() {
id: v.id, id: v.id,
title: v.name, title: v.name,
subtitle: v.code, subtitle: v.code,
detail: v.detail,
remark: v.remark,
value: { value: {
vat: 0, vat: 0,
pricePerUnit, pricePerUnit,
@ -296,7 +288,6 @@ function mapNode() {
product: v.raw, product: v.raw,
}, },
checked: true, checked: true,
type: 'product',
icon: 'mdi-shopping-outline', icon: 'mdi-shopping-outline',
bg: 'hsla(var(--teal-10-hsl)/0.1)', bg: 'hsla(var(--teal-10-hsl)/0.1)',
fg: 'var(--teal-10)', fg: 'var(--teal-10)',

View file

@ -2,7 +2,15 @@
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { nextTick, onBeforeMount, onMounted, reactive, ref, watch } from 'vue'; import {
computed,
nextTick,
onBeforeMount,
onMounted,
reactive,
ref,
watch,
} from 'vue';
import { dialog } from 'stores/utils'; import { dialog } from 'stores/utils';
// NOTE: Import stores // NOTE: Import stores
@ -53,6 +61,8 @@ import {
columnsAttachment, columnsAttachment,
} from 'src/pages/03_customer-management/constant'; } from 'src/pages/03_customer-management/constant';
import { group } from 'node:console'; import { group } from 'node:console';
import { precisionRound } from 'src/utils/arithmetic';
import { useConfigStore } from 'src/stores/config';
defineProps<{ defineProps<{
readonly?: boolean; readonly?: boolean;
@ -71,6 +81,7 @@ type Node = {
type ProductGroupId = string; type ProductGroupId = string;
type Id = string; type Id = string;
const configStore = useConfigStore();
const productServiceStore = useProductServiceStore(); const productServiceStore = useProductServiceStore();
const employeeFormStore = useEmployeeForm(); const employeeFormStore = useEmployeeForm();
const customerStore = useCustomerStore(); const customerStore = useCustomerStore();
@ -89,6 +100,8 @@ const {
quotationFull, quotationFull,
} = storeToRefs(quotationForm); } = storeToRefs(quotationForm);
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 selectedBranchIssuer = ref(''); const selectedBranchIssuer = ref('');
@ -115,17 +128,33 @@ const workerList = ref<Employee[]>([]);
const selectedProductGroup = ref(''); const selectedProductGroup = ref('');
const agentPrice = ref(false); const agentPrice = ref(false);
const summaryPrice = ref<{ const finalDiscount = ref(0);
totalPrice: number; const summaryPrice = computed(() =>
totalDiscount: number; productServiceList.value.reduce(
vat: number; (a, c) => {
finalPrice: number; console.log(finalDiscount.value);
}>({ const price = precisionRound(c.pricePerUnit * c.amount);
totalPrice: 0, const vat = precisionRound(
totalDiscount: 0, (c.pricePerUnit * c.amount - c.discount) * (config.value?.vat || 0.07),
vat: 0, );
finalPrice: 0,
}); a.totalPrice = precisionRound(a.totalPrice + price);
a.totalDiscount = precisionRound(a.totalDiscount + Number(c.discount));
a.vat = precisionRound(a.vat + vat);
a.finalPrice = precisionRound(
a.totalPrice - a.totalDiscount + a.vat - Number(finalDiscount.value),
);
return a;
},
{
totalPrice: 0,
totalDiscount: 0,
vat: 0,
finalPrice: 0,
},
),
);
const quotationNo = ref(''); const quotationNo = ref('');
const payBank = ref(''); const payBank = ref('');
@ -409,6 +438,7 @@ function changeMode(mode: string) {
} }
onMounted(async () => { onMounted(async () => {
await configStore.getConfig();
// get language // get language
const getCurLang = localStorage.getItem('currentLanguage'); const getCurLang = localStorage.getItem('currentLanguage');
if (getCurLang === 'English') { if (getCurLang === 'English') {
@ -748,6 +778,7 @@ watch(
v-model:pay-split-count="quotationFormData.paySplitCount" v-model:pay-split-count="quotationFormData.paySplitCount"
v-model:pay-split="quotationFormData.paySplit" v-model:pay-split="quotationFormData.paySplit"
:readonly :readonly
v-model:final-discount="finalDiscount"
v-model:summary-price="summaryPrice" v-model:summary-price="summaryPrice"
/> />
</div> </div>
@ -1082,7 +1113,7 @@ watch(
mrz = await runOcr(file, parseResultMRZ); mrz = await runOcr(file, parseResultMRZ);
if (mrz !== null) { if (mrz !== null) {
const mapMrz = Object.entries(mrz.result).map( const mapMrz = Object.entries(mrz.result || {}).map(
([key, value]) => ({ ([key, value]) => ({
name: key, name: key,
value: value, value: value,

View file

@ -71,7 +71,7 @@ const summaryPrice = defineModel<{
const optionStore = useOptionStore(); const optionStore = useOptionStore();
const finalDiscount = ref<number>(0); const finalDiscount = defineModel('finalDiscount', { default: 0 });
const payTypeOpion = ref([ const payTypeOpion = ref([
{ {
value: 'Full', value: 'Full',
@ -429,7 +429,9 @@ watch(
<span class="q-ml-auto"> <span class="q-ml-auto">
{{ {{
formatNumberDecimal( formatNumberDecimal(
summaryPrice.finalPrice + Number(finalDiscount), summaryPrice.finalPrice +
summaryPrice.totalDiscount +
Number(finalDiscount),
2, 2,
) || 0 ) || 0
}} }}
@ -444,15 +446,41 @@ watch(
</div> </div>
<div class="row"> <div class="row">
{{ $t('general.totalAfterDiscount') }} {{ $t('general.totalAfterDiscount') }}
<span class="q-ml-auto">{{ data?.totalAfterDiscount || 0 }} ฿</span> <span class="q-ml-auto">
{{
formatNumberDecimal(
summaryPrice.finalPrice + Number(finalDiscount),
2,
)
}}
฿
</span>
</div> </div>
<div class="row"> <div class="row">
{{ $t('general.totalVatExcluded') }} {{ $t('general.totalVatExcluded') }}
<span class="q-ml-auto">{{ data?.totalVatExcluded || 0 }} ฿</span> <span class="q-ml-auto">
{{
formatNumberDecimal(
summaryPrice.finalPrice +
Number(finalDiscount) -
Number(summaryPrice.vat),
2,
) || 0
}}
฿
</span>
</div> </div>
<div class="row"> <div class="row">
{{ $t('general.totalVatIncluded') }} {{ $t('general.totalVatIncluded') }}
<span class="q-ml-auto">{{ data?.totalVatIncluded || 0 }} ฿</span> <span class="q-ml-auto">
{{
formatNumberDecimal(
summaryPrice.finalPrice + Number(finalDiscount),
2,
) || 0
}}
฿
</span>
</div> </div>
<div class="row"> <div class="row">
{{ {{
@ -460,7 +488,7 @@ watch(
msg: `${config && Math.round(config.vat * 100)}%`, msg: `${config && Math.round(config.vat * 100)}%`,
}) })
}} }}
<span class="q-ml-auto">{{ data?.totalVatIncluded || 0 }} ฿</span> <span class="q-ml-auto">{{ summaryPrice.vat || 0 }} ฿</span>
</div> </div>
<div class="row"> <div class="row">
{{ $t('general.discountAfterVat') }} {{ $t('general.discountAfterVat') }}