fix: calc price

This commit is contained in:
Thanaphon Saengchan 2025-09-15 17:02:25 +07:00 committed by Methapon2001
parent 67cde37e34
commit c430b6082e
5 changed files with 119 additions and 50 deletions

View file

@ -31,6 +31,7 @@ import {
EditButton, EditButton,
UndoButton, UndoButton,
} from 'src/components/button'; } from 'src/components/button';
import { precisionRound } from 'src/utils/arithmetic';
import { DebitNote, useDebitNote } from 'src/stores/debit-note'; import { DebitNote, useDebitNote } from 'src/stores/debit-note';
import { RequestWork } from 'src/stores/request-list/types'; import { RequestWork } from 'src/stores/request-list/types';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
@ -40,6 +41,8 @@ import { useI18n } from 'vue-i18n';
import { QForm } from 'quasar'; import { QForm } from 'quasar';
import { getName } from 'src/services/keycloak'; import { getName } from 'src/services/keycloak';
import { RequestWorkStatus } from 'src/stores/request-list/types';
const route = useRoute(); const route = useRoute();
const router = useRouter(); const router = useRouter();
const creditNote = useCreditNote(); const creditNote = useCreditNote();
@ -49,6 +52,7 @@ const configStore = useConfigStore();
const { data: config } = storeToRefs(configStore); const { data: config } = storeToRefs(configStore);
const { t } = useI18n(); const { t } = useI18n();
const agentPrice = ref<boolean>(false);
const refForm = ref<InstanceType<typeof QForm>>(); const refForm = ref<InstanceType<typeof QForm>>();
const creditNoteData = ref<CreditNote>(); const creditNoteData = ref<CreditNote>();
const quotationData = ref<DebitNote | QuotationFull>(); const quotationData = ref<DebitNote | QuotationFull>();
@ -206,22 +210,23 @@ function getPrice(
) { ) {
return list.reduce( return list.reduce(
(a, c) => { (a, c) => {
const pricePerUnit = const value = creditNote.getfinalPriceCredit(c.list || []);
c.product.pricePerUnit - c.product.discount / c.product.amount;
const amount = c.list.length;
const priceNoVat = pricePerUnit;
const priceDiscountNoVat = priceNoVat * amount;
const rawVatTotal = priceDiscountNoVat * (config.value?.vat || 0.07); a.totalPrice = precisionRound(a.totalPrice + value.totalPrice);
a.totalDiscount = precisionRound(a.totalDiscount + value.totalDiscount);
a.vat = precisionRound(a.vat + value.vat);
a.vatIncluded = precisionRound(a.vatIncluded + value.vatIncluded);
a.vatExcluded = precisionRound(a.vatExcluded + value.vatExcluded);
a.finalPrice = precisionRound(a.finalPrice + value.finalPrice);
a.totalPrice = a.totalPrice + priceDiscountNoVat;
a.vat = c.product.vat !== 0 ? a.vat + rawVatTotal : a.vat;
a.finalPrice = a.totalPrice + a.vat;
return a; return a;
}, },
{ {
totalPrice: 0, totalPrice: 0,
totalDiscount: 0,
vat: 0, vat: 0,
vatIncluded: 0,
vatExcluded: 0,
finalPrice: 0, finalPrice: 0,
}, },
); );
@ -296,6 +301,8 @@ async function getQuotation() {
if (!ret) return; if (!ret) return;
quotationData.value = ret; quotationData.value = ret;
agentPrice.value = quotationData.value.agentPrice;
} }
async function submit() { async function submit() {

View file

@ -245,22 +245,9 @@ function calcPricePerUnit(product: RequestWork['productService']['product']) {
: product.price; : product.price;
} }
function calcPrice( function calcPrice(c: RequestWork[]): number {
product: RequestWork['productService']['product'], const price = creditNoteStore.getfinalPriceCredit(c);
amount: number, return price.finalPrice;
vat: number = 0,
) {
const pricePerUnit = agentPrice.value ? product.agentPrice : product.price;
const priceNoVat = product.vatIncluded
? pricePerUnit / (1 + (config.value?.vat || 0.07))
: pricePerUnit;
const priceDiscountNoVat = priceNoVat * amount - 0;
const rawVatTotal =
vat === 0 ? 0 : priceDiscountNoVat * (config.value?.vat || 0.07);
return precisionRound(priceNoVat * amount + rawVatTotal);
} }
watch(elements, () => {}); watch(elements, () => {});
@ -327,13 +314,11 @@ function closeAble() {
<th>{{ $t('preview.pricePerUnit') }}</th> <th>{{ $t('preview.pricePerUnit') }}</th>
<th>{{ $t('preview.value') }}</th> <th>{{ $t('preview.value') }}</th>
</tr> </tr>
{{ console.log(chunks) }}
{{ console.log(chunk) }}
<tr v-for="(v, i) in chunk" :key="i"> <tr v-for="(v, i) in chunk" :key="i">
<td class="text-center">{{ i + 1 }}</td> <td class="text-center">{{ i + 1 }}</td>
<td>{{ v.product.product.code }}</td> <td>{{ v.product.product.code }}</td>
<td>{{ v.product.product.name }}</td> <td>{{ v.product.product.name }}</td>
<td style="text-align: center"> <td style="text-align: right">
{{ {{
formatNumberDecimal( formatNumberDecimal(
calcPricePerUnit(v.product.product) + calcPricePerUnit(v.product.product) +
@ -345,13 +330,8 @@ function closeAble() {
) )
}} }}
</td> </td>
<td style="text-align: center"> <td style="text-align: right">
{{ {{ formatNumberDecimal(calcPrice(v.list), 2) }}
formatNumberDecimal(
calcPrice(v.product.product, v.list.length, v.product.vat),
2,
)
}}
</td> </td>
</tr> </tr>
</tbody> </tbody>
@ -409,8 +389,8 @@ function closeAble() {
{{ {{
formatNumberDecimal( formatNumberDecimal(
summaryPrice.totalPrice - summaryPrice.totalPrice -
summaryPrice.totalDiscount + summaryPrice.totalDiscount -
summaryPrice.vat, summaryPrice.vatExcluded,
2, 2,
) )
}} }}

View file

@ -12,10 +12,13 @@ import { baseUrl, formatNumberDecimal } from 'src/stores/utils';
import { precisionRound } from 'src/utils/arithmetic'; import { precisionRound } from 'src/utils/arithmetic';
import { productColumn } from '../constants'; import { productColumn } from '../constants';
import { useCreditNote } from 'src/stores/credit-note';
const configStore = useConfigStore(); const configStore = useConfigStore();
const { data: config } = storeToRefs(configStore); const { data: config } = storeToRefs(configStore);
const currentExpanded = ref<boolean[]>([]); const currentExpanded = ref<boolean[]>([]);
const creditNote = useCreditNote();
defineProps<{ defineProps<{
readonly?: boolean; readonly?: boolean;
@ -44,15 +47,22 @@ function calcPricePerUnit(product: RequestWork['productService']) {
return product.pricePerUnit - product.discount / product.amount; return product.pricePerUnit - product.discount / product.amount;
} }
function calcPrice(c: RequestWork['productService'], amount: number) { function calcVat(c: RequestWork['productService']) {
const pricePerUnit = c.pricePerUnit - c.discount / c.amount; const vatFactor = c.product.serviceChargeCalcVat
const priceNoVat = pricePerUnit; ? (config.value?.vat ?? 0.07)
const priceDiscountNoVat = priceNoVat * amount; : 0;
const rawVatTotal = const price = precisionRound(
c.vat === 0 ? 0 : priceDiscountNoVat * (config.value?.vat || 0.07); c.pricePerUnit * (1 + vatFactor) - c.discount / (1 + vatFactor),
);
return precisionRound(priceNoVat * amount + rawVatTotal); const vat = (price / (1 + vatFactor)) * vatFactor;
return vat;
}
function calcPrice(c: RequestWork[]) {
const price = creditNote.getfinalPriceCredit(c);
return price.finalPrice;
} }
</script> </script>
<template> <template>
@ -162,12 +172,7 @@ function calcPrice(c: RequestWork['productService'], amount: number) {
<!-- total --> <!-- total -->
<q-td class="text-right"> <q-td class="text-right">
{{ {{ formatNumberDecimal(calcPrice(props.row.list), 2) }}
formatNumberDecimal(
calcPrice(props.row.product, props.row.list.length),
2,
)
}}
</q-td> </q-td>
<q-td> <q-td>
<q-btn <q-btn

View file

@ -9,6 +9,10 @@ import { defineStore } from 'pinia';
import { api } from 'src/boot/axios'; import { api } from 'src/boot/axios';
import { PaginationResult } from 'src/types'; import { PaginationResult } from 'src/types';
import { RequestWork, RequestWorkStatus } from 'src/stores/request-list/types';
import { precisionRound } from 'src/utils/arithmetic';
import { useConfigStore } from 'src/stores/config';
import { manageAttachment, manageFile } from '../utils'; import { manageAttachment, manageFile } from '../utils';
const ENDPOINT = 'credit-note'; const ENDPOINT = 'credit-note';
@ -80,6 +84,7 @@ export async function acceptCreditNote(id: string) {
} }
export const useCreditNote = defineStore('credit-note-store', () => { export const useCreditNote = defineStore('credit-note-store', () => {
const configStore = useConfigStore();
const data = ref<Data[]>([]); const data = ref<Data[]>([]);
const page = ref<number>(1); const page = ref<number>(1);
const pageMax = ref<number>(1); const pageMax = ref<number>(1);
@ -90,6 +95,75 @@ export const useCreditNote = defineStore('credit-note-store', () => {
[Status.Success]: 0, [Status.Success]: 0,
}); });
function getfinalPriceCredit(c: RequestWork[]): {
totalPrice: number;
totalDiscount: number;
vat: number;
vatIncluded: number;
vatExcluded: number;
finalPrice: number;
} {
const price = c.reduce(
(acc, crr) => {
const servicesChargeStepCount =
crr.productService.work?.productOnWork?.find(
(item) => item.productId === crr.productService.productId,
).stepCount;
const successCount = crr.stepStatus.filter(
(item) => item.workStatus === RequestWorkStatus.Completed,
).length;
const vatFactor =
crr.productService.vat > 0 ? (configStore.data?.vat ?? 0.07) : 0;
const price = precisionRound(
crr.productService.pricePerUnit * (1 + vatFactor) -
crr.productService.discount,
);
const vat = crr.productService.product.calcVat
? (price / (1 + vatFactor)) * vatFactor
: 0;
acc.totalPrice = precisionRound(
acc.totalPrice + price + crr.productService.discount,
);
acc.totalDiscount = precisionRound(
acc.totalDiscount + crr.productService.discount,
);
acc.vat = precisionRound(acc.vat + vat);
acc.vatExcluded = crr.productService.product.agentPriceCalcVat
? acc.vatExcluded
: precisionRound(acc.vatExcluded + price / (1 + vatFactor));
if (servicesChargeStepCount && successCount) {
acc.finalPrice = precisionRound(
acc.finalPrice +
price -
crr.productService.product.serviceCharge * successCount,
);
return acc;
}
acc.finalPrice = precisionRound(acc.finalPrice + price);
return acc;
},
{
totalPrice: 0,
totalDiscount: 0,
vat: 0,
vatIncluded: 0,
vatExcluded: 0,
finalPrice: 0,
},
);
return price;
}
return { return {
data, data,
page, page,
@ -97,6 +171,8 @@ export const useCreditNote = defineStore('credit-note-store', () => {
pageSize, pageSize,
stats, stats,
getfinalPriceCredit,
getCreditNoteStats, getCreditNoteStats,
getCreditNote, getCreditNote,
getCreditNoteList, getCreditNoteList,

View file

@ -178,6 +178,7 @@ type WorkRelation = {
createdByUserId?: string; createdByUserId?: string;
updatedAt: string; updatedAt: string;
updatedByUserId?: string; updatedByUserId?: string;
productOnWork?: { productId: string; stepCount: number }[];
}; };
type ServiceRelation = { type ServiceRelation = {