feat: add select product service function to form
This commit is contained in:
parent
af0a66347c
commit
f73aa84336
2 changed files with 87 additions and 68 deletions
|
|
@ -1,38 +1,49 @@
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { QTableProps } from 'quasar';
|
import { QTableProps } from 'quasar';
|
||||||
import TableComponents from 'src/components/TableComponents.vue';
|
import TableComponents from 'src/components/TableComponents.vue';
|
||||||
|
import { QuotationPayload } from 'src/stores/quotations/types';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
const calTax = defineModel('calTax', { default: false });
|
const calTax = defineModel('calTax', { default: false });
|
||||||
const priceData = defineModel<{
|
|
||||||
productList: number;
|
|
||||||
discount: number;
|
|
||||||
tax: number;
|
|
||||||
totalPrice: number;
|
|
||||||
}>('priceData', {
|
|
||||||
default: { productList: 0, discount: 0, tax: 0, totalPrice: 0 },
|
|
||||||
});
|
|
||||||
|
|
||||||
defineEmits<{
|
defineEmits<{
|
||||||
(e: 'delete', index: number): void;
|
(e: 'delete', index: number): void;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
withDefaults(
|
const rows = defineModel<
|
||||||
defineProps<{
|
Required<QuotationPayload['productServiceList'][number]>[]
|
||||||
rows: {
|
>('rows', { required: true });
|
||||||
[key: string]: any;
|
|
||||||
code: string;
|
function calcPrice(data: (typeof rows.value)[number]) {
|
||||||
name: string;
|
const price = data.pricePerUnit * data.amount;
|
||||||
type: string;
|
const discount = Math.round(price * data.discount) / 100;
|
||||||
amount: number;
|
const vat = Math.round((price - discount) * data.vat) / 100;
|
||||||
pricePerUnit: number;
|
|
||||||
discount: number;
|
return (price * 100 - discount * 100 + vat * 100) / 100;
|
||||||
tax: number;
|
}
|
||||||
sumPrice: number;
|
|
||||||
}[];
|
const summary = computed(() =>
|
||||||
}>(),
|
rows.value.reduce(
|
||||||
{
|
(a, c) => {
|
||||||
rows: () => [],
|
const price = c.pricePerUnit * c.amount;
|
||||||
|
const discount = Math.round(price * c.discount) / 100;
|
||||||
|
const vat = Math.round((price - discount) * c.vat) / 100;
|
||||||
|
|
||||||
|
a.totalPrice = (a.totalPrice * 100 + price * 100) / 100;
|
||||||
|
a.totalDiscount = (a.totalDiscount * 100 + discount * 100) / 100;
|
||||||
|
a.vat = (a.vat * 100 + vat * 100) / 100;
|
||||||
|
a.finalPrice =
|
||||||
|
(a.finalPrice * 100 + price * 100 - discount * 100 + vat * 100) / 100;
|
||||||
|
|
||||||
|
return a;
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
totalPrice: 0,
|
||||||
|
totalDiscount: 0,
|
||||||
|
vat: 0,
|
||||||
|
finalPrice: 0,
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
const columns = [
|
const columns = [
|
||||||
|
|
@ -46,13 +57,13 @@ const columns = [
|
||||||
name: 'code',
|
name: 'code',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
label: 'productService.group.code',
|
label: 'productService.group.code',
|
||||||
field: 'code',
|
field: (v) => v.product.code,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'name',
|
name: 'name',
|
||||||
align: 'left',
|
align: 'left',
|
||||||
label: 'productService.service.list',
|
label: 'productService.service.list',
|
||||||
field: 'name',
|
field: (v) => v.product.name,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'amount',
|
name: 'amount',
|
||||||
|
|
@ -101,11 +112,18 @@ const columns = [
|
||||||
button-delete
|
button-delete
|
||||||
:columns="columns"
|
:columns="columns"
|
||||||
:rows="rows"
|
:rows="rows"
|
||||||
imgColumn="name"
|
:customColumn="[
|
||||||
:customColumn="['amount', 'pricePerUnit', 'discount', 'tax', 'sumPrice']"
|
'name',
|
||||||
|
'amount',
|
||||||
|
'pricePerUnit',
|
||||||
|
'discount',
|
||||||
|
'tax',
|
||||||
|
'sumPrice',
|
||||||
|
]"
|
||||||
@delete="(i) => $emit('delete', i)"
|
@delete="(i) => $emit('delete', i)"
|
||||||
>
|
>
|
||||||
<template v-slot:img-column="{ props }">
|
<template v-slot:body-cell-name="{ props }">
|
||||||
|
<div>
|
||||||
<q-avatar class="q-mr-sm" size="md">
|
<q-avatar class="q-mr-sm" size="md">
|
||||||
<q-icon
|
<q-icon
|
||||||
class="full-width full-height"
|
class="full-width full-height"
|
||||||
|
|
@ -113,6 +131,8 @@ const columns = [
|
||||||
:style="`color: var(--teal-10); background: hsla(var(--teal-${$q.dark.isActive ? '8' : '10'}-hsl)/0.15)`"
|
:style="`color: var(--teal-10); background: hsla(var(--teal-${$q.dark.isActive ? '8' : '10'}-hsl)/0.15)`"
|
||||||
/>
|
/>
|
||||||
</q-avatar>
|
</q-avatar>
|
||||||
|
{{ props.row.product.name }}
|
||||||
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-slot:body-cell-amount="{ props }">
|
<template v-slot:body-cell-amount="{ props }">
|
||||||
|
|
@ -123,8 +143,7 @@ const columns = [
|
||||||
type="number"
|
type="number"
|
||||||
style="width: 70px"
|
style="width: 70px"
|
||||||
min="0"
|
min="0"
|
||||||
:modelValue="props.row.amount"
|
v-model="props.row.amount"
|
||||||
@update:modelValue="(v) => (props.row = v)"
|
|
||||||
/>
|
/>
|
||||||
</q-td>
|
</q-td>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -136,9 +155,8 @@ const columns = [
|
||||||
outlined
|
outlined
|
||||||
type="number"
|
type="number"
|
||||||
style="width: 70px"
|
style="width: 70px"
|
||||||
:modelValue="props.row.pricePerUnit"
|
|
||||||
min="0"
|
min="0"
|
||||||
@update:modelValue="(v) => (props.row = v)"
|
v-model="props.row.pricePerUnit"
|
||||||
/>
|
/>
|
||||||
</q-td>
|
</q-td>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -151,9 +169,12 @@ const columns = [
|
||||||
outlined
|
outlined
|
||||||
type="number"
|
type="number"
|
||||||
style="width: 70px"
|
style="width: 70px"
|
||||||
:modelValue="props.row.discount"
|
v-model="props.row.discount"
|
||||||
@update:modelValue="(v) => (props.row = v)"
|
>
|
||||||
/>
|
<template v-slot:append>
|
||||||
|
<span class="text-caption no-padding">%</span>
|
||||||
|
</template>
|
||||||
|
</q-input>
|
||||||
</q-td>
|
</q-td>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -165,8 +186,7 @@ const columns = [
|
||||||
outlined
|
outlined
|
||||||
type="number"
|
type="number"
|
||||||
style="width: 70px"
|
style="width: 70px"
|
||||||
:modelValue="props.row.tax"
|
v-model="props.row.vat"
|
||||||
@update:modelValue="(v) => (props.row = v)"
|
|
||||||
>
|
>
|
||||||
<template v-slot:append>
|
<template v-slot:append>
|
||||||
<span class="text-caption no-padding">%</span>
|
<span class="text-caption no-padding">%</span>
|
||||||
|
|
@ -177,15 +197,7 @@ const columns = [
|
||||||
|
|
||||||
<template v-slot:body-cell-sumPrice="{ props }">
|
<template v-slot:body-cell-sumPrice="{ props }">
|
||||||
<q-td align="center">
|
<q-td align="center">
|
||||||
<q-input
|
{{ calcPrice(props.row) }}
|
||||||
dense
|
|
||||||
min="0"
|
|
||||||
outlined
|
|
||||||
type="number"
|
|
||||||
style="width: 70px"
|
|
||||||
:modelValue="props.row.sumPrice"
|
|
||||||
@update:modelValue="(v) => (props.row = v)"
|
|
||||||
/>
|
|
||||||
</q-td>
|
</q-td>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -215,21 +227,21 @@ const columns = [
|
||||||
style="width: 15vw"
|
style="width: 15vw"
|
||||||
>
|
>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{{ $t('quotation.allProduct') }}
|
{{ $t('quotation.price') }}
|
||||||
<span class="q-ml-auto">{{ priceData.productList }} ฿</span>
|
<span class="q-ml-auto">{{ summary.totalPrice }} ฿</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{{ $t('general.discount') }}
|
{{ $t('general.discount') }}
|
||||||
<span class="q-ml-auto">{{ priceData.discount }} ฿</span>
|
<span class="q-ml-auto">{{ summary.totalDiscount }} ฿</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
{{ $t('quotation.tax') }}
|
{{ $t('quotation.tax') }}
|
||||||
<span class="q-ml-auto">{{ priceData.tax }} ฿</span>
|
<span class="q-ml-auto">{{ summary.vat }} ฿</span>
|
||||||
</div>
|
</div>
|
||||||
<q-separator spaced="md" />
|
<q-separator spaced="md" />
|
||||||
<div class="row text-bold text-body2" style="color: var(--foreground)">
|
<div class="row text-bold text-body2" style="color: var(--foreground)">
|
||||||
Total Price
|
Total Price
|
||||||
<span class="q-ml-auto">{{ priceData.totalPrice }} ฿</span>
|
<span class="q-ml-auto">{{ summary.finalPrice }} ฿</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,10 @@ const selectedGroup = ref<ProductGroup | null>(null);
|
||||||
const selectedGroupSub = ref<'product' | 'service' | null>(null);
|
const selectedGroupSub = ref<'product' | 'service' | null>(null);
|
||||||
const selectedProductServiceId = ref('');
|
const selectedProductServiceId = ref('');
|
||||||
|
|
||||||
|
const productServiceList = ref<
|
||||||
|
Required<QuotationPayload['productServiceList'][number]>[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
async function getAllProduct(
|
async function getAllProduct(
|
||||||
groupId: string,
|
groupId: string,
|
||||||
opts?: { force?: false; page?: number; pageSize?: number },
|
opts?: { force?: false; page?: number; pageSize?: number },
|
||||||
|
|
@ -141,7 +145,12 @@ function toggleDeleteProduct(index: number) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function convertToTable(nodes: Node[]) {
|
function convertToTable(nodes: Node[]) {
|
||||||
console.log(nodes);
|
const _recursive = (v: Node): Node | Node[] => {
|
||||||
|
if (v.checked && v.children) return v.children.flatMap(_recursive);
|
||||||
|
if (v.checked) return v;
|
||||||
|
return [];
|
||||||
|
};
|
||||||
|
productServiceList.value = nodes.flatMap(_recursive).map((v) => v.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
function changeMode(mode: string) {
|
function changeMode(mode: string) {
|
||||||
|
|
@ -343,7 +352,10 @@ onMounted(async () => {
|
||||||
</section>
|
</section>
|
||||||
</template>
|
</template>
|
||||||
<div class="surface-1 q-pa-md full-width">
|
<div class="surface-1 q-pa-md full-width">
|
||||||
<ProductItem @delete="toggleDeleteProduct" :rows="[]" />
|
<ProductItem
|
||||||
|
@delete="toggleDeleteProduct"
|
||||||
|
v-model:rows="productServiceList"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
</q-expansion-item>
|
</q-expansion-item>
|
||||||
|
|
||||||
|
|
@ -502,12 +514,7 @@ onMounted(async () => {
|
||||||
v-model:product-group="productGroup"
|
v-model:product-group="productGroup"
|
||||||
v-model:product-list="productList"
|
v-model:product-list="productList"
|
||||||
v-model:service-list="serviceList"
|
v-model:service-list="serviceList"
|
||||||
@submit="
|
@submit="convertToTable"
|
||||||
(nodes) => {
|
|
||||||
convertToTable(nodes);
|
|
||||||
// pageState.productServiceModal = false;
|
|
||||||
}
|
|
||||||
"
|
|
||||||
@select-group="
|
@select-group="
|
||||||
async (id) => {
|
async (id) => {
|
||||||
await getAllService(id);
|
await getAllService(id);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue