fix: product table

This commit is contained in:
puriphatt 2024-10-08 16:38:15 +07:00
parent c2910ce459
commit 4e736a2c96
3 changed files with 326 additions and 122 deletions

View file

@ -1,15 +1,17 @@
<script lang="ts" setup>
import { computed, watch } from 'vue';
import { computed, ref, watch } from 'vue';
import { QTableProps } from 'quasar';
import ThaiBahtText from 'thai-baht-text';
import { toWords } from 'number-to-words';
import { storeToRefs } from 'pinia';
import { commaInput } from 'stores/utils';
import { precisionRound } from 'src/utils/arithmetic';
import TableComponents from 'components/TableComponents.vue';
import { QuotationPayload } from 'stores/quotations/types';
import { formatNumberDecimal } from 'stores/utils';
import { useConfigStore } from 'stores/config';
import { Product, Service, Work } from 'src/stores/product-service/types';
defineProps<{
agentPrice: boolean;
@ -27,6 +29,21 @@ const rows = defineModel<
Required<QuotationPayload['productServiceList'][number]>[]
>('rows', { required: true });
const groupList = defineModel<
{
title: string;
product: {
amount: number;
discount: number;
pricePerUnit: number;
vat: number;
product: Product;
service?: Service;
work?: Work;
}[];
}[]
>('groupList', { default: [] });
const summaryPrice = defineModel<{
totalPrice: number;
totalDiscount: number;
@ -42,9 +59,13 @@ const summaryPrice = defineModel<{
},
});
const totalDiscount4show = ref(summaryPrice.value.totalDiscount.toString());
function calcPrice(c: (typeof rows.value)[number]) {
return precisionRound(
c.pricePerUnit * c.amount - c.discount + c.vat * c.amount,
c.pricePerUnit * c.amount -
c.discount +
c.pricePerUnit * (config.value?.vat || 0.07) * c.amount,
);
}
@ -87,7 +108,7 @@ const columns = [
},
{
name: 'name',
align: 'left',
align: 'center',
label: 'productService.service.list',
field: (v) => v.product.name,
},
@ -109,10 +130,16 @@ const columns = [
label: 'general.discount',
field: 'discount',
},
{
name: 'priceBeforeVat',
align: 'center',
label: 'quotation.priceBeforeVat',
field: 'priceBeforeVat',
},
{
name: 'tax',
align: 'center',
label: 'quotation.tax',
label: 'general.vat',
field: 'tax',
},
{
@ -144,105 +171,217 @@ watch(
<template>
<div class="column">
<div class="full-width">
<TableComponents
flat
bordered
hidePagination
button-delete
:columns="columns"
:rows="rows"
:customColumn="[
'name',
'amount',
'pricePerUnit',
'discount',
'tax',
'sumPrice',
]"
@delete="(i) => $emit('delete', i)"
>
<template v-slot:body-cell-name="{ props }">
<q-td>
<q-avatar class="q-mr-sm" size="md">
<q-icon
class="full-width full-height"
name="mdi-shopping-outline"
:style="`color: var(--teal-10); background: hsla(var(--teal-${$q.dark.isActive ? '8' : '10'}-hsl)/0.15)`"
/>
</q-avatar>
{{ props.row.product.name }}
</q-td>
</template>
<template v-slot:body-cell-amount="{ props }">
<q-td align="center">
<q-input
dense
outlined
type="number"
style="width: 70px"
min="0"
v-model="props.row.amount"
/>
</q-td>
</template>
<template v-slot:body-cell-pricePerUnit="{ props }">
<q-td align="right">
{{ formatNumberDecimal(props.row.pricePerUnit, 2) }}
</q-td>
</template>
<template v-slot:body-cell-discount="{ props }">
<q-td align="center">
<q-input
dense
min="0"
outlined
type="number"
style="width: 70px"
v-model="props.row.discount"
/>
</q-td>
</template>
<template v-slot:body-cell-tax="{ props }">
<q-td align="center">
{{
precisionRound(
(props.row.pricePerUnit * props.row.amount -
props.row.discount) *
(config?.vat || 0.07),
)
}}
</q-td>
</template>
<template v-slot:body-cell-sumPrice="{ props }">
<q-td align="right">
{{ formatNumberDecimal(calcPrice(props.row), 2) }}
</q-td>
</template>
<template #button>
<q-btn
icon="mdi-monitor"
<section v-for="(item, i) in groupList" :key="i" class="q-pb-md">
<div
v-if="item.title"
class="q-py-sm q-px-md bordered"
style="background: hsla(var(--orange-5-hsl) / 0.1)"
>
<q-avatar
class="q-mr-lg"
style="background: var(--orange-5); color: var(--surface-1)"
size="sm"
class="rounded q-mr-xs"
padding="4px 8px"
dense
flat
style="
background-color: hsla(var(--positive-bg) / 0.1);
color: hsl(var(--positive-bg));
"
/>
</template>
</TableComponents>
>
{{ i + 1 }}
</q-avatar>
{{ item.title }}
</div>
<TableComponents
flat
bordered
hidePagination
button-delete
:columns="columns"
:rows="item.product"
:customColumn="[
'name',
'amount',
'pricePerUnit',
'discount',
'tax',
'sumPrice',
'priceBeforeVat',
]"
@delete="(i) => $emit('delete', i)"
>
<template v-slot:body-cell-name="{ props }">
<q-td>
<q-avatar class="q-mr-sm" size="md">
<q-icon
class="full-width full-height"
name="mdi-shopping-outline"
:style="`color: var(--teal-10); background: hsla(var(--teal-${$q.dark.isActive ? '8' : '10'}-hsl)/0.15)`"
/>
</q-avatar>
{{ props.row.product.name }}
</q-td>
</template>
<template v-slot:body-cell-amount="{ props }">
<q-td align="center">
<q-input
dense
outlined
type="number"
style="width: 70px"
min="0"
v-model="props.row.amount"
/>
</q-td>
</template>
<template v-slot:body-cell-pricePerUnit="{ props }">
<q-td align="right">
{{
formatNumberDecimal(
props.row.pricePerUnit +
props.row.pricePerUnit * (config?.vat || 0.07),
2,
)
}}
</q-td>
</template>
<template v-slot:body-cell-discount="{ props }">
<q-td align="center">
<q-input
dense
min="0"
outlined
input-class="text-right"
type="number"
style="width: 90px"
v-model="props.row.discount"
/>
</q-td>
</template>
<template v-slot:body-cell-priceBeforeVat="{ props }">
<q-td align="right">
{{ formatNumberDecimal(props.row.pricePerUnit, 2) }}
</q-td>
</template>
<template v-slot:body-cell-tax="{ props }">
<q-td align="right">
{{
precisionRound(
(props.row.pricePerUnit * props.row.amount -
props.row.discount) *
(config?.vat || 0.07),
)
}}
</q-td>
</template>
<template v-slot:body-cell-sumPrice="{ props }">
<q-td align="right">
{{ formatNumberDecimal(calcPrice(props.row), 2) }}
</q-td>
</template>
<template #button>
<q-btn
icon="mdi-monitor"
size="sm"
class="rounded q-mr-xs"
padding="4px 8px"
dense
flat
style="
background-color: hsla(var(--positive-bg) / 0.1);
color: hsl(var(--positive-bg));
"
/>
</template>
</TableComponents>
</section>
</div>
<div
class="column q-ml-auto text-caption app-text-muted-2 q-pt-md"
<div class="row text-weight-bold text-caption items-start">
<span
class="col row items-center justify-between q-px-md q-py-xs q-mr-lg rounded bg-color-orange-light"
>
<span class="app-text-muted-2">
{{ $t('general.totalAmount') }}
</span>
<span v-if="summary.finalPrice">
{{
$i18n.locale === 'eng'
? EngBahtText(summary.finalPrice)
: ThaiBahtText(summary.finalPrice)
}}
</span>
</span>
<div class="col column">
<div class="row q-py-xs q-pr-xs q-pl-md">
<span class="col-9 app-text-muted-2">
{{ $t('quotation.allProductPrice') }}
</span>
<q-input
dense
class="col price-tag"
readonly
outlined
input-class="text-right text-bold "
:model-value="formatNumberDecimal(summary.totalPrice, 2)"
/>
</div>
<div class="row q-py-xs q-pr-xs q-pl-md">
<span class="col-9 app-text-muted-2">
{{ $t('quotation.quotationDiscount') }}
{{ console.log(totalDiscount4show) }}
</span>
<q-input
dense
class="col price-tag"
outlined
input-class="text-right text-bold"
v-model="summary.totalDiscount"
/>
<!-- <q-input
dense
class="col price-tag"
outlined
input-class="text-right text-bold"
:model-value="commaInput(summary.totalDiscount.toString())"
@update:model-value="
(v) => {
if (typeof v === 'string') totalDiscount4show = commaInput(v);
const x = parseInt(
totalDiscount4show && typeof totalDiscount4show === 'string'
? totalDiscount4show.replace(/,/g, '')
: '',
);
summary.totalDiscount = x;
}
"
/> -->
</div>
<div
class="row text-bold bg-color-orange rounded q-py-xs q-pr-xs q-pl-md items-center"
style="color: var(--surface-1)"
>
<span class="col-9">
{{ $t('general.totalAmount') }}
</span>
<q-input
dense
class="col price-tag"
outlined
readonly
input-class="text-right text-bold "
:model-value="formatNumberDecimal(summary.finalPrice, 2)"
/>
</div>
</div>
</div>
<!-- <div
class="column q-ml-auto text-caption app-text-muted-2"
style="width: 15vw"
>
<div class="row">
@ -280,11 +419,26 @@ watch(
? EngBahtText(summary.finalPrice)
: ThaiBahtText(summary.finalPrice)
}})
</span>
</span> -->
</div>
</template>
<style scoped>
.bg-color-orange-light {
--_color: var(--yellow-7-hsl);
background: hsla(var(--_color) / 0.2);
}
.bg-color-orange {
--_color: var(--yellow-7-hsl);
color: white;
background: hsla(var(--_color));
}
.dark .bg-color-orange {
--_color: var(--orange-6-hsl / 0.2);
}
.worker-item {
--side-color: var(--brand-1);
position: relative;
@ -299,6 +453,12 @@ watch(
}
}
:deep(.price-tag .q-field__control) {
display: flex;
align-items: center;
height: 25px;
}
.worker-item::before {
position: absolute;
content: ' ';