Merge branch 'feat/separate-price-calc' into develop
This commit is contained in:
commit
84282ff5ce
18 changed files with 744 additions and 332 deletions
|
|
@ -1,7 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, watch } from 'vue';
|
import { ref, watch } from 'vue';
|
||||||
import { formatNumberDecimal, commaInput } from 'stores/utils';
|
import { formatNumberDecimal, commaInput } from 'stores/utils';
|
||||||
import { QTableProps } from 'quasar';
|
import { QTableProps, QTableSlots } from 'quasar';
|
||||||
import { calculatePrice } from 'src/utils/arithmetic';
|
import { calculatePrice } from 'src/utils/arithmetic';
|
||||||
|
|
||||||
const serviceCharge = defineModel<number>('serviceCharge');
|
const serviceCharge = defineModel<number>('serviceCharge');
|
||||||
|
|
@ -9,24 +9,48 @@ const agentPrice = defineModel<number>('agentPrice');
|
||||||
const price = defineModel<number>('price');
|
const price = defineModel<number>('price');
|
||||||
const vatIncluded = defineModel<boolean>('vatIncluded');
|
const vatIncluded = defineModel<boolean>('vatIncluded');
|
||||||
const calcVat = defineModel<boolean>('calcVat');
|
const calcVat = defineModel<boolean>('calcVat');
|
||||||
|
const agentPriceVatIncluded = defineModel<boolean>('agentPriceVatIncluded');
|
||||||
|
const agentPriceCalcVat = defineModel<boolean>('agentPriceCalcVat');
|
||||||
|
const serviceChargeVatIncluded = defineModel<boolean>(
|
||||||
|
'serviceChargeVatIncluded',
|
||||||
|
);
|
||||||
|
const serviceChargeCalcVat = defineModel<boolean>('serviceChargeCalcVat');
|
||||||
|
|
||||||
const price4Show = ref<string>(commaInput(price.value?.toString() || '0'));
|
const formattedPrice = ref<string>(commaInput(price.value?.toString() || '0'));
|
||||||
const agentPrice4Show = ref<string>(
|
const formattedAgentPrice = ref<string>(
|
||||||
commaInput(agentPrice.value?.toString() || '0'),
|
commaInput(agentPrice.value?.toString() || '0'),
|
||||||
);
|
);
|
||||||
const serviceCharge4Show = ref<string>(
|
const formattedServiceCharge = ref<string>(
|
||||||
commaInput(serviceCharge.value?.toString() || '0'),
|
commaInput(serviceCharge.value?.toString() || '0'),
|
||||||
);
|
);
|
||||||
|
|
||||||
const column = [
|
type RowData = {
|
||||||
|
pricePerUnit: number;
|
||||||
|
calcVat: boolean;
|
||||||
|
vatIncluded: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = [
|
||||||
{
|
{
|
||||||
name: 'label',
|
name: 'label',
|
||||||
align: 'center',
|
align: 'left',
|
||||||
label: 'productService.product.priceInformation',
|
label: 'productService.product.priceInformation',
|
||||||
field: 'label',
|
field: 'label',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'pricePerUnit',
|
name: '#calcVat',
|
||||||
|
align: 'center',
|
||||||
|
label: 'general.calculateVat',
|
||||||
|
field: '#calcVat',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '#vatIncluded',
|
||||||
|
align: 'center',
|
||||||
|
label: 'productService.product.vatIncluded',
|
||||||
|
field: '#vatIncluded',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '#pricePerUnit',
|
||||||
align: 'center',
|
align: 'center',
|
||||||
label: 'quotation.pricePerUnit',
|
label: 'quotation.pricePerUnit',
|
||||||
field: 'pricePerUnit',
|
field: 'pricePerUnit',
|
||||||
|
|
@ -35,54 +59,77 @@ const column = [
|
||||||
name: 'beforeVat',
|
name: 'beforeVat',
|
||||||
align: 'right',
|
align: 'right',
|
||||||
label: 'quotation.priceBeforeVat',
|
label: 'quotation.priceBeforeVat',
|
||||||
field: 'beforeVat',
|
field: (data: RowData) =>
|
||||||
|
formatNumberDecimal(
|
||||||
|
calculatePrice({
|
||||||
|
output: 'beforeVat',
|
||||||
|
vatIncluded: data.vatIncluded,
|
||||||
|
price: data.pricePerUnit || 0,
|
||||||
|
}),
|
||||||
|
2,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'vat',
|
name: 'vat',
|
||||||
align: 'right',
|
align: 'right',
|
||||||
label: 'general.vat',
|
label: 'general.vat',
|
||||||
field: 'vat',
|
field: (data: RowData) =>
|
||||||
|
formatNumberDecimal(
|
||||||
|
calculatePrice({
|
||||||
|
output: 'vat',
|
||||||
|
calcVat: data.calcVat,
|
||||||
|
price: Number(
|
||||||
|
formatNumberDecimal(
|
||||||
|
calculatePrice({
|
||||||
|
output: 'beforeVat',
|
||||||
|
vatIncluded: data.vatIncluded,
|
||||||
|
price: data.pricePerUnit,
|
||||||
|
}),
|
||||||
|
2,
|
||||||
|
).replaceAll(',', ''),
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
2,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'total',
|
name: 'total',
|
||||||
align: 'right',
|
align: 'right',
|
||||||
label: 'quotation.sumPrice',
|
label: 'quotation.sumPrice',
|
||||||
field: 'total',
|
field: (data: RowData) =>
|
||||||
|
formatNumberDecimal(
|
||||||
|
calculatePrice({
|
||||||
|
output: 'total',
|
||||||
|
vat: 0.07,
|
||||||
|
price: data.pricePerUnit,
|
||||||
|
}) +
|
||||||
|
(!data.vatIncluded
|
||||||
|
? calculatePrice({
|
||||||
|
output: 'vat',
|
||||||
|
calcVat: data.calcVat,
|
||||||
|
price: data.pricePerUnit,
|
||||||
|
})
|
||||||
|
: 0),
|
||||||
|
2,
|
||||||
|
),
|
||||||
},
|
},
|
||||||
] as const satisfies QTableProps['columns'];
|
] as QTableProps['columns'];
|
||||||
|
|
||||||
const row = [
|
watch([calcVat, agentPriceCalcVat, serviceChargeCalcVat], () => {
|
||||||
{
|
if (calcVat.value === false) {
|
||||||
label: 'productService.product.salePrice',
|
vatIncluded.value = false;
|
||||||
beforeVat: 0,
|
}
|
||||||
vat: 0,
|
if (agentPriceCalcVat.value === false) {
|
||||||
total: 0,
|
agentPriceVatIncluded.value = false;
|
||||||
},
|
}
|
||||||
{
|
if (serviceChargeCalcVat.value === false) {
|
||||||
label: 'productService.product.agentPrice',
|
serviceChargeVatIncluded.value = false;
|
||||||
beforeVat: 0,
|
}
|
||||||
vat: 0,
|
|
||||||
total: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'productService.product.processingPrice',
|
|
||||||
beforeVat: 0,
|
|
||||||
vat: 0,
|
|
||||||
total: 0,
|
|
||||||
},
|
|
||||||
] as const satisfies QTableProps['rows'];
|
|
||||||
|
|
||||||
watch(calcVat, () => {
|
|
||||||
if (calcVat.value === false) vatIncluded.value = false;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
withDefaults(
|
withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
dense?: boolean;
|
|
||||||
outlined?: boolean;
|
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
separator?: boolean;
|
|
||||||
isType?: boolean;
|
|
||||||
priceDisplay?: {
|
priceDisplay?: {
|
||||||
price: boolean;
|
price: boolean;
|
||||||
agentPrice: boolean;
|
agentPrice: boolean;
|
||||||
|
|
@ -115,255 +162,239 @@ withDefaults(
|
||||||
{{ $t('productService.product.priceInformation') }}
|
{{ $t('productService.product.priceInformation') }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<section class="q-pr-md">
|
|
||||||
<input
|
|
||||||
id="input-calc-vat"
|
|
||||||
type="checkbox"
|
|
||||||
v-model="calcVat"
|
|
||||||
:disabled="readonly"
|
|
||||||
/>
|
|
||||||
<label
|
|
||||||
class="q-pl-sm"
|
|
||||||
for="input-calc-vat"
|
|
||||||
:style="{ opacity: readonly ? '.5' : undefined }"
|
|
||||||
>
|
|
||||||
{{ $t('general.calculateVat') }}
|
|
||||||
</label>
|
|
||||||
</section>
|
|
||||||
<div
|
|
||||||
class="surface-3 q-px-sm q-py-xs row text-caption app-text-muted"
|
|
||||||
style="border-radius: var(--radius-3)"
|
|
||||||
v-if="calcVat"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
id="btn-include-vat"
|
|
||||||
for="btn-include-vat"
|
|
||||||
class="q-px-sm q-mr-lg rounded cursor-pointer"
|
|
||||||
:class="{
|
|
||||||
dark: $q.dark.isActive,
|
|
||||||
'active-addr': vatIncluded,
|
|
||||||
'cursor-not-allowed': readonly,
|
|
||||||
}"
|
|
||||||
@click="readonly ? '' : (vatIncluded = true)"
|
|
||||||
>
|
|
||||||
{{ $t('productService.product.vatIncluded') }}
|
|
||||||
</span>
|
|
||||||
<span
|
|
||||||
id="btn-no-include-vat"
|
|
||||||
for="btn-no-include-vat"
|
|
||||||
class="q-px-sm rounded cursor-pointer"
|
|
||||||
:class="{
|
|
||||||
dark: $q.dark.isActive,
|
|
||||||
'active-addr': !vatIncluded,
|
|
||||||
'cursor-not-allowed': readonly,
|
|
||||||
}"
|
|
||||||
@click="readonly ? '' : (vatIncluded = false)"
|
|
||||||
>
|
|
||||||
{{ $t('productService.product.vatExcluded') }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="col-12 full-width">
|
<div class="col-12 full-width">
|
||||||
<q-table
|
<q-table
|
||||||
:columns="column"
|
|
||||||
:rows="row"
|
|
||||||
:rows-per-page-options="[0]"
|
:rows-per-page-options="[0]"
|
||||||
|
:rows="[
|
||||||
|
{
|
||||||
|
label: $t('productService.product.salePrice'),
|
||||||
|
pricePerUnit: price,
|
||||||
|
calcVat,
|
||||||
|
vatIncluded,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: $t('productService.product.agentPrice'),
|
||||||
|
calcVat: agentPriceCalcVat,
|
||||||
|
vatIncluded: agentPriceVatIncluded,
|
||||||
|
pricePerUnit: agentPrice,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: $t('productService.product.processingPrice'),
|
||||||
|
calcVat: serviceChargeCalcVat,
|
||||||
|
vatIncluded: serviceChargeVatIncluded,
|
||||||
|
pricePerUnit: serviceCharge,
|
||||||
|
},
|
||||||
|
]"
|
||||||
|
:columns
|
||||||
|
hide-bottom
|
||||||
bordered
|
bordered
|
||||||
flat
|
flat
|
||||||
hide-pagination
|
hide-pagination
|
||||||
|
selection="multiple"
|
||||||
class="full-width"
|
class="full-width"
|
||||||
:no-data-label="$t('general.noDataTable')"
|
|
||||||
>
|
>
|
||||||
<template v-slot:header="props">
|
<template v-slot:header="props">
|
||||||
<q-tr
|
<q-tr
|
||||||
style="background-color: hsla(var(--info-bg) / 0.07)"
|
style="background-color: hsla(var(--info-bg) / 0.07)"
|
||||||
:props="props"
|
:props="props"
|
||||||
>
|
>
|
||||||
<q-th v-for="col in props.cols" :key="col.name" :props="props">
|
<q-th v-for="col in columns" :key="col.name" :props="props">
|
||||||
{{ col.label && $t(col.label) }}
|
<template v-if="!!col.label">
|
||||||
|
{{ $t(col.label) }}
|
||||||
|
</template>
|
||||||
</q-th>
|
</q-th>
|
||||||
</q-tr>
|
</q-tr>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-slot:body="props">
|
<template
|
||||||
<q-tr>
|
v-slot:body="props: {
|
||||||
<q-td>{{ $t(props.row.label) }}</q-td>
|
row: RowData;
|
||||||
<q-td class="text-right" style="width: 15%">
|
} & Omit<Parameters<QTableSlots['body']>[0], 'row'>"
|
||||||
<q-input
|
>
|
||||||
v-if="priceDisplay?.price && props.rowIndex === 0"
|
<q-tr :class="{ dark: $q.dark.isActive }" class="text-center">
|
||||||
id="input-price"
|
<q-td v-for="(col, i) in columns" :align="col.align" :key="i">
|
||||||
for="input-price"
|
<!-- NOTE: custom column will starts with # -->
|
||||||
:dense="dense"
|
<template v-if="!col.name.startsWith('#')">
|
||||||
outlined
|
<span
|
||||||
:readonly="readonly"
|
v-if="col.name === 'total'"
|
||||||
:borderless="readonly"
|
class="text-weight-bold"
|
||||||
hide-bottom-space
|
:class="{
|
||||||
input-class="text-right"
|
['tags-color-orange']: props.rowIndex === 0,
|
||||||
:model-value="price4Show"
|
['tags-color-purple']: props.rowIndex === 1,
|
||||||
@blur="
|
['tags-color-pink']: props.rowIndex === 2,
|
||||||
() => {
|
['dark']: $q.dark.isActive,
|
||||||
price = Number(price4Show.replace(/,/g, ''));
|
}"
|
||||||
if (price % 1 === 0) {
|
>
|
||||||
const [, dec] = price4Show.split('.');
|
{{
|
||||||
if (!dec) {
|
typeof col.field === 'string'
|
||||||
price4Show += '.00';
|
? props.row[col.field as keyof RowData]
|
||||||
|
: col.field(props.row)
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
<template v-else>
|
||||||
|
{{
|
||||||
|
typeof col.field === 'string'
|
||||||
|
? props.row[col.field as keyof RowData]
|
||||||
|
: col.field(props.row)
|
||||||
|
}}
|
||||||
|
</template>
|
||||||
|
</template>
|
||||||
|
<template v-if="col.name === '#calcVat'">
|
||||||
|
<q-checkbox
|
||||||
|
v-if="priceDisplay?.price && props.rowIndex === 0"
|
||||||
|
v-model="calcVat"
|
||||||
|
size="xs"
|
||||||
|
/>
|
||||||
|
<q-checkbox
|
||||||
|
v-if="priceDisplay?.agentPrice && props.rowIndex === 1"
|
||||||
|
v-model="agentPriceCalcVat"
|
||||||
|
size="xs"
|
||||||
|
/>
|
||||||
|
<q-checkbox
|
||||||
|
v-if="priceDisplay?.serviceCharge && props.rowIndex === 2"
|
||||||
|
v-model="serviceChargeCalcVat"
|
||||||
|
size="xs"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template v-if="col.name === '#vatIncluded'">
|
||||||
|
<q-select
|
||||||
|
:options="[
|
||||||
|
{ label: $t('general.included'), value: true },
|
||||||
|
{ label: $t('general.notIncluded'), value: false },
|
||||||
|
]"
|
||||||
|
v-if="priceDisplay?.price && props.rowIndex === 0"
|
||||||
|
map-options
|
||||||
|
emit-value
|
||||||
|
flat
|
||||||
|
outlined
|
||||||
|
dense
|
||||||
|
v-model="vatIncluded"
|
||||||
|
></q-select>
|
||||||
|
<q-select
|
||||||
|
:options="[
|
||||||
|
{ label: $t('general.included'), value: true },
|
||||||
|
{ label: $t('general.notIncluded'), value: false },
|
||||||
|
]"
|
||||||
|
v-if="priceDisplay?.agentPrice && props.rowIndex === 1"
|
||||||
|
map-options
|
||||||
|
emit-value
|
||||||
|
flat
|
||||||
|
outlined
|
||||||
|
dense
|
||||||
|
v-model="agentPriceVatIncluded"
|
||||||
|
></q-select>
|
||||||
|
<q-select
|
||||||
|
:options="[
|
||||||
|
{ label: $t('general.included'), value: true },
|
||||||
|
{ label: $t('general.notIncluded'), value: false },
|
||||||
|
]"
|
||||||
|
v-if="priceDisplay?.serviceCharge && props.rowIndex === 2"
|
||||||
|
map-options
|
||||||
|
emit-value
|
||||||
|
flat
|
||||||
|
outlined
|
||||||
|
dense
|
||||||
|
v-model="serviceChargeVatIncluded"
|
||||||
|
></q-select>
|
||||||
|
</template>
|
||||||
|
<template v-if="col.name === '#pricePerUnit'">
|
||||||
|
<q-input
|
||||||
|
v-if="priceDisplay?.price && props.rowIndex === 0"
|
||||||
|
id="input-price"
|
||||||
|
for="input-price"
|
||||||
|
dense
|
||||||
|
outlined
|
||||||
|
hide-bottom-space
|
||||||
|
input-class="text-right"
|
||||||
|
:readonly
|
||||||
|
:borderless="readonly"
|
||||||
|
:model-value="formattedPrice"
|
||||||
|
@blur="
|
||||||
|
() => {
|
||||||
|
price = Number(formattedPrice.replace(/,/g, ''));
|
||||||
|
if (price % 1 === 0 && !formattedPrice.split('.').at(1)) {
|
||||||
|
formattedPrice += '.00';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
"
|
||||||
"
|
@update:model-value="
|
||||||
@update:model-value="
|
(v) => {
|
||||||
(v) => {
|
formattedPrice = commaInput(
|
||||||
price4Show = commaInput(v?.toString() || '0', 'string');
|
v?.toString() || '0',
|
||||||
}
|
'string',
|
||||||
"
|
);
|
||||||
/>
|
}
|
||||||
<q-input
|
"
|
||||||
v-if="priceDisplay?.agentPrice && props.rowIndex === 1"
|
/>
|
||||||
id="input-agent-price"
|
<q-input
|
||||||
for="input-agent-price"
|
v-if="priceDisplay?.agentPrice && props.rowIndex === 1"
|
||||||
:dense="dense"
|
id="input-agent-price"
|
||||||
outlined
|
for="input-agent-price"
|
||||||
:readonly="readonly"
|
dense
|
||||||
:borderless="readonly"
|
outlined
|
||||||
hide-bottom-space
|
hide-bottom-space
|
||||||
input-class="text-right"
|
input-class="text-right"
|
||||||
:model-value="agentPrice4Show"
|
:readonly
|
||||||
@blur="
|
:borderless="readonly"
|
||||||
() => {
|
:model-value="formattedAgentPrice"
|
||||||
agentPrice = Number(agentPrice4Show.replace(/,/g, ''));
|
@blur="
|
||||||
if (agentPrice % 1 === 0) {
|
() => {
|
||||||
const [, dec] = agentPrice4Show.split('.');
|
agentPrice = Number(
|
||||||
if (!dec) {
|
formattedAgentPrice.replace(/,/g, ''),
|
||||||
agentPrice4Show += '.00';
|
);
|
||||||
|
if (
|
||||||
|
agentPrice % 1 === 0 &&
|
||||||
|
!formattedAgentPrice.split('.').at(1)
|
||||||
|
) {
|
||||||
|
formattedAgentPrice += '.00';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
"
|
||||||
"
|
@update:model-value="
|
||||||
@update:model-value="
|
(v) => {
|
||||||
(v) => {
|
formattedAgentPrice = commaInput(
|
||||||
agentPrice4Show = commaInput(
|
v?.toString() || '0',
|
||||||
v?.toString() || '0',
|
'string',
|
||||||
'string',
|
);
|
||||||
);
|
}
|
||||||
}
|
"
|
||||||
"
|
/>
|
||||||
/>
|
<q-input
|
||||||
<q-input
|
v-if="priceDisplay?.serviceCharge && props.rowIndex === 2"
|
||||||
v-if="priceDisplay?.serviceCharge && props.rowIndex === 2"
|
id="input-service-charge"
|
||||||
id="input-service-charge"
|
for="input-service-charge"
|
||||||
for="input-service-charge"
|
dense
|
||||||
:dense="dense"
|
outlined
|
||||||
outlined
|
input-class="text-right"
|
||||||
:readonly="readonly"
|
hide-bottom-space
|
||||||
:borderless="readonly"
|
:readonly
|
||||||
input-class="text-right"
|
:borderless="readonly"
|
||||||
hide-bottom-space
|
:model-value="formattedServiceCharge"
|
||||||
:model-value="serviceCharge4Show"
|
@blur="
|
||||||
@blur="
|
() => {
|
||||||
() => {
|
serviceCharge = Number(
|
||||||
serviceCharge = Number(
|
formattedServiceCharge.replace(/,/g, ''),
|
||||||
serviceCharge4Show.replace(/,/g, ''),
|
);
|
||||||
);
|
if (
|
||||||
if (serviceCharge % 1 === 0) {
|
serviceCharge % 1 === 0 &&
|
||||||
const [, dec] = serviceCharge4Show.split('.');
|
!formattedServiceCharge.split('.').at(1)
|
||||||
if (!dec) {
|
) {
|
||||||
serviceCharge4Show += '.00';
|
formattedServiceCharge += '.00';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
"
|
||||||
"
|
@update:model-value="
|
||||||
@update:model-value="
|
(v) => {
|
||||||
(v) => {
|
formattedServiceCharge = commaInput(
|
||||||
serviceCharge4Show = commaInput(
|
v?.toString() || '0',
|
||||||
v?.toString() || '0',
|
'string',
|
||||||
'string',
|
);
|
||||||
);
|
}
|
||||||
}
|
"
|
||||||
"
|
/>
|
||||||
/>
|
</template>
|
||||||
</q-td>
|
|
||||||
<q-td class="text-right" style="width: 15%">
|
|
||||||
{{
|
|
||||||
formatNumberDecimal(
|
|
||||||
calculatePrice({
|
|
||||||
output: 'beforeVat',
|
|
||||||
vatIncluded: vatIncluded,
|
|
||||||
price:
|
|
||||||
(props.rowIndex === 0
|
|
||||||
? price
|
|
||||||
: props.rowIndex === 1
|
|
||||||
? agentPrice
|
|
||||||
: serviceCharge) || 0,
|
|
||||||
}),
|
|
||||||
2,
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</q-td>
|
|
||||||
<q-td class="text-right" style="width: 15%">
|
|
||||||
{{
|
|
||||||
formatNumberDecimal(
|
|
||||||
calculatePrice({
|
|
||||||
output: 'vat',
|
|
||||||
calcVat: calcVat,
|
|
||||||
price: Number(
|
|
||||||
formatNumberDecimal(
|
|
||||||
calculatePrice({
|
|
||||||
output: 'beforeVat',
|
|
||||||
vatIncluded: vatIncluded,
|
|
||||||
price:
|
|
||||||
(props.rowIndex === 0
|
|
||||||
? price
|
|
||||||
: props.rowIndex === 1
|
|
||||||
? agentPrice
|
|
||||||
: serviceCharge) || 0,
|
|
||||||
}),
|
|
||||||
2,
|
|
||||||
).replaceAll(',', ''),
|
|
||||||
),
|
|
||||||
}),
|
|
||||||
2,
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</q-td>
|
|
||||||
<q-td class="text-right" style="width: 15%">
|
|
||||||
<span
|
|
||||||
class="text-weight-bold"
|
|
||||||
:class="{
|
|
||||||
'tags-color-orange': props.rowIndex === 0,
|
|
||||||
'tags-color-purple': props.rowIndex === 1,
|
|
||||||
'tags-color-pink': props.rowIndex === 2,
|
|
||||||
dark: $q.dark.isActive,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
{{
|
|
||||||
formatNumberDecimal(
|
|
||||||
calculatePrice({
|
|
||||||
output: 'total',
|
|
||||||
vat: 0.03,
|
|
||||||
price:
|
|
||||||
(props.rowIndex === 0
|
|
||||||
? price
|
|
||||||
: props.rowIndex === 1
|
|
||||||
? agentPrice
|
|
||||||
: serviceCharge) || 0,
|
|
||||||
}) +
|
|
||||||
(!vatIncluded
|
|
||||||
? calculatePrice({
|
|
||||||
output: 'vat',
|
|
||||||
calcVat: calcVat,
|
|
||||||
price:
|
|
||||||
(props.rowIndex === 0
|
|
||||||
? price
|
|
||||||
: props.rowIndex === 1
|
|
||||||
? agentPrice
|
|
||||||
: serviceCharge) || 0,
|
|
||||||
})
|
|
||||||
: 0),
|
|
||||||
2,
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
</span>
|
|
||||||
</q-td>
|
</q-td>
|
||||||
</q-tr>
|
</q-tr>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -373,16 +404,6 @@ withDefaults(
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
.active-addr {
|
|
||||||
color: hsl(var(--info-bg));
|
|
||||||
background-color: hsla(var(--info-bg) / 0.1);
|
|
||||||
border-radius: var(--radius-3);
|
|
||||||
|
|
||||||
&.dark {
|
|
||||||
background-color: var(--surface-1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.tags-color-orange {
|
.tags-color-orange {
|
||||||
color: var(--orange-5);
|
color: var(--orange-5);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,7 @@ function calcPrice(c: (typeof rows.value)[number]) {
|
||||||
const finalPriceNoVat = finalPriceWithVat / (1 + (config.value?.vat || 0.07));
|
const finalPriceNoVat = finalPriceWithVat / (1 + (config.value?.vat || 0.07));
|
||||||
|
|
||||||
const price = finalPriceNoVat * c.amount - c.discount;
|
const price = finalPriceNoVat * c.amount - c.discount;
|
||||||
const vat = c.product.calcVat
|
const vat = c.product[props.agentPrice ? 'agentPriceCalcVat' : 'calcVat']
|
||||||
? (finalPriceNoVat * c.amount - c.discount) * (config.value?.vat || 0.07)
|
? (finalPriceNoVat * c.amount - c.discount) * (config.value?.vat || 0.07)
|
||||||
: 0;
|
: 0;
|
||||||
return precisionRound(price + vat);
|
return precisionRound(price + vat);
|
||||||
|
|
@ -387,7 +387,9 @@ watch(
|
||||||
{{
|
{{
|
||||||
formatNumberDecimal(
|
formatNumberDecimal(
|
||||||
props.row.pricePerUnit +
|
props.row.pricePerUnit +
|
||||||
(props.row.product.calcVat
|
(props.row.product[
|
||||||
|
agentPrice ? 'agentPriceCalcVat' : 'calcVat'
|
||||||
|
]
|
||||||
? props.row.pricePerUnit * (config?.vat || 0.07)
|
? props.row.pricePerUnit * (config?.vat || 0.07)
|
||||||
: 0),
|
: 0),
|
||||||
2,
|
2,
|
||||||
|
|
@ -443,7 +445,9 @@ watch(
|
||||||
<q-td align="right">
|
<q-td align="right">
|
||||||
{{
|
{{
|
||||||
formatNumberDecimal(
|
formatNumberDecimal(
|
||||||
props.row.product.calcVat
|
props.row.product[
|
||||||
|
agentPrice ? 'agentPriceCalcVat' : 'calcVat'
|
||||||
|
]
|
||||||
? precisionRound(
|
? precisionRound(
|
||||||
(props.row.pricePerUnit * props.row.amount -
|
(props.row.pricePerUnit * props.row.amount -
|
||||||
props.row.discount) *
|
props.row.discount) *
|
||||||
|
|
|
||||||
|
|
@ -145,6 +145,8 @@ export default {
|
||||||
quotationPayment: 'Used to display payment details',
|
quotationPayment: 'Used to display payment details',
|
||||||
orderDetail: 'To display the list of employees in that product',
|
orderDetail: 'To display the list of employees in that product',
|
||||||
ofPage: '{current} of {total}',
|
ofPage: '{current} of {total}',
|
||||||
|
included: 'Included',
|
||||||
|
notIncluded: 'Not Included',
|
||||||
},
|
},
|
||||||
|
|
||||||
menu: {
|
menu: {
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,8 @@ export default {
|
||||||
quotationPayment: 'ใช้แสดงรายละเอียดในการชำระเงิน',
|
quotationPayment: 'ใช้แสดงรายละเอียดในการชำระเงิน',
|
||||||
orderDetail: 'เพื่อแสดงรายชื่อลูกจ้างในสินค้านั้นๆ',
|
orderDetail: 'เพื่อแสดงรายชื่อลูกจ้างในสินค้านั้นๆ',
|
||||||
ofPage: '{current} จาก {total}',
|
ofPage: '{current} จาก {total}',
|
||||||
|
included: 'รวม',
|
||||||
|
notIncluded: 'ไม่รวม',
|
||||||
},
|
},
|
||||||
|
|
||||||
menu: {
|
menu: {
|
||||||
|
|
|
||||||
|
|
@ -1014,6 +1014,10 @@ const currentNoAction = ref(false);
|
||||||
const prevProduct = ref<ProductCreate>({
|
const prevProduct = ref<ProductCreate>({
|
||||||
expenseType: '',
|
expenseType: '',
|
||||||
vatIncluded: true,
|
vatIncluded: true,
|
||||||
|
agentPriceVatIncluded: true,
|
||||||
|
agentPriceCalcVat: true,
|
||||||
|
serviceChargeVatIncluded: true,
|
||||||
|
serviceChargeCalcVat: true,
|
||||||
productGroupId: '',
|
productGroupId: '',
|
||||||
remark: '',
|
remark: '',
|
||||||
serviceCharge: 0,
|
serviceCharge: 0,
|
||||||
|
|
@ -1050,6 +1054,10 @@ async function assignFormDataProduct(data: Product) {
|
||||||
status: data.status,
|
status: data.status,
|
||||||
expenseType: data.expenseType,
|
expenseType: data.expenseType,
|
||||||
vatIncluded: data.vatIncluded,
|
vatIncluded: data.vatIncluded,
|
||||||
|
serviceChargeCalcVat: data.serviceChargeCalcVat,
|
||||||
|
serviceChargeVatIncluded: data.serviceChargeVatIncluded,
|
||||||
|
agentPriceCalcVat: data.agentPriceCalcVat,
|
||||||
|
agentPriceVatIncluded: data.agentPriceVatIncluded,
|
||||||
selectedImage: data.selectedImage,
|
selectedImage: data.selectedImage,
|
||||||
document: data.document,
|
document: data.document,
|
||||||
shared: data.shared,
|
shared: data.shared,
|
||||||
|
|
@ -1086,6 +1094,10 @@ function clearFormProduct() {
|
||||||
expenseType: '',
|
expenseType: '',
|
||||||
calcVat: true,
|
calcVat: true,
|
||||||
vatIncluded: true,
|
vatIncluded: true,
|
||||||
|
agentPriceCalcVat: true,
|
||||||
|
agentPriceVatIncluded: true,
|
||||||
|
serviceChargeCalcVat: true,
|
||||||
|
serviceChargeVatIncluded: true,
|
||||||
shared: false,
|
shared: false,
|
||||||
};
|
};
|
||||||
imageProduct.value = undefined;
|
imageProduct.value = undefined;
|
||||||
|
|
@ -3900,7 +3912,12 @@ watch(
|
||||||
v-model:service-charge="formProduct.serviceCharge"
|
v-model:service-charge="formProduct.serviceCharge"
|
||||||
v-model:vat-included="formProduct.vatIncluded"
|
v-model:vat-included="formProduct.vatIncluded"
|
||||||
v-model:calc-vat="formProduct.calcVat"
|
v-model:calc-vat="formProduct.calcVat"
|
||||||
dense
|
v-model:agent-price-vat-included="formProduct.agentPriceVatIncluded"
|
||||||
|
v-model:agent-price-calc-vat="formProduct.agentPriceCalcVat"
|
||||||
|
v-model:service-charge-vat-included="
|
||||||
|
formProduct.serviceChargeVatIncluded
|
||||||
|
"
|
||||||
|
v-model:service-charge-calc-vat="formProduct.serviceChargeCalcVat"
|
||||||
/>
|
/>
|
||||||
<FormDocument
|
<FormDocument
|
||||||
v-if="productTab === 3"
|
v-if="productTab === 3"
|
||||||
|
|
@ -4109,7 +4126,12 @@ watch(
|
||||||
v-model:service-charge="formProduct.serviceCharge"
|
v-model:service-charge="formProduct.serviceCharge"
|
||||||
v-model:vat-included="formProduct.vatIncluded"
|
v-model:vat-included="formProduct.vatIncluded"
|
||||||
v-model:calc-vat="formProduct.calcVat"
|
v-model:calc-vat="formProduct.calcVat"
|
||||||
dense
|
v-model:agent-price-vat-included="formProduct.agentPriceVatIncluded"
|
||||||
|
v-model:agent-price-calc-vat="formProduct.agentPriceCalcVat"
|
||||||
|
v-model:service-charge-vat-included="
|
||||||
|
formProduct.serviceChargeVatIncluded
|
||||||
|
"
|
||||||
|
v-model:service-charge-calc-vat="formProduct.serviceChargeCalcVat"
|
||||||
:priceDisplay="priceDisplay"
|
:priceDisplay="priceDisplay"
|
||||||
/>
|
/>
|
||||||
<FormDocument
|
<FormDocument
|
||||||
|
|
|
||||||
|
|
@ -251,10 +251,13 @@ function getPrice(
|
||||||
const vat =
|
const vat =
|
||||||
(finalPriceNoVat * c.amount - c.discount) * (config.value?.vat || 0.07);
|
(finalPriceNoVat * c.amount - c.discount) * (config.value?.vat || 0.07);
|
||||||
|
|
||||||
|
const calcVat =
|
||||||
|
c.product[agentPrice.value ? 'agentPriceCalcVat' : 'calcVat'];
|
||||||
|
|
||||||
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 = c.product.calcVat ? precisionRound(a.vat + vat) : a.vat;
|
a.vat = calcVat ? precisionRound(a.vat + vat) : a.vat;
|
||||||
a.vatExcluded = c.product.calcVat
|
a.vatExcluded = calcVat
|
||||||
? a.vatExcluded
|
? a.vatExcluded
|
||||||
: precisionRound(a.vatExcluded + price);
|
: precisionRound(a.vatExcluded + price);
|
||||||
a.finalPrice = precisionRound(
|
a.finalPrice = precisionRound(
|
||||||
|
|
@ -424,6 +427,7 @@ async function fetchQuotation() {
|
||||||
const id = currentQuotationId.value || quotationFormData.value.id || '';
|
const id = currentQuotationId.value || quotationFormData.value.id || '';
|
||||||
|
|
||||||
await quotationForm.assignFormData(id, quotationFormState.value.mode);
|
await quotationForm.assignFormData(id, quotationFormState.value.mode);
|
||||||
|
|
||||||
tempPaySplitCount.value = quotationFormData.value.paySplitCount || 0;
|
tempPaySplitCount.value = quotationFormData.value.paySplitCount || 0;
|
||||||
tempPaySplit.value = JSON.parse(
|
tempPaySplit.value = JSON.parse(
|
||||||
JSON.stringify(quotationFormData.value.paySplit),
|
JSON.stringify(quotationFormData.value.paySplit),
|
||||||
|
|
@ -606,6 +610,7 @@ async function convertDataToFormSubmit() {
|
||||||
status: quotationFormData.value.status,
|
status: quotationFormData.value.status,
|
||||||
discount: quotationFormData.value.discount,
|
discount: quotationFormData.value.discount,
|
||||||
remark: quotationFormData.value.remark || '',
|
remark: quotationFormData.value.remark || '',
|
||||||
|
agentPrice: agentPrice.value,
|
||||||
};
|
};
|
||||||
|
|
||||||
newWorkerList.value = [];
|
newWorkerList.value = [];
|
||||||
|
|
@ -1119,6 +1124,7 @@ function storeDataLocal() {
|
||||||
},
|
},
|
||||||
selectedWorker: selectedWorker.value,
|
selectedWorker: selectedWorker.value,
|
||||||
createdBy: quotationFormState.value.createdBy('tha'),
|
createdBy: quotationFormState.value.createdBy('tha'),
|
||||||
|
agentPrice: agentPrice.value,
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -260,7 +260,11 @@ function mapNode() {
|
||||||
const price = prop.agentPrice
|
const price = prop.agentPrice
|
||||||
? p.product.agentPrice
|
? p.product.agentPrice
|
||||||
: p.product.price;
|
: p.product.price;
|
||||||
const pricePerUnit = p.product.vatIncluded
|
const pricePerUnit = (
|
||||||
|
prop.agentPrice
|
||||||
|
? p.product.agentPriceVatIncluded
|
||||||
|
: p.product.vatIncluded
|
||||||
|
)
|
||||||
? precisionRound(price / (1 + (config.value?.vat || 0.07)))
|
? precisionRound(price / (1 + (config.value?.vat || 0.07)))
|
||||||
: price;
|
: price;
|
||||||
productCount.value++;
|
productCount.value++;
|
||||||
|
|
@ -303,7 +307,11 @@ function mapNode() {
|
||||||
const price = prop.agentPrice
|
const price = prop.agentPrice
|
||||||
? p.product.agentPrice
|
? p.product.agentPrice
|
||||||
: p.product.price;
|
: p.product.price;
|
||||||
const pricePerUnit = p.product.vatIncluded
|
const pricePerUnit = (
|
||||||
|
prop.agentPrice
|
||||||
|
? p.product.agentPriceVatIncluded
|
||||||
|
: p.product.vatIncluded
|
||||||
|
)
|
||||||
? precisionRound(price / (1 + (config.value?.vat || 0.07)))
|
? precisionRound(price / (1 + (config.value?.vat || 0.07)))
|
||||||
: price;
|
: price;
|
||||||
productCount.value++;
|
productCount.value++;
|
||||||
|
|
@ -334,7 +342,9 @@ function mapNode() {
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
const price = prop.agentPrice ? v.raw.agentPrice : v.raw.price;
|
const price = prop.agentPrice ? v.raw.agentPrice : v.raw.price;
|
||||||
const pricePerUnit = v.raw.vatIncluded
|
const pricePerUnit = (
|
||||||
|
prop.agentPrice ? v.raw.agentPriceVatIncluded : v.raw.vatIncluded
|
||||||
|
)
|
||||||
? precisionRound(price / (1 + (config.value?.vat || 0.07)))
|
? precisionRound(price / (1 + (config.value?.vat || 0.07)))
|
||||||
: price;
|
: price;
|
||||||
productCount.value++;
|
productCount.value++;
|
||||||
|
|
|
||||||
|
|
@ -39,6 +39,7 @@ export const DEFAULT_DATA: QuotationPayload = {
|
||||||
_count: { worker: 0 },
|
_count: { worker: 0 },
|
||||||
status: 'CREATED',
|
status: 'CREATED',
|
||||||
remark: '#[quotation-labor]<br/><br/>#[quotation-payment]',
|
remark: '#[quotation-labor]<br/><br/>#[quotation-payment]',
|
||||||
|
agentPrice: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
const DEFAULT_DATA_INVOICE: InvoicePayload = {
|
const DEFAULT_DATA_INVOICE: InvoicePayload = {
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ import {
|
||||||
QuotationPayload,
|
QuotationPayload,
|
||||||
Details,
|
Details,
|
||||||
QuotationFull,
|
QuotationFull,
|
||||||
|
ProductRelation,
|
||||||
} from 'src/stores/quotations/types';
|
} from 'src/stores/quotations/types';
|
||||||
|
|
||||||
// NOTE: Import Components
|
// NOTE: Import Components
|
||||||
|
|
@ -40,11 +41,12 @@ type Product = {
|
||||||
code: string;
|
code: string;
|
||||||
detail: string;
|
detail: string;
|
||||||
amount: number;
|
amount: number;
|
||||||
priceUnit: number;
|
pricePerUnit: number;
|
||||||
discount: number;
|
discount: number;
|
||||||
vat: number;
|
vat: number;
|
||||||
value: number;
|
value: number;
|
||||||
calcVat: boolean;
|
calcVat: boolean;
|
||||||
|
product: ProductRelation;
|
||||||
};
|
};
|
||||||
|
|
||||||
type SummaryPrice = {
|
type SummaryPrice = {
|
||||||
|
|
@ -60,6 +62,8 @@ const branch = ref<Branch>();
|
||||||
const productList = ref<Product[]>([]);
|
const productList = ref<Product[]>([]);
|
||||||
const bankList = ref<BankBook[]>([]);
|
const bankList = ref<BankBook[]>([]);
|
||||||
|
|
||||||
|
const agentPrice = ref(false);
|
||||||
|
|
||||||
const elements = ref<HTMLElement[]>([]);
|
const elements = ref<HTMLElement[]>([]);
|
||||||
const chunks = ref<Product[][]>([[]]);
|
const chunks = ref<Product[][]>([[]]);
|
||||||
const attachmentList = ref<
|
const attachmentList = ref<
|
||||||
|
|
@ -194,6 +198,8 @@ onMounted(async () => {
|
||||||
customer.value = resCustomerBranch;
|
customer.value = resCustomerBranch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
agentPrice.value = data.value.agentPrice || parsed?.meta?.agentPrice;
|
||||||
|
|
||||||
details.value = {
|
details.value = {
|
||||||
code: parsed?.meta?.source?.code ?? data.value?.code,
|
code: parsed?.meta?.source?.code ?? data.value?.code,
|
||||||
createdAt:
|
createdAt:
|
||||||
|
|
@ -247,11 +253,12 @@ onMounted(async () => {
|
||||||
code: v.product.code,
|
code: v.product.code,
|
||||||
detail: v.product.name,
|
detail: v.product.name,
|
||||||
amount: v.amount || 0,
|
amount: v.amount || 0,
|
||||||
priceUnit: v.pricePerUnit || 0,
|
pricePerUnit: v.pricePerUnit || 0,
|
||||||
discount: v.discount || 0,
|
discount: v.discount || 0,
|
||||||
vat: v.vat || 0,
|
vat: v.vat || 0,
|
||||||
value: precisionRound(price + (v.product.calcVat ? vat : 0)),
|
value: precisionRound(price + (v.product.calcVat ? vat : 0)),
|
||||||
calcVat: v.product.calcVat,
|
calcVat: v.product.calcVat,
|
||||||
|
product: v.product,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
) || [];
|
) || [];
|
||||||
|
|
@ -273,10 +280,13 @@ onMounted(async () => {
|
||||||
const vat =
|
const vat =
|
||||||
(finalPriceNoVat * c.amount - c.discount) * (config.value?.vat || 0.07);
|
(finalPriceNoVat * c.amount - c.discount) * (config.value?.vat || 0.07);
|
||||||
|
|
||||||
|
const calcVat =
|
||||||
|
c.product[agentPrice.value ? 'agentPriceCalcVat' : 'calcVat'];
|
||||||
|
|
||||||
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 = c.product.calcVat ? precisionRound(a.vat + vat) : a.vat;
|
a.vat = calcVat ? precisionRound(a.vat + vat) : a.vat;
|
||||||
a.vatExcluded = c.product.calcVat
|
a.vatExcluded = calcVat
|
||||||
? a.vatExcluded
|
? a.vatExcluded
|
||||||
: precisionRound(a.vatExcluded + price);
|
: precisionRound(a.vatExcluded + price);
|
||||||
a.finalPrice = precisionRound(
|
a.finalPrice = precisionRound(
|
||||||
|
|
@ -285,7 +295,6 @@ onMounted(async () => {
|
||||||
a.vat -
|
a.vat -
|
||||||
Number(data.value?.discount || 0),
|
Number(data.value?.discount || 0),
|
||||||
);
|
);
|
||||||
|
|
||||||
return a;
|
return a;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -300,6 +309,20 @@ onMounted(async () => {
|
||||||
assignData();
|
assignData();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function calcPrice(c: Product) {
|
||||||
|
const originalPrice = c.pricePerUnit;
|
||||||
|
const finalPriceWithVat = precisionRound(
|
||||||
|
originalPrice + originalPrice * (config.value?.vat || 0.07),
|
||||||
|
);
|
||||||
|
const finalPriceNoVat = finalPriceWithVat / (1 + (config.value?.vat || 0.07));
|
||||||
|
|
||||||
|
const price = finalPriceNoVat * c.amount - c.discount;
|
||||||
|
const vat = c.product[agentPrice.value ? 'agentPriceCalcVat' : 'calcVat']
|
||||||
|
? (finalPriceNoVat * c.amount - c.discount) * (config.value?.vat || 0.07)
|
||||||
|
: 0;
|
||||||
|
return precisionRound(price + vat);
|
||||||
|
}
|
||||||
|
|
||||||
watch(elements, () => {});
|
watch(elements, () => {});
|
||||||
|
|
||||||
function print() {
|
function print() {
|
||||||
|
|
@ -359,7 +382,15 @@ function print() {
|
||||||
<td>{{ v.detail }}</td>
|
<td>{{ v.detail }}</td>
|
||||||
<td style="text-align: right">{{ v.amount }}</td>
|
<td style="text-align: right">{{ v.amount }}</td>
|
||||||
<td style="text-align: right">
|
<td style="text-align: right">
|
||||||
{{ formatNumberDecimal(v.priceUnit, 2) }}
|
{{
|
||||||
|
formatNumberDecimal(
|
||||||
|
v.pricePerUnit +
|
||||||
|
(v.product[agentPrice ? 'agentPriceCalcVat' : 'calcVat']
|
||||||
|
? v.pricePerUnit * (config?.vat || 0.07)
|
||||||
|
: 0),
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
}}
|
||||||
</td>
|
</td>
|
||||||
<td style="text-align: right">
|
<td style="text-align: right">
|
||||||
{{ formatNumberDecimal(v.discount, 2) }}
|
{{ formatNumberDecimal(v.discount, 2) }}
|
||||||
|
|
@ -367,9 +398,9 @@ function print() {
|
||||||
<td style="text-align: right">
|
<td style="text-align: right">
|
||||||
{{
|
{{
|
||||||
formatNumberDecimal(
|
formatNumberDecimal(
|
||||||
v.calcVat
|
v.product[agentPrice ? 'agentPriceCalcVat' : 'calcVat']
|
||||||
? precisionRound(
|
? precisionRound(
|
||||||
(v.priceUnit * v.amount - v.discount) *
|
(v.pricePerUnit * v.amount - v.discount) *
|
||||||
(config?.vat || 0.07),
|
(config?.vat || 0.07),
|
||||||
)
|
)
|
||||||
: 0,
|
: 0,
|
||||||
|
|
@ -378,7 +409,7 @@ function print() {
|
||||||
}}
|
}}
|
||||||
</td>
|
</td>
|
||||||
<td style="text-align: right">
|
<td style="text-align: right">
|
||||||
{{ formatNumberDecimal(v.value, 2) }}
|
{{ formatNumberDecimal(calcPrice(v), 2) }}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
</tbody>
|
</tbody>
|
||||||
|
|
|
||||||
|
|
@ -199,7 +199,7 @@ onMounted(async () => {
|
||||||
})
|
})
|
||||||
.reduce(
|
.reduce(
|
||||||
(a, c) => {
|
(a, c) => {
|
||||||
const priceNoVat = c.product.vatIncluded
|
const priceNoVat = c.product.serviceChargeVatIncluded
|
||||||
? c.pricePerUnit / (1 + (config.value?.vat || 0.07))
|
? c.pricePerUnit / (1 + (config.value?.vat || 0.07))
|
||||||
: c.pricePerUnit;
|
: c.pricePerUnit;
|
||||||
const adjustedPriceWithVat = precisionRound(
|
const adjustedPriceWithVat = precisionRound(
|
||||||
|
|
@ -219,9 +219,10 @@ onMounted(async () => {
|
||||||
priceUnit: precisionRound(priceNoVat),
|
priceUnit: precisionRound(priceNoVat),
|
||||||
amount: c.amount,
|
amount: c.amount,
|
||||||
discount: c.discount,
|
discount: c.discount,
|
||||||
vat: c.product.calcVat ? precisionRound(rawVat) : 0,
|
vat: c.product.serviceChargeCalcVat ? precisionRound(rawVat) : 0,
|
||||||
value: precisionRound(
|
value: precisionRound(
|
||||||
priceNoVat * c.amount + (c.product.calcVat ? rawVatTotal : 0),
|
priceNoVat * c.amount +
|
||||||
|
(c.product.serviceChargeCalcVat ? rawVatTotal : 0),
|
||||||
),
|
),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -56,18 +56,25 @@ function openList(index: number) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function calcPricePerUnit(product: RequestWork['productService']['product']) {
|
function calcPricePerUnit(product: RequestWork['productService']['product']) {
|
||||||
return product.vatIncluded
|
const val = props.creditNote
|
||||||
? (props.creditNote
|
? props.agentPrice
|
||||||
|
? product.agentPrice
|
||||||
|
: product.price
|
||||||
|
: product.serviceCharge;
|
||||||
|
|
||||||
|
if (
|
||||||
|
product[
|
||||||
|
props.creditNote
|
||||||
? props.agentPrice
|
? props.agentPrice
|
||||||
? product.agentPrice
|
? 'agentPriceCalcVat'
|
||||||
: product.price
|
: 'calcVat'
|
||||||
: product.serviceCharge) /
|
: 'serviceChargeCalcVat'
|
||||||
(1 + (config.value?.vat || 0.07))
|
]
|
||||||
: props.creditNote
|
) {
|
||||||
? props.agentPrice
|
return val / (1 + (config.value?.vat || 0.07));
|
||||||
? product.agentPrice
|
} else {
|
||||||
: product.price
|
return val;
|
||||||
: product.serviceCharge;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function calcPrice(
|
function calcPrice(
|
||||||
|
|
@ -81,12 +88,24 @@ function calcPrice(
|
||||||
: product.serviceCharge;
|
: product.serviceCharge;
|
||||||
const discount =
|
const discount =
|
||||||
taskProduct.value.find((v) => v.productId === product.id)?.discount || 0;
|
taskProduct.value.find((v) => v.productId === product.id)?.discount || 0;
|
||||||
const priceNoVat = product.vatIncluded
|
const priceNoVat = product[
|
||||||
|
props.creditNote
|
||||||
|
? props.agentPrice
|
||||||
|
? 'agentPriceVatIncluded'
|
||||||
|
: 'vatIncluded'
|
||||||
|
: 'serviceChargeVatIncluded'
|
||||||
|
]
|
||||||
? pricePerUnit / (1 + (config.value?.vat || 0.07))
|
? pricePerUnit / (1 + (config.value?.vat || 0.07))
|
||||||
: pricePerUnit;
|
: pricePerUnit;
|
||||||
const priceDiscountNoVat = priceNoVat * amount - discount;
|
const priceDiscountNoVat = priceNoVat * amount - discount;
|
||||||
|
|
||||||
const rawVatTotal = product.calcVat
|
const rawVatTotal = product[
|
||||||
|
props.creditNote
|
||||||
|
? props.agentPrice
|
||||||
|
? 'agentPriceCalcVat'
|
||||||
|
: 'calcVat'
|
||||||
|
: 'serviceChargeCalcVat'
|
||||||
|
]
|
||||||
? priceDiscountNoVat * (config.value?.vat || 0.07)
|
? priceDiscountNoVat * (config.value?.vat || 0.07)
|
||||||
: 0;
|
: 0;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -135,7 +135,7 @@ function getPrice(
|
||||||
const discount =
|
const discount =
|
||||||
taskProduct.value.find((v) => v.productId === c.product.id)?.discount ||
|
taskProduct.value.find((v) => v.productId === c.product.id)?.discount ||
|
||||||
0;
|
0;
|
||||||
const priceNoVat = c.product.vatIncluded
|
const priceNoVat = c.product.serviceChargeVatIncluded
|
||||||
? pricePerUnit / (1 + (config.value?.vat || 0.07))
|
? pricePerUnit / (1 + (config.value?.vat || 0.07))
|
||||||
: pricePerUnit;
|
: pricePerUnit;
|
||||||
const adjustedPriceWithVat = precisionRound(
|
const adjustedPriceWithVat = precisionRound(
|
||||||
|
|
@ -149,8 +149,8 @@ function getPrice(
|
||||||
|
|
||||||
a.totalPrice = a.totalPrice + priceDiscountNoVat;
|
a.totalPrice = a.totalPrice + priceDiscountNoVat;
|
||||||
a.totalDiscount = a.totalDiscount + Number(discount);
|
a.totalDiscount = a.totalDiscount + Number(discount);
|
||||||
a.vat = c.product.calcVat ? a.vat + rawVatTotal : a.vat;
|
a.vat = c.product.serviceChargeCalcVat ? a.vat + rawVatTotal : a.vat;
|
||||||
a.vatExcluded = c.product.calcVat
|
a.vatExcluded = c.product.serviceChargeCalcVat
|
||||||
? a.vatExcluded
|
? a.vatExcluded
|
||||||
: precisionRound(a.vatExcluded + priceDiscountNoVat);
|
: precisionRound(a.vatExcluded + priceDiscountNoVat);
|
||||||
a.finalPrice = a.totalPrice - a.totalDiscount + a.vat;
|
a.finalPrice = a.totalPrice - a.totalDiscount + a.vat;
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import AdditionalFileExpansion from '../09_task-order/expansion/AdditionalFileEx
|
||||||
import PaymentExpansion from './expansion/PaymentExpansion.vue';
|
import PaymentExpansion from './expansion/PaymentExpansion.vue';
|
||||||
import CreditNoteExpansion from './expansion/CreditNoteExpansion.vue';
|
import CreditNoteExpansion from './expansion/CreditNoteExpansion.vue';
|
||||||
import StateButton from 'src/components/button/StateButton.vue';
|
import StateButton from 'src/components/button/StateButton.vue';
|
||||||
import ProductExpansion from '../09_task-order/expansion/ProductExpansion.vue';
|
import ProductExpansion from './expansion/ProductExpansion.vue';
|
||||||
import SelectReadyRequestWork from '../09_task-order/SelectReadyRequestWork.vue';
|
import SelectReadyRequestWork from '../09_task-order/SelectReadyRequestWork.vue';
|
||||||
import RefundInformation from './RefundInformation.vue';
|
import RefundInformation from './RefundInformation.vue';
|
||||||
import QuotationFormReceipt from '../05_quotation/QuotationFormReceipt.vue';
|
import QuotationFormReceipt from '../05_quotation/QuotationFormReceipt.vue';
|
||||||
|
|
@ -111,7 +111,7 @@ const formTaskList = ref<
|
||||||
let taskListGroup = computed(() => {
|
let taskListGroup = computed(() => {
|
||||||
const cacheData = formTaskList.value.reduce<
|
const cacheData = formTaskList.value.reduce<
|
||||||
{
|
{
|
||||||
product: RequestWork['productService']['product'];
|
product: RequestWork['productService'];
|
||||||
list: RequestWork[];
|
list: RequestWork[];
|
||||||
}[]
|
}[]
|
||||||
>((acc, curr) => {
|
>((acc, curr) => {
|
||||||
|
|
@ -129,7 +129,7 @@ let taskListGroup = computed(() => {
|
||||||
exist.list.push(task.requestWork);
|
exist.list.push(task.requestWork);
|
||||||
} else {
|
} else {
|
||||||
acc.push({
|
acc.push({
|
||||||
product: task.requestWork.productService.product,
|
product: task.requestWork.productService,
|
||||||
list: [record],
|
list: [record],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
@ -189,37 +189,28 @@ async function initStatus() {
|
||||||
|
|
||||||
function getPrice(
|
function getPrice(
|
||||||
list: {
|
list: {
|
||||||
product: RequestWork['productService']['product'];
|
product: RequestWork['productService'];
|
||||||
list: RequestWork[];
|
list: RequestWork[];
|
||||||
}[],
|
}[],
|
||||||
) {
|
) {
|
||||||
return list.reduce(
|
return list.reduce(
|
||||||
(a, c) => {
|
(a, c) => {
|
||||||
const pricePerUnit = quotationData.value?.agentPrice
|
const pricePerUnit =
|
||||||
? c.product.agentPrice
|
c.product.pricePerUnit - c.product.discount / c.product.amount;
|
||||||
: c.product.price;
|
|
||||||
const amount = c.list.length;
|
const amount = c.list.length;
|
||||||
const discount = 0;
|
const priceNoVat = pricePerUnit;
|
||||||
const priceNoVat = c.product.vatIncluded
|
const priceDiscountNoVat = priceNoVat * amount;
|
||||||
? pricePerUnit / (1 + (config.value?.vat || 0.07))
|
|
||||||
: pricePerUnit;
|
|
||||||
const priceDiscountNoVat = priceNoVat * amount - discount;
|
|
||||||
|
|
||||||
const rawVatTotal = priceDiscountNoVat * (config.value?.vat || 0.07);
|
const rawVatTotal = priceDiscountNoVat * (config.value?.vat || 0.07);
|
||||||
// const rawVat = rawVatTotal / amount;
|
|
||||||
|
|
||||||
a.totalPrice = a.totalPrice + priceDiscountNoVat;
|
a.totalPrice = a.totalPrice + priceDiscountNoVat;
|
||||||
a.totalDiscount = a.totalDiscount + Number(discount);
|
a.vat = c.product.vat !== 0 ? a.vat + rawVatTotal : a.vat;
|
||||||
a.vat = c.product.calcVat ? a.vat + rawVatTotal : a.vat;
|
a.finalPrice = a.totalPrice + a.vat;
|
||||||
a.vatExcluded = c.product.calcVat ? a.vatExcluded : a.vat + rawVatTotal;
|
|
||||||
a.finalPrice = a.totalPrice - a.totalDiscount + a.vat;
|
|
||||||
return a;
|
return a;
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
totalPrice: 0,
|
totalPrice: 0,
|
||||||
totalDiscount: 0,
|
|
||||||
vat: 0,
|
vat: 0,
|
||||||
vatExcluded: 0,
|
|
||||||
finalPrice: 0,
|
finalPrice: 0,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
@ -749,12 +740,12 @@ onMounted(async () => {
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- TODO: bind remark -->
|
|
||||||
<RemarkExpansion
|
<RemarkExpansion
|
||||||
v-if="view !== CreditNoteStatus.Success"
|
v-if="view !== CreditNoteStatus.Success"
|
||||||
:readonly="readonly"
|
|
||||||
v-model:remark="formData.remark"
|
v-model:remark="formData.remark"
|
||||||
:default-remark="defaultRemark"
|
:default-remark="defaultRemark"
|
||||||
|
:items="[]"
|
||||||
|
:readonly
|
||||||
>
|
>
|
||||||
<template #hint>
|
<template #hint>
|
||||||
{{ $t('general.hintRemark') }}
|
{{ $t('general.hintRemark') }}
|
||||||
|
|
|
||||||
|
|
@ -76,3 +76,60 @@ export const hslaColors: Record<string, string> = {
|
||||||
Pending: '--orange-5-hsl',
|
Pending: '--orange-5-hsl',
|
||||||
Success: '--blue-6-hsl',
|
Success: '--blue-6-hsl',
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const productColumn = [
|
||||||
|
{
|
||||||
|
name: 'order',
|
||||||
|
align: 'center',
|
||||||
|
label: 'general.order',
|
||||||
|
field: 'no',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'code',
|
||||||
|
align: 'center',
|
||||||
|
label: 'productService.product.code',
|
||||||
|
field: 'code',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'productList',
|
||||||
|
align: 'center',
|
||||||
|
label: 'taskOrder.productList',
|
||||||
|
field: 'productList',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'amountOfEmployee',
|
||||||
|
align: 'center',
|
||||||
|
label: 'taskOrder.amountOfEmployee',
|
||||||
|
field: 'amountOfEmployee',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'pricePerUnit',
|
||||||
|
align: 'center',
|
||||||
|
label: 'quotation.pricePerUnit',
|
||||||
|
field: 'pricePerUnit',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'discount',
|
||||||
|
align: 'center',
|
||||||
|
label: 'general.discount',
|
||||||
|
field: 'discount',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'priceBeforeVat',
|
||||||
|
align: 'center',
|
||||||
|
label: 'quotation.priceBeforeVat',
|
||||||
|
field: 'priceBeforeVat',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'vat',
|
||||||
|
align: 'center',
|
||||||
|
label: 'general.vat',
|
||||||
|
field: 'vat',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'totalPriceBaht',
|
||||||
|
align: 'center',
|
||||||
|
label: 'quotation.totalPriceBaht',
|
||||||
|
field: 'totalPriceBaht',
|
||||||
|
},
|
||||||
|
] as const satisfies QTableProps['columns'];
|
||||||
|
|
|
||||||
232
src/pages/11_credit-note/expansion/ProductExpansion.vue
Normal file
232
src/pages/11_credit-note/expansion/ProductExpansion.vue
Normal file
|
|
@ -0,0 +1,232 @@
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { QTableSlots } from 'quasar';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
|
||||||
|
import { AddButton } from 'src/components/button';
|
||||||
|
import TableEmployee from '../../09_task-order/TableEmployee.vue';
|
||||||
|
|
||||||
|
import { useConfigStore } from 'stores/config';
|
||||||
|
import { RequestWork } from 'src/stores/request-list';
|
||||||
|
import { baseUrl, formatNumberDecimal } from 'src/stores/utils';
|
||||||
|
import { precisionRound } from 'src/utils/arithmetic';
|
||||||
|
import { productColumn } from '../constants';
|
||||||
|
|
||||||
|
const configStore = useConfigStore();
|
||||||
|
const { data: config } = storeToRefs(configStore);
|
||||||
|
|
||||||
|
const currentExpanded = ref<boolean[]>([]);
|
||||||
|
|
||||||
|
defineProps<{
|
||||||
|
readonly?: boolean;
|
||||||
|
taskList: {
|
||||||
|
product: RequestWork['productService'];
|
||||||
|
list: RequestWork[];
|
||||||
|
}[];
|
||||||
|
}>();
|
||||||
|
|
||||||
|
defineEmits<{
|
||||||
|
(e: 'addProduct'): void;
|
||||||
|
}>();
|
||||||
|
|
||||||
|
defineExpose({ calcPricePerUnit });
|
||||||
|
|
||||||
|
function openList(index: number) {
|
||||||
|
if (!currentExpanded.value[index]) {
|
||||||
|
currentExpanded.value.map((_, i) => {
|
||||||
|
if (i !== index) currentExpanded.value[i] = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
currentExpanded.value[index] = !currentExpanded.value[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
function calcPricePerUnit(product: RequestWork['productService']) {
|
||||||
|
return product.pricePerUnit - product.discount / product.amount;
|
||||||
|
}
|
||||||
|
|
||||||
|
function calcPrice(c: RequestWork['productService'], amount: number) {
|
||||||
|
const pricePerUnit = c.pricePerUnit - c.discount / c.amount;
|
||||||
|
const priceNoVat = pricePerUnit;
|
||||||
|
const priceDiscountNoVat = priceNoVat * amount;
|
||||||
|
|
||||||
|
const rawVatTotal =
|
||||||
|
c.vat === 0 ? 0 : priceDiscountNoVat * (config.value?.vat || 0.07);
|
||||||
|
|
||||||
|
return precisionRound(priceNoVat * amount + rawVatTotal);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<q-expansion-item
|
||||||
|
dense
|
||||||
|
class="overflow-hidden bordered full-width"
|
||||||
|
switch-toggle-side
|
||||||
|
style="border-radius: var(--radius-2)"
|
||||||
|
expand-icon="mdi-chevron-down-circle"
|
||||||
|
header-class="surface-1 q-py-sm text-medium text-body1"
|
||||||
|
default-opened
|
||||||
|
>
|
||||||
|
<template #header>
|
||||||
|
<span
|
||||||
|
class="row items-center justify-between full-width"
|
||||||
|
style="min-height: 31.01px"
|
||||||
|
>
|
||||||
|
{{ $t('general.information', { msg: $t('taskOrder.productList') }) }}
|
||||||
|
<AddButton
|
||||||
|
icon-only
|
||||||
|
@click.stop="$emit('addProduct')"
|
||||||
|
v-if="!readonly"
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<main class="q-px-md q-py-sm surface-1">
|
||||||
|
<q-table
|
||||||
|
:columns="
|
||||||
|
productColumn.filter(
|
||||||
|
(v) =>
|
||||||
|
v.name !== 'discount' &&
|
||||||
|
v.name !== 'priceBeforeVat' &&
|
||||||
|
v.name !== 'vat',
|
||||||
|
)
|
||||||
|
"
|
||||||
|
:rows="taskList"
|
||||||
|
bordered
|
||||||
|
flat
|
||||||
|
hide-pagination
|
||||||
|
card-container-class="q-col-gutter-sm"
|
||||||
|
class="full-width"
|
||||||
|
:rows-per-page-options="[0]"
|
||||||
|
:no-data-label="$t('general.noDataTable')"
|
||||||
|
>
|
||||||
|
<template v-slot:header="props">
|
||||||
|
<q-tr
|
||||||
|
style="background-color: hsla(var(--info-bg) / 0.07)"
|
||||||
|
:props="props"
|
||||||
|
>
|
||||||
|
<q-th v-for="col in props.cols" :key="col.name" :props="props">
|
||||||
|
{{ col.label && $t(col.label) }}
|
||||||
|
{{ col.label === 'quotation.vat' ? '%' : '' }}
|
||||||
|
</q-th>
|
||||||
|
<q-th></q-th>
|
||||||
|
</q-tr>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template
|
||||||
|
v-slot:body="props: {
|
||||||
|
row: {
|
||||||
|
product: RequestWork['productService'];
|
||||||
|
list: RequestWork[];
|
||||||
|
};
|
||||||
|
} & Omit<Parameters<QTableSlots['body']>[0], 'row'>"
|
||||||
|
>
|
||||||
|
<q-tr class="text-center">
|
||||||
|
<q-td>
|
||||||
|
{{ props.rowIndex + 1 }}
|
||||||
|
</q-td>
|
||||||
|
<q-td>
|
||||||
|
{{ props.row.product.product.code }}
|
||||||
|
</q-td>
|
||||||
|
<q-td style="width: 100%" class="text-left">
|
||||||
|
<q-avatar class="q-mr-sm" size="md">
|
||||||
|
<q-img
|
||||||
|
class="text-center"
|
||||||
|
:ratio="1"
|
||||||
|
:src="`${baseUrl}/product/${props.row.product.id}/image/${props.row.product.product.selectedImage}`"
|
||||||
|
>
|
||||||
|
<template #error>
|
||||||
|
<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)`"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</q-img>
|
||||||
|
</q-avatar>
|
||||||
|
{{ props.row.product.product.name }}
|
||||||
|
</q-td>
|
||||||
|
<q-td>
|
||||||
|
{{ props.row.list.length }}
|
||||||
|
</q-td>
|
||||||
|
<q-td class="text-right">
|
||||||
|
{{
|
||||||
|
formatNumberDecimal(
|
||||||
|
calcPricePerUnit(props.row.product) +
|
||||||
|
(props.row.product.vat > 0
|
||||||
|
? calcPricePerUnit(props.row.product) *
|
||||||
|
(config?.vat || 0.07)
|
||||||
|
: 0),
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</q-td>
|
||||||
|
|
||||||
|
<!-- total -->
|
||||||
|
<q-td class="text-right">
|
||||||
|
{{
|
||||||
|
formatNumberDecimal(
|
||||||
|
calcPrice(props.row.product, props.row.list.length),
|
||||||
|
2,
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
</q-td>
|
||||||
|
<q-td>
|
||||||
|
<q-btn
|
||||||
|
dense
|
||||||
|
flat
|
||||||
|
class="rounded"
|
||||||
|
@click.stop="openList(props.rowIndex)"
|
||||||
|
>
|
||||||
|
<div class="row items-center no-wrap">
|
||||||
|
<q-icon name="mdi-account-group-outline" />
|
||||||
|
<q-icon
|
||||||
|
class="btn-arrow-right"
|
||||||
|
:class="{
|
||||||
|
active: currentExpanded[props.rowIndex],
|
||||||
|
}"
|
||||||
|
size="xs"
|
||||||
|
:name="`mdi-chevron-${currentExpanded[props.rowIndex] ? 'down' : 'up'}`"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</q-btn>
|
||||||
|
</q-td>
|
||||||
|
</q-tr>
|
||||||
|
|
||||||
|
<q-tr v-show="currentExpanded[props.rowIndex]" :props="props">
|
||||||
|
<q-td colspan="100%" style="padding: 16px">
|
||||||
|
<TableEmployee :rows="props.row.list" />
|
||||||
|
</q-td>
|
||||||
|
</q-tr>
|
||||||
|
</template>
|
||||||
|
</q-table>
|
||||||
|
|
||||||
|
<div class="q-pt-md row items-center">
|
||||||
|
<span class="q-ml-auto q-mr-sm">
|
||||||
|
{{
|
||||||
|
$t('general.numberOf', {
|
||||||
|
msg: $t('productService.product.product'),
|
||||||
|
})
|
||||||
|
}}
|
||||||
|
</span>
|
||||||
|
<div class="surface-3 q-px-sm rounded">{{ taskList.length }}</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
</q-expansion-item>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
.product-status {
|
||||||
|
padding-left: 8px;
|
||||||
|
border-radius: 20px;
|
||||||
|
color: hsl(var(--_color));
|
||||||
|
background: hsla(var(--_color) / 0.15);
|
||||||
|
|
||||||
|
&.warning {
|
||||||
|
--_color: var(--warning-bg);
|
||||||
|
}
|
||||||
|
&.positive {
|
||||||
|
--_color: var(--positive-bg);
|
||||||
|
}
|
||||||
|
&.negative {
|
||||||
|
--_color: var(--negative-bg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -160,6 +160,10 @@ export interface Product {
|
||||||
calcVat: boolean;
|
calcVat: boolean;
|
||||||
expenseType: string;
|
expenseType: string;
|
||||||
vatIncluded: boolean;
|
vatIncluded: boolean;
|
||||||
|
agentPriceVatIncluded: boolean;
|
||||||
|
agentPriceCalcVat: boolean;
|
||||||
|
serviceChargeVatIncluded: boolean;
|
||||||
|
serviceChargeCalcVat: boolean;
|
||||||
remark: string;
|
remark: string;
|
||||||
updatedAt: string;
|
updatedAt: string;
|
||||||
updatedBy: UpdatedBy;
|
updatedBy: UpdatedBy;
|
||||||
|
|
@ -189,8 +193,12 @@ export interface ProductCreate {
|
||||||
productGroupId: string;
|
productGroupId: string;
|
||||||
remark: string;
|
remark: string;
|
||||||
serviceCharge: number;
|
serviceCharge: number;
|
||||||
|
serviceChargeCalcVat: boolean;
|
||||||
|
serviceChargeVatIncluded: boolean;
|
||||||
calcVat?: boolean;
|
calcVat?: boolean;
|
||||||
agentPrice: number;
|
agentPrice: number;
|
||||||
|
agentPriceCalcVat: boolean;
|
||||||
|
agentPriceVatIncluded: boolean;
|
||||||
price: number;
|
price: number;
|
||||||
process: number;
|
process: number;
|
||||||
detail: string;
|
detail: string;
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ export const useQuotationStore = defineStore('quotation-store', () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
async function editQuotation(data: QuotationPayload & { id: string }) {
|
async function editQuotation(data: QuotationPayload & { id: string }) {
|
||||||
const { _count, ...payload } = data;
|
const { _count, agentPrice, ...payload } = data;
|
||||||
const res = await api.put(`/quotation/${data.id}`, {
|
const res = await api.put(`/quotation/${data.id}`, {
|
||||||
...payload,
|
...payload,
|
||||||
quotationStatus:
|
quotationStatus:
|
||||||
|
|
|
||||||
|
|
@ -143,6 +143,10 @@ export type ProductRelation = {
|
||||||
serviceCharge: number;
|
serviceCharge: number;
|
||||||
vatIncluded: boolean;
|
vatIncluded: boolean;
|
||||||
calcVat: boolean;
|
calcVat: boolean;
|
||||||
|
agentPriceVatIncluded: boolean;
|
||||||
|
agentPriceCalcVat: boolean;
|
||||||
|
serviceChargeVatIncluded: boolean;
|
||||||
|
serviceChargeCalcVat: boolean;
|
||||||
expenseType: string;
|
expenseType: string;
|
||||||
status: Status;
|
status: Status;
|
||||||
statusOrder: number;
|
statusOrder: number;
|
||||||
|
|
@ -367,6 +371,7 @@ export type QuotationPayload = {
|
||||||
status?: Status;
|
status?: Status;
|
||||||
remark?: string;
|
remark?: string;
|
||||||
quotationStatus?: QuotationStatus;
|
quotationStatus?: QuotationStatus;
|
||||||
|
agentPrice?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type EmployeeWorker = {
|
export type EmployeeWorker = {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue