Compare commits

..

No commits in common. "develop" and "version-0.11.37" have entirely different histories.

109 changed files with 6984 additions and 8758 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 151 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 128 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 63 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 47 KiB

Before After
Before After

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Before After
Before After

View file

@ -159,6 +159,42 @@ function formatCode(input: string | undefined, type: 'code' | 'number') {
]" ]"
for="input-name-en" for="input-name-en"
/> />
<q-select
v-if="
typeBranch !== 'headOffice' &&
isRoleInclude(['head_of_admin', 'head_of_account'])
"
outlined
use-input
fill-input
emit-value
map-options
hide-selected
hide-bottom-space
input-debounce="0"
option-label="label"
option-value="value"
class="col-2"
dense
for="input-branch-status"
:readonly="readonly || isRoleInclude(['head_of_account'])"
:options="['Virtual', 'Branch']"
:hide-dropdown-icon="readonly"
:label="$t('general.branchStatus')"
:model-value="virtual ? 'Virtual' : 'Branch'"
@update:model-value="(v) => (virtual = v === 'Virtual')"
:rules="[(val) => val && val.length > 0]"
:error-message="$t('form.error.required')"
>
<template v-slot:no-option>
<q-item>
<q-item-section class="text-grey">
{{ $t('general.noData') }}
</q-item-section>
</q-item>
</template>
</q-select>
</div> </div>
<div class="col-12 row q-col-gutter-sm"> <div class="col-12 row q-col-gutter-sm">

View file

@ -293,11 +293,15 @@ watch(
:readonly="readonly" :readonly="readonly"
:label="$t('form.birthDate')" :label="$t('form.birthDate')"
:disabled-dates="disabledAfterToday" :disabled-dates="disabledAfterToday"
:rules="[ :rules="
employee
? []
: [
(val: string) => (val: string) =>
!!val || !!val ||
$t('form.error.selectField', { field: $t('form.birthDate') }), $t('form.error.selectField', { field: $t('form.birthDate') }),
]" ]
"
/> />
<q-input <q-input

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -20,8 +20,8 @@ const issuePlace = defineModel<string>('issuePlace');
const issueCountry = defineModel<string>('issueCountry'); const issueCountry = defineModel<string>('issueCountry');
const issueDate = defineModel<Date | null | string>('issueDate'); const issueDate = defineModel<Date | null | string>('issueDate');
const type = defineModel<string>('type'); const type = defineModel<string>('type');
const expireDate = defineModel<Date | string>('expireDate'); const expireDate = defineModel<Date>('expireDate');
const birthDate = defineModel<Date | string>('birthDate'); const birthDate = defineModel<Date>('birthDate');
const workerStatus = defineModel<string>('workerStatus'); const workerStatus = defineModel<string>('workerStatus');
const nationality = defineModel<string>('nationality'); const nationality = defineModel<string>('nationality');
const gender = defineModel<string>('gender'); const gender = defineModel<string>('gender');

View file

@ -28,12 +28,12 @@ const arrivalAt = defineModel<string>('arrivalAt');
const arrivalTMNo = defineModel<string>('arrivalTmNo'); const arrivalTMNo = defineModel<string>('arrivalTmNo');
const arrivalTM = defineModel<string>('arrivalTm'); const arrivalTM = defineModel<string>('arrivalTm');
const mrz = defineModel<string>('mrz'); const mrz = defineModel<string>('mrz');
const entryCount = defineModel<number | string>('entryCount'); const entryCount = defineModel<number>('entryCount');
const issuePlace = defineModel<string>('issuePlace'); const issuePlace = defineModel<string>('issuePlace');
const issueCountry = defineModel<string>('issueCountry'); const issueCountry = defineModel<string>('issueCountry');
const issueDate = defineModel<Date | null | string>('visaIssueDate'); const issueDate = defineModel<Date | null | string>('visaIssueDate');
const type = defineModel<string>('type'); const type = defineModel<string>('type');
const expireDate = defineModel<Date | string>('expireDate'); const expireDate = defineModel<Date>('expireDate');
const remark = defineModel<string>('remark'); const remark = defineModel<string>('remark');
const workerType = defineModel<string>('workerType'); const workerType = defineModel<string>('workerType');
const number = defineModel<string>('number'); const number = defineModel<string>('number');
@ -157,7 +157,7 @@ watch(
name="mdi-passport" name="mdi-passport"
style="background-color: var(--surface-3)" style="background-color: var(--surface-3)"
/> />
{{ $t(title) }} {{ title }}
</div> </div>
<div <div

View file

@ -141,9 +141,8 @@ defineEmits<{
<q-avatar size="md"> <q-avatar size="md">
<q-img <q-img
:src=" :src="
props.row.selectedImage `${baseUrl}/employee/${props.row.id}/image/${props.row.selectedImage}` ||
? `${baseUrl}/employee/${props.row.id}/image/${props.row.selectedImage}` `/images/employee-avatar-${props.row.gender}.png`
: `/images/employee-avatar-${props.row.gender}.png`
" "
class="text-center" class="text-center"
:ratio="1" :ratio="1"
@ -296,9 +295,9 @@ defineEmits<{
$i18n.locale === 'eng' $i18n.locale === 'eng'
? `${props.row.firstNameEN} ${props.row.lastNameEN} `.trim() ? `${props.row.firstNameEN} ${props.row.lastNameEN} `.trim()
: `${props.row.firstName} ${props.row.lastName} `.trim(), : `${props.row.firstName} ${props.row.lastName} `.trim(),
img: props.row.selectedImage img:
? `${baseUrl}/employee/${props.row.id}/image/${props.row.selectedImage}` `${baseUrl}/employee/${props.row.id}/image/${props.row.selectedImage}` ||
: `/images/employee-avatar-${props.row.gender}.png`, `/images/employee-avatar-${props.row.gender}.png`,
fallbackImg: `/images/employee-avatar-${props.row.gender}.png`, fallbackImg: `/images/employee-avatar-${props.row.gender}.png`,
male: props.row.gender === 'male', male: props.row.gender === 'male',
female: props.row.gender === 'female', female: props.row.gender === 'female',

View file

@ -2,18 +2,11 @@
import SelectCustomer from '../shared/select/SelectCustomer.vue'; import SelectCustomer from '../shared/select/SelectCustomer.vue';
import SelectBranch from '../shared/select/SelectBranch.vue'; import SelectBranch from '../shared/select/SelectBranch.vue';
import { CustomerBranch } from 'src/stores/customer';
import { ref } from 'vue';
const branchId = defineModel<string>('branchId'); const branchId = defineModel<string>('branchId');
const customerBranchId = defineModel<string>('customerBranchId'); const customerBranchId = defineModel<string>('customerBranchId');
const agentPrice = defineModel<boolean>('agentPrice'); const agentPrice = defineModel<boolean>('agentPrice');
const special = defineModel<boolean>('special'); const special = defineModel<boolean>('special');
const customerBranchOption = defineModel<CustomerBranch>(
'customerBranchOption',
);
defineProps<{ defineProps<{
outlined?: boolean; outlined?: boolean;
readonly?: boolean; readonly?: boolean;
@ -74,12 +67,10 @@ defineEmits<{
required required
:readonly :readonly
/> />
<SelectCustomer <SelectCustomer
id="about-select-customer-branch-id" id="about-select-customer-branch-id"
for="about-select-customer-branch-id" for="about-select-customer-branch-id"
v-model:value="customerBranchId" v-model:value="customerBranchId"
v-model:value-option="customerBranchOption"
:label="$t('quotation.customer')" :label="$t('quotation.customer')"
:creatable-disabled-text="`(${$t('form.error.selectField', { :creatable-disabled-text="`(${$t('form.error.selectField', {
field: $t('quotation.branchVirtual'), field: $t('quotation.branchVirtual'),

View file

@ -58,15 +58,16 @@ const currentBtnOpen = ref<{ title: string; opened: boolean[] }[]>([
function calcPrice(c: (typeof rows.value)[number]) { function calcPrice(c: (typeof rows.value)[number]) {
const originalPrice = c.pricePerUnit; const originalPrice = c.pricePerUnit;
const finalPricePerUnit = precisionRound( const finalPriceWithVat = precisionRound(
originalPrice + originalPrice + originalPrice * (config.value?.vat || 0.07),
(c.product[props.agentPrice ? 'agentPriceCalcVat' : 'calcVat']
? originalPrice * (config.value?.vat || 0.07)
: 0),
); );
const price = finalPricePerUnit * c.amount - c.discount; const finalPriceNoVat = finalPriceWithVat / (1 + (config.value?.vat || 0.07));
return precisionRound(price); const price = finalPriceNoVat * c.amount - c.discount;
const vat = c.product[props.agentPrice ? 'agentPriceCalcVat' : 'calcVat']
? (finalPriceNoVat * c.amount - c.discount) * (config.value?.vat || 0.07)
: 0;
return precisionRound(price + vat);
} }
const discount4Show = ref<string[]>([]); const discount4Show = ref<string[]>([]);
@ -434,20 +435,8 @@ watch(
<q-td align="right"> <q-td align="right">
{{ {{
formatNumberDecimal( formatNumberDecimal(
props.row.product[
agentPrice ? 'agentPriceCalcVat' : 'calcVat'
]
? precisionRound(
(props.row.pricePerUnit *
(1 + (config?.vat || 0.07)) *
props.row.amount -
props.row.discount) /
(1 + (config?.vat || 0.07)),
)
: precisionRound(
props.row.pricePerUnit * props.row.amount - props.row.pricePerUnit * props.row.amount -
props.row.discount, props.row.discount,
),
2, 2,
) )
}} }}
@ -459,12 +448,9 @@ watch(
agentPrice ? 'agentPriceCalcVat' : 'calcVat' agentPrice ? 'agentPriceCalcVat' : 'calcVat'
] ]
? precisionRound( ? precisionRound(
((props.row.pricePerUnit * (props.row.pricePerUnit * props.row.amount -
(1 + (config?.vat || 0.07)) * props.row.discount) *
props.row.amount - (config?.vat || 0.07),
props.row.discount) /
(1 + (config?.vat || 0.07))) *
0.07,
) )
: 0, : 0,
2, 2,

View file

@ -1,6 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { QTableProps } from 'quasar'; import { QTableProps } from 'quasar';
import { dateFormat, dateFormatJS } from 'src/utils/datetime'; import { dateFormat } from 'src/utils/datetime';
import { formatNumberDecimal } from 'stores/utils'; import { formatNumberDecimal } from 'stores/utils';
@ -86,11 +86,11 @@ defineEmits<{
</q-td> </q-td>
<q-td v-if="visibleColumns.includes('createdAt')"> <q-td v-if="visibleColumns.includes('createdAt')">
{{ dateFormatJS({ date: props.row.createdAt }) }} {{ dateFormat(props.row.createdAt) }}
</q-td> </q-td>
<q-td v-if="visibleColumns.includes('dueDate')"> <q-td v-if="visibleColumns.includes('dueDate')">
{{ dateFormatJS({ date: props.row.dueDate }) }} {{ dateFormat(props.row.dueDate) }}
</q-td> </q-td>
<q-td v-if="visibleColumns.includes('contactName')"> <q-td v-if="visibleColumns.includes('contactName')">

View file

@ -27,38 +27,26 @@ withDefaults(
class="app-text-muted q-pr-sm" class="app-text-muted q-pr-sm"
:width="iconSize || '2rem'" :width="iconSize || '2rem'"
/> />
<span :id="`dd-wrapper-${label}`" class="row col"> <span class="row col">
<span <span class="col-12 app-text-muted-2" style="font-size: 10px">
:id="`dd-label-${label}`"
class="col-12 app-text-muted-2"
style="font-size: 10px"
>
{{ label }} {{ label }}
</span> </span>
<span :id="`dd-value-wrapper-${label}`" class="col-12 ellipsis"> <span class="col-12 ellipsis">
<slot name="value"> <slot name="value">
<span <span
:class="{ 'link cursor-pointer': clickable }" :class="{ 'link cursor-pointer': clickable }"
v-if="typeof value === 'string'" v-if="typeof value === 'string'"
@click="clickable ? $emit('labelClick', value, null) : undefined" @click="clickable ? $emit('labelClick', value, null) : undefined"
:id="`link-${value}`"
:for="`link-${value}`"
> >
{{ value }} {{ value }}
<q-tooltip v-if="tooltip" :delay="500">{{ value }}</q-tooltip> <q-tooltip v-if="tooltip" :delay="500">{{ value }}</q-tooltip>
</span> </span>
<span <span v-else :class="{ 'link cursor-pointer': clickable }">
:id="`dd-value-${label}`"
v-else
:class="{ 'link cursor-pointer': clickable }"
>
<span <span
v-for="(item, index) in value" v-for="(item, index) in value"
:key="index" :key="index"
@click="$emit('labelClick', item, index)" @click="$emit('labelClick', item, index)"
class="link cursor-pointer" class="link cursor-pointer"
:id="`link-${item}`"
:for="`link-${item}`"
> >
{{ item }} {{ item }}
<span v-if="index < value.length - 1">,&nbsp;</span> <span v-if="index < value.length - 1">,&nbsp;</span>

View file

@ -8,7 +8,7 @@ import {
UndoButton, UndoButton,
} from 'components/button'; } from 'components/button';
const props = withDefaults( withDefaults(
defineProps<{ defineProps<{
title: string; title: string;
category?: string; category?: string;
@ -42,11 +42,6 @@ const drawerOpen = defineModel<boolean>('drawerOpen', {
const myForm = ref(); const myForm = ref();
function reset() { function reset() {
if (props.beforeClose) {
drawerOpen.value = props.beforeClose
? props.beforeClose()
: !drawerOpen.value;
}
if (myForm.value) { if (myForm.value) {
myForm.value.resetValidation(); myForm.value.resetValidation();
} }
@ -67,6 +62,7 @@ async function onValidationError(ref: any) {
@show="show" @show="show"
@before-hide="reset" @before-hide="reset"
@hide="close" @hide="close"
@update:model-value="(v) => (drawerOpen = beforeClose ? beforeClose() : v)"
:width="$q.screen.gt.xs ? windowSize * 0.85 : windowSize" :width="$q.screen.gt.xs ? windowSize * 0.85 : windowSize"
v-model="drawerOpen" v-model="drawerOpen"
behavior="mobile" behavior="mobile"

View file

@ -96,9 +96,7 @@ defineEmits<{
expandedTree[expandedTree.length - 1] === node.id, expandedTree[expandedTree.length - 1] === node.id,
}" }"
> >
{{ {{ node.name }}
$i18n.locale === 'eng' ? node.nameEN || node.name : node.name
}}
</span> </span>
<span class="app-text-muted text-caption ellipsis"> <span class="app-text-muted text-caption ellipsis">
{{ node.code }} {{ node.code }}

View file

@ -5,7 +5,6 @@ defineEmits<{
(e: 'click', v: MouseEvent): void; (e: 'click', v: MouseEvent): void;
}>(); }>();
defineProps<{ defineProps<{
id?: string;
icon?: string; icon?: string;
color: string; color: string;
iconOnly?: boolean; iconOnly?: boolean;
@ -19,7 +18,6 @@ defineProps<{
<template> <template>
<button <button
:id="id"
@click="(e) => $emit('click', e)" @click="(e) => $emit('click', e)"
class="main-btn" class="main-btn"
:class="{ :class="{

View file

@ -10,7 +10,6 @@ defineProps<{
outlined?: boolean; outlined?: boolean;
disabled?: boolean; disabled?: boolean;
dark?: boolean; dark?: boolean;
color?: string;
label?: string; label?: string;
icon?: string; icon?: string;
@ -24,7 +23,7 @@ defineProps<{
@click="(e) => $emit('click', e)" @click="(e) => $emit('click', e)"
v-bind="{ ...$props, ...$attrs }" v-bind="{ ...$props, ...$attrs }"
:icon="icon || 'mdi-content-save-outline'" :icon="icon || 'mdi-content-save-outline'"
:color="color || '207 96% 32%'" color="207 96% 32%"
:title="iconOnly ? $t('general.save') : undefined" :title="iconOnly ? $t('general.save') : undefined"
> >
{{ label || $t('general.save') }} {{ label || $t('general.save') }}

View file

@ -16,4 +16,3 @@ export { default as SideMenu } from './SideMenu.vue';
export { default as StatCardComponent } from './StatCardComponent.vue'; export { default as StatCardComponent } from './StatCardComponent.vue';
export { default as TooltipComponent } from './TooltipComponent.vue'; export { default as TooltipComponent } from './TooltipComponent.vue';
export { default as TreeComponent } from './TreeComponent.vue'; export { default as TreeComponent } from './TreeComponent.vue';
export { default as PaginationPageSize } from './PaginationPageSize.vue';

View file

@ -13,7 +13,6 @@ let defaultFilter: (
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
prefix?: string;
id?: string; id?: string;
label?: string; label?: string;
option: T[]; option: T[];
@ -72,7 +71,6 @@ watch(
</script> </script>
<template> <template>
<q-select <q-select
:id="id"
:placeholder="placeholder" :placeholder="placeholder"
outlined outlined
:clearable :clearable

View file

@ -75,9 +75,9 @@ function setDefaultValue() {
</script> </script>
<template> <template>
<SelectInput <SelectInput
for="select-hq-id"
v-model="value" v-model="value"
incremental incremental
id="select-hq-id"
:label :label
:placeholder :placeholder
:readonly :readonly

View file

@ -1,5 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted } from 'vue'; import { ref, onMounted } from 'vue';
import { getRole } from 'src/services/keycloak';
import { createSelect, SelectProps } from './select'; import { createSelect, SelectProps } from './select';
import SelectInput from '../SelectInput.vue'; import SelectInput from '../SelectInput.vue';
@ -94,103 +95,6 @@ function setDefaultValue() {
{{ (lang ?? $i18n.locale) !== 'eng' ? opt.name : opt.nameEN }} {{ (lang ?? $i18n.locale) !== 'eng' ? opt.name : opt.nameEN }}
</template> </template>
<template #no-option v-if="creatable">
<q-item
:disable="creatableDisabled"
clickable
v-close-popup
@click.stop="$emit('create')"
>
<q-item-section>
<span class="row items-center">
<q-icon
name="mdi-plus-circle-outline"
class="q-mr-sm"
style="color: hsl(var(--positive-bg))"
/>
<b>
{{ $t('general.add', { text: $t('businessType.title') }) }}
</b>
<span
v-if="creatableDisabled && creatableDisabledText"
class="app-text-muted q-pl-xs"
style="font-size: 80%"
>
{{ creatableDisabledText }}
</span>
</span>
</q-item-section>
</q-item>
<q-separator class="q-mx-sm" />
</template>
<template #before-options v-if="creatable">
<q-item
:disable="creatableDisabled"
clickable
v-close-popup
@click.stop="$emit('create')"
for="select-biz-type-add-new"
id="select-biz-type-add-new"
>
<q-item-section>
<span class="row items-center">
<q-icon
name="mdi-plus-circle-outline"
class="q-mr-sm"
style="color: hsl(var(--positive-bg))"
/>
<b>
{{ $t('general.add', { text: $t('businessType.title') }) }}
</b>
<span
v-if="creatableDisabled && creatableDisabledText"
class="app-text-muted q-pl-xs"
style="font-size: 80%"
>
{{ creatableDisabledText }}
</span>
</span>
</q-item-section>
</q-item>
<q-separator class="q-mx-sm" />
</template>
<template #before-options v-if="creatable">
<q-item
:disable="creatableDisabled"
clickable
v-close-popup
@click.stop="$emit('create')"
for="select-business-type-add-new"
id="select-business-type-add-new"
>
<q-item-section>
<span class="row items-center">
<q-icon
name="mdi-plus-circle-outline"
class="q-mr-sm"
style="color: hsl(var(--positive-bg))"
/>
<b>
{{ $t('general.add', { text: $t('menu.manage.businessType') }) }}
</b>
<span
v-if="creatableDisabled && creatableDisabledText"
class="app-text-muted q-pl-xs"
style="font-size: 80%"
>
{{ creatableDisabledText }}
</span>
</span>
</q-item-section>
</q-item>
<q-separator class="q-mx-sm" />
</template>
<template #option="{ opt, scope }"> <template #option="{ opt, scope }">
<q-item v-bind="scope.itemProps"> <q-item v-bind="scope.itemProps">
<span class="row items-center"> <span class="row items-center">

View file

@ -30,7 +30,6 @@ defineEmits<{
type ExclusiveProps = { type ExclusiveProps = {
simple?: boolean; simple?: boolean;
simpleBranchNo?: boolean; simpleBranchNo?: boolean;
selectFirstValue?: boolean;
}; };
const props = defineProps<SelectProps<typeof getList> & ExclusiveProps>(); const props = defineProps<SelectProps<typeof getList> & ExclusiveProps>();
@ -65,14 +64,10 @@ onMounted(async () => {
setFirstValue(); setFirstValue();
} }
if (props.selectFirstValue) { await getSelectedOption();
setDefaultValue();
} else await getSelectedOption();
});
function setDefaultValue() { valueOption.value = selectOptions.value.find((v) => v.id === value.value);
setFirstValue(); });
}
</script> </script>
<template> <template>
<SelectInput <SelectInput
@ -165,9 +160,11 @@ function setDefaultValue() {
</template> </template>
<template #option="{ opt, scope }"> <template #option="{ opt, scope }">
<q-item v-bind="scope.itemProps" class="q-mx-sm bodrder"> <q-item @click="valueOption = opt" v-bind="scope.itemProps">
<SelectCustomerItem :data="opt" :simple :simple-branch-no /> <SelectCustomerItem :data="opt" :simple :simple-branch-no />
</q-item> </q-item>
<q-separator class="q-mx-sm" />
</template> </template>
<template #append v-if="clearable"> <template #append v-if="clearable">
@ -180,11 +177,3 @@ function setDefaultValue() {
</template> </template>
</SelectInput> </SelectInput>
</template> </template>
<style scoped>
.bodrder {
border-bottom: solid;
border-bottom-width: 1px;
border-color: var(--border-color);
}
</style>

View file

@ -26,7 +26,6 @@ defineEmits<{
type ExclusiveProps = { type ExclusiveProps = {
selectFirstValue?: boolean; selectFirstValue?: boolean;
prefix?: string;
}; };
const props = defineProps<SelectProps<typeof getList> & ExclusiveProps>(); const props = defineProps<SelectProps<typeof getList> & ExclusiveProps>();
@ -72,7 +71,6 @@ function setDefaultValue() {
<SelectInput <SelectInput
v-model="value" v-model="value"
incremental incremental
:id="`${prefix || 'nome'}-select-user`"
:label :label
:placeholder :placeholder
:readonly :readonly

View file

@ -35,13 +35,7 @@ export const createSelect = <T extends Record<string, any>>(
let previousSearch = ''; let previousSearch = '';
watch(value, (v) => { watch(value, (v) => {
if (!v) return; if (!v || (cache && cache.find((opt) => opt[valueField] === v))) return;
if (cache && cache.find((opt) => opt[valueField] === v)) {
valueOption.value = cache.find((opt) => opt[valueField] === v);
return;
}
getSelectedOption(); getSelectedOption();
}); });
@ -69,26 +63,15 @@ export const createSelect = <T extends Record<string, any>>(
const currentValue = value.value; const currentValue = value.value;
if (!currentValue) return; if (!currentValue) return;
if (selectOptions.value.find((v) => v[valueField] === currentValue)) return;
if (valueOption.value && valueOption.value[valueField] === currentValue) { if (valueOption.value && valueOption.value[valueField] === currentValue) {
selectOptions.value.unshift(valueOption.value); return selectOptions.value.unshift(valueOption.value);
selectOptions.value = selectOptions.value.filter((curr, idx, arr) => {
return (
arr.findIndex((item) => item[valueField] === curr[valueField]) === idx
);
});
return;
} }
const ret = await getByValue(currentValue); const ret = await getByValue(currentValue);
if (ret) { if (ret) {
selectOptions.value.unshift(ret); selectOptions.value.unshift(ret);
selectOptions.value = selectOptions.value.filter((curr, idx, arr) => {
return (
arr.findIndex((item) => item[valueField] === curr[valueField]) === idx
);
});
valueOption.value = ret; valueOption.value = ret;
} }
} }

View file

@ -145,7 +145,6 @@ function selectedIndex(item: Employee) {
<template v-if="col.name === '#check'"> <template v-if="col.name === '#check'">
<q-checkbox <q-checkbox
id="select-worker-all" id="select-worker-all"
for="select-worker-all"
v-model="props.selected" v-model="props.selected"
@update:model-value="(v) => handleUpdate()" @update:model-value="(v) => handleUpdate()"
size="sm" size="sm"
@ -201,7 +200,6 @@ function selectedIndex(item: Employee) {
v-model="props.selected" v-model="props.selected"
size="sm" size="sm"
:id="`select-worker-${props.row.firstName}`" :id="`select-worker-${props.row.firstName}`"
:for="`select-worker-${props.row.firstName}`"
/> />
</template> </template>
</q-td> </q-td>

View file

@ -54,11 +54,10 @@ onMounted(() => {
@click="$emit('click')" @click="$emit('click')"
> >
<q-icon :name="icon" size="lg" :style="`color: ${color}`" /> <q-icon :name="icon" size="lg" :style="`color: ${color}`" />
<div class="col column q-pl-md"> <article class="col column q-pl-md">
<div class="ellipsis full-width" style="max-width: 65vw !important"> <span class="ellipsis full-width">
{{ name }} {{ name }}
</div> </span>
<span class="text-caption app-text-muted-2"> <span class="text-caption app-text-muted-2">
{{ {{
uploading.loaded uploading.loaded
@ -80,7 +79,7 @@ onMounted(() => {
/> />
{{ idle ? `Pending` : progress !== 1 ? `Uploading...` : 'Completed' }} {{ idle ? `Pending` : progress !== 1 ? `Uploading...` : 'Completed' }}
</span> </span>
</div> </article>
<q-btn <q-btn
v-if="closeable" v-if="closeable"
icon="mdi-close" icon="mdi-close"

View file

@ -45,9 +45,9 @@ const props = withDefaults(
readonly?: boolean; readonly?: boolean;
showTitle?: boolean; showTitle?: boolean;
ocr?: ( ocr?: (
group: string, group: any,
file: File, file: File,
) => Promise<{ ) => void | Promise<{
status: boolean; status: boolean;
group: string; group: string;
meta: { name: string; value: string }[]; meta: { name: string; value: string }[];
@ -168,8 +168,8 @@ async function change(e: Event) {
type: map['doc_type'], type: map['doc_type'],
number: map['doc_number'], number: map['doc_number'],
gender: map['sex'], gender: map['sex'],
firstName: map['last_name'], firstName: map['first_name'],
lastName: map['first_name'], lastName: map['last_name'],
issueDate: map['issue_date'], issueDate: map['issue_date'],
expireDate: map['expire_date'], expireDate: map['expire_date'],
issuePlace: map['nationality'], issuePlace: map['nationality'],

View file

@ -198,10 +198,3 @@ i.q-icon.mdi.mdi-chevron-down-circle.q-expansion-item__toggle-icon.q-expansion-i
.q-focus-helper { .q-focus-helper {
visibility: hidden; visibility: hidden;
} }
.clear-btn {
opacity: 0.6;
&:hover {
opacity: 1;
}
}

View file

@ -161,7 +161,6 @@ export default {
documentStatus: 'Document Status', documentStatus: 'Document Status',
advanceSearch: 'Advance Search', advanceSearch: 'Advance Search',
totalPeople: '{meg} people', totalPeople: '{meg} people',
price: 'Price {price} Baht',
}, },
menu: { menu: {
@ -341,7 +340,7 @@ export default {
requireLength: 'Please enter {msg} character', requireLength: 'Please enter {msg} character',
branchNameField: "Only letters, numbers, or the characters . , - ' &.", branchNameField: "Only letters, numbers, or the characters . , - ' &.",
branchNameENField: branchNameENField:
"Only English letters, numbers, or the characters . , - ' &. ( )", "Only English letters, numbers, or the characters . , - ' &.",
passportFormat: 'Please enter the passport number in the correct format.', passportFormat: 'Please enter the passport number in the correct format.',
}, },
warning: { warning: {
@ -508,7 +507,6 @@ export default {
miss: 'MISS.', miss: 'MISS.',
}, },
taxpayyerNo: 'Taxpayer Identification Number',
citizenId: 'Citizen ID', citizenId: 'Citizen ID',
religion: 'Religion', religion: 'Religion',
issueDate: 'Issue Date', issueDate: 'Issue Date',
@ -779,8 +777,6 @@ export default {
seller: 'Seller', seller: 'Seller',
paymentChannels: 'Payment Channels', paymentChannels: 'Payment Channels',
channelsThat: 'Channels That', channelsThat: 'Channels That',
refNo: 'Reference Number',
bankAccount: 'Bank Account',
bankAccountNumber: 'Bank Account Number', bankAccountNumber: 'Bank Account Number',
bankAccountName: 'Bank Account Name', bankAccountName: 'Bank Account Name',
inTheNameOf: 'In The Name Of', inTheNameOf: 'In The Name Of',
@ -1234,9 +1230,6 @@ export default {
taskListNotPending: 'One or more task is not pending.', taskListNotPending: 'One or more task is not pending.',
reqNotMet: 'Not Match', reqNotMet: 'Not Match',
systemError: 'A system error occurred.', systemError: 'A system error occurred.',
taskOrderInvalid: 'Please select the product and the organization.',
flowAccountProductIdNotFound: 'Product not found in flow account',
}, },
}, },

View file

@ -161,7 +161,6 @@ export default {
documentStatus: 'สถานะเอกสาร', documentStatus: 'สถานะเอกสาร',
advanceSearch: 'ค้นหาขั้นสูง', advanceSearch: 'ค้นหาขั้นสูง',
totalPeople: '{meg} คน', totalPeople: '{meg} คน',
price: 'ราคา {price} บาท',
}, },
menu: { menu: {
@ -338,7 +337,7 @@ export default {
letterAndNumOnly: 'โปรดใช้เฉพาะ _ ตัวอักษรภาษาอังกฤษและตัวเลขเท่านั้น', letterAndNumOnly: 'โปรดใช้เฉพาะ _ ตัวอักษรภาษาอังกฤษและตัวเลขเท่านั้น',
numOnly: 'โปรดใช้เฉพาะตัวเลขเท่านั้น', numOnly: 'โปรดใช้เฉพาะตัวเลขเท่านั้น',
requireLength: 'กรุณากรอกให้ครบ {msg} หลัก', requireLength: 'กรุณากรอกให้ครบ {msg} หลัก',
branchNameField: "โปรดใช้ตัวอักษร ตัวเลข หรือ . , - ' ( ) & เท่านั้น", branchNameField: "โปรดใช้ตัวอักษร ตัวเลข หรือ . , - ' & เท่านั้น",
branchNameENField: branchNameENField:
"โปรดใช้ตัวอักษรภาษาอังกฤษ ตัวเลข หรือ . , - ' & เท่านั้น", "โปรดใช้ตัวอักษรภาษาอังกฤษ ตัวเลข หรือ . , - ' & เท่านั้น",
passportFormat: 'กรุณากรอกหมายเลขพาสปอร์ตให้ถูกต้องตามรูปแบบ', passportFormat: 'กรุณากรอกหมายเลขพาสปอร์ตให้ถูกต้องตามรูปแบบ',
@ -509,7 +508,6 @@ export default {
religion: 'ศาสนา', religion: 'ศาสนา',
issueDate: 'วันที่ออกหนังสือ', issueDate: 'วันที่ออกหนังสือ',
passportExpiryDate: 'วันหiมดอายุหนังสือเดินทาง', passportExpiryDate: 'วันหiมดอายุหนังสือเดินทาง',
taxpayyerNo: 'เลขที่ประจำตัวผู้เสียภาษี',
ownerName: 'ชื่อนายจ้าง', ownerName: 'ชื่อนายจ้าง',
firstName: 'ชื่อ ', firstName: 'ชื่อ ',
@ -777,8 +775,6 @@ export default {
seller: 'ผู้ขาย', seller: 'ผู้ขาย',
paymentChannels: 'ช่องทางชำระเงิน', paymentChannels: 'ช่องทางชำระเงิน',
channelsThat: 'ช่องทางที่', channelsThat: 'ช่องทางที่',
refNo: 'เลขที่อ้างอิง',
bankAccount: 'บัญชีธนาคาร',
bankAccountNumber: 'เลขบัญชีธนาคาร', bankAccountNumber: 'เลขบัญชีธนาคาร',
bankAccountName: 'ชื่อบัญชี', bankAccountName: 'ชื่อบัญชี',
inTheNameOf: 'ในนาม', inTheNameOf: 'ในนาม',
@ -803,7 +799,7 @@ export default {
branch: 'สาขาที่ออกใบเสนอราคา', branch: 'สาขาที่ออกใบเสนอราคา',
branchVirtual: 'จุดรับบริการที่ออกใบเสนอราคา', branchVirtual: 'จุดรับบริการที่ออกใบเสนอราคา',
customer: 'ลูกค้า', customer: 'ลูกค้า',
newCustomer: 'แรงงานใหม่', newCustomer: 'ลูกค้าใหม่',
employeeList: 'รายชื่อแรงงาน', employeeList: 'รายชื่อแรงงาน',
employee: 'แรงงาน', employee: 'แรงงาน',
employeeName: 'ชื่อ-นามสกุล แรงงาน', employeeName: 'ชื่อ-นามสกุล แรงงาน',
@ -1220,8 +1216,6 @@ export default {
'มีงานหนึ่งงานหรือมากกว่าที่ไม่อยู่ในสถานะรอดำเนินการ', 'มีงานหนึ่งงานหรือมากกว่าที่ไม่อยู่ในสถานะรอดำเนินการ',
reqNotMet: 'ไม่ตรงกัน', reqNotMet: 'ไม่ตรงกัน',
systemError: 'ระบบเกิดข้อผิดพลาด', systemError: 'ระบบเกิดข้อผิดพลาด',
taskOrderInvalid: 'โปรดเลือก สินค้าเเละสาขา',
flowAccountProductIdNotFound: 'ไม่พบสินค้าใน flow account',
}, },
}, },

View file

@ -2,7 +2,7 @@
import { ref, onMounted, computed, reactive } from 'vue'; import { ref, onMounted, computed, reactive } from 'vue';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { getUserId, getUsername, getName, logout, getRole } from 'src/services/keycloak'; import { getUserId, getUsername, logout, getRole } from 'src/services/keycloak';
import { Icon } from '@iconify/vue'; import { Icon } from '@iconify/vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import moment from 'moment'; import moment from 'moment';
@ -39,7 +39,7 @@ const configStore = useConfigStore();
const { data: notificationData } = storeToRefs(notificationStore); const { data: notificationData } = storeToRefs(notificationStore);
const { visible } = storeToRefs(loaderStore); const { visible } = storeToRefs(loaderStore);
const { t, locale } = useI18n({ useScope: 'global' }); const { t } = useI18n({ useScope: 'global' });
const userStore = useUserStore(); const userStore = useUserStore();
const canvasModal = ref(false); const canvasModal = ref(false);
@ -52,14 +52,8 @@ const unread = computed<number>(
); );
const userImage = ref<string>(); const userImage = ref<string>();
const userGender = ref(''); const userGender = ref('');
const userName = ref({ th: '', en: '' });
const canvasRef = ref(); const canvasRef = ref();
const displayName = computed(() => {
if (!userName.value.th && !userName.value.en) return getName() || 'Guest';
return locale.value === 'eng' ? userName.value.en : userName.value.th;
});
const language: { const language: {
value: Lang; value: Lang;
label: string; label: string;
@ -167,15 +161,10 @@ onMounted(async () => {
if (user === 'admin') return; if (user === 'admin') return;
if (uid) { if (uid) {
const res = await userStore.fetchById(uid); const res = await userStore.fetchById(uid);
if (res) { if (res && res.gender) {
if (res.gender) {
userGender.value = res.gender; userGender.value = res.gender;
userImage.value = `${baseUrl}/user/${uid}/profile-image/${res.selectedImage}`; userImage.value = `${baseUrl}/user/${uid}/profile-image/${res.selectedImage}`;
} }
//
userName.value.th = `${res.firstName || ''} ${res.lastName || ''}`.trim();
userName.value.en = `${res.firstNameEN || ''} ${res.lastNameEN || ''}`.trim();
}
} }
}); });
</script> </script>
@ -495,7 +484,6 @@ onMounted(async () => {
<!-- User --> <!-- User -->
<ProfileMenu <ProfileMenu
id="btn-profile-menu" id="btn-profile-menu"
:user-name="displayName"
@logout="doLogout" @logout="doLogout"
@edit-personal-info="console.log('edit')" @edit-personal-info="console.log('edit')"
@signature=" @signature="

View file

@ -12,7 +12,6 @@ const filterRole = ref<string[]>();
defineProps<{ defineProps<{
userImage?: string; userImage?: string;
gender?: string; gender?: string;
userName?: string;
}>(); }>();
const inputFile = document.createElement('input'); const inputFile = document.createElement('input');
@ -148,9 +147,9 @@ onMounted(async () => {
class="text-weight-bold ellipsis" class="text-weight-bold ellipsis"
style="max-width: 9vw" style="max-width: 9vw"
> >
{{ userName || getName() }} {{ getName() }}
<q-tooltip> <q-tooltip>
{{ userName || getName() }} {{ getName() }}
</q-tooltip> </q-tooltip>
</span> </span>
<span <span
@ -235,12 +234,12 @@ onMounted(async () => {
style="margin-top: 58px" style="margin-top: 58px"
> >
<span v-if="isLoggedIn()"> <span v-if="isLoggedIn()">
{{ userName || getName() }} {{ getName() }}
</span> </span>
<span v-else>{{ 'Guest' }}</span> <span v-else>{{ 'Guest' }}</span>
<q-tooltip> <q-tooltip>
<span v-if="isLoggedIn()"> <span v-if="isLoggedIn()">
{{ userName || getName() }} {{ getName() }}
</span> </span>
<span v-else>{{ 'Guest' }}</span> <span v-else>{{ 'Guest' }}</span>
</q-tooltip> </q-tooltip>

View file

@ -18,8 +18,6 @@ import { CustomerBranch, CustomerType } from 'stores/customer/types';
import { columnsEmployee } from './constant'; import { columnsEmployee } from './constant';
import { useCustomerBranchForm, useEmployeeForm } from './form'; import { useCustomerBranchForm, useEmployeeForm } from './form';
import DialogEmployee from 'src/components/03_customer-management/DialogEmployee.vue';
import DrawerEmployee from 'src/components/03_customer-management/DrawerEmployee.vue';
import EmployerFormAuthorized from './components/employer/EmployerFormAuthorized.vue'; import EmployerFormAuthorized from './components/employer/EmployerFormAuthorized.vue';
import FloatingActionButton from 'components/FloatingActionButton.vue'; import FloatingActionButton from 'components/FloatingActionButton.vue';
import SideMenu from 'components/SideMenu.vue'; import SideMenu from 'components/SideMenu.vue';
@ -91,11 +89,6 @@ const prop = withDefaults(
currentCitizenId?: string; currentCitizenId?: string;
gender: string; gender: string;
selectedImage: string; selectedImage: string;
fetchImageList: (
id: string,
selectedName: string,
type: 'customer' | 'employee',
) => Promise<void>;
}>(), }>(),
{ {
color: 'green', color: 'green',
@ -103,6 +96,7 @@ const prop = withDefaults(
); );
const currentBranchEmployee = ref<string>(''); const currentBranchEmployee = ref<string>('');
const listEmployee = ref<Employee[]>([]); const listEmployee = ref<Employee[]>([]);
const customerId = defineModel<string>('customerId', { required: true }); const customerId = defineModel<string>('customerId', { required: true });
defineEmits<{ defineEmits<{
@ -112,6 +106,16 @@ defineEmits<{
(e: 'dialog'): void; (e: 'dialog'): void;
}>(); }>();
onMounted(async () => {
customerBranchFormState.value.currentCustomerId = route.params
.customerId as string;
await fetchList();
branch.value?.forEach((v) => {
currentBtnOpen.value.push(false);
});
});
const columns = [ const columns = [
{ {
name: 'branchName', name: 'branchName',
@ -253,6 +257,10 @@ async function fetchEmployee(opts: { branchId: string; pageSize?: number }) {
} }
} }
onMounted(async () => {
await fetchList();
});
watch([customerId, inputSearch, currentStatus, pageSizeBranch], async () => { watch([customerId, inputSearch, currentStatus, pageSizeBranch], async () => {
await fetchList(); await fetchList();
}); });
@ -272,16 +280,6 @@ watch(
} }
}, },
); );
onMounted(async () => {
customerBranchFormState.value.currentCustomerId = route.params
.customerId as string;
await fetchList();
branch.value?.forEach((v) => {
currentBtnOpen.value.push(false);
});
});
</script> </script>
<template> <template>
@ -475,6 +473,7 @@ onMounted(async () => {
<q-tr <q-tr
:class="{ :class="{
'app-text-muted': props.row.status === 'INACTIVE', 'app-text-muted': props.row.status === 'INACTIVE',
'cursor-pointer': props.row._count?.branch !== 0,
}" }"
:props="props" :props="props"
@click="$emit('viewDetail', props.row, props.rowIndex)" @click="$emit('viewDetail', props.row, props.rowIndex)"
@ -548,13 +547,7 @@ onMounted(async () => {
v-if="branchFieldSelected.includes('businessTypePure')" v-if="branchFieldSelected.includes('businessTypePure')"
class="text-left" class="text-left"
> >
{{ {{ useOptionStore().mapOption(props.row.businessType) || '-' }}
props.row.businessType
? props.row.businessType[
$i18n.locale === 'eng' ? 'nameEN' : 'name'
]
: '-'
}}
</q-td> </q-td>
<q-td <q-td
v-if="branchFieldSelected.includes('totalEmployee')" v-if="branchFieldSelected.includes('totalEmployee')"
@ -573,6 +566,8 @@ onMounted(async () => {
await fetchEmployee({ await fetchEmployee({
branchId: currentBranchEmployee, branchId: currentBranchEmployee,
pageSize: 999, pageSize: 999,
passport: true,
visa: true,
}); });
currentBtnOpen.map((v, i) => { currentBtnOpen.map((v, i) => {
@ -643,15 +638,10 @@ onMounted(async () => {
" "
@history="(item) => {}" @history="(item) => {}"
@view=" @view="
async (item) => { (item) => {
employeeFormState.drawerModal = true; employeeFormState.drawerModal = true;
//employeeFormState.isEmployeeEdit = true; //employeeFormState.isEmployeeEdit = true;
employeeFormStore.assignFormDataEmployee(item.id); employeeFormStore.assignFormDataEmployee(item.id);
await fetchImageList(
item.id,
item.selectedImage || '',
'employee',
);
} }
" "
/> />
@ -678,11 +668,9 @@ onMounted(async () => {
? `${props.row.addressEN || ''} ${props.row.subDistrict?.nameEN || ''} ${props.row.district?.nameEN || ''} ${props.row.province?.nameEN || ''}` ? `${props.row.addressEN || ''} ${props.row.subDistrict?.nameEN || ''} ${props.row.district?.nameEN || ''} ${props.row.province?.nameEN || ''}`
: `${props.row.address || ''} ${props.row.subDistrict?.name || ''} ${props.row.district?.name || ''} ${props.row.province?.name || ''}`, : `${props.row.address || ''} ${props.row.subDistrict?.name || ''} ${props.row.district?.name || ''} ${props.row.province?.name || ''}`,
telephone: props.row.telephoneNo, telephone: props.row.telephoneNo,
businessTypePure: props.row.businessType businessTypePure: useOptionStore().mapOption(
? props.row.businessType[ props.row.businessType,
$i18n.locale === 'eng' ? 'nameEN' : 'name' ),
]
: '-',
totalEmployee: props.row._count?.employee, totalEmployee: props.row._count?.employee,
}" }"
:visible-columns="branchFieldSelected" :visible-columns="branchFieldSelected"
@ -897,14 +885,15 @@ onMounted(async () => {
</div> </div>
<EmployerFormBusiness <EmployerFormBusiness
dense dense
class="q-mb-xl"
outlined outlined
prefix-id="employer-branch" prefix-id="employer-branch"
:readonly="customerBranchFormState.dialogType === 'info'" :readonly="customerBranchFormState.dialogType === 'info'"
v-model:business-type-id="customerBranchFormData.businessTypeId" v-model:bussiness-type="customerBranchFormData.businessType"
v-model:job-position="customerBranchFormData.jobPosition" v-model:job-position="customerBranchFormData.jobPosition"
v-model:job-description="customerBranchFormData.jobDescription" v-model:job-description="customerBranchFormData.jobDescription"
v-model:pay-date="customerBranchFormData.payDate" v-model:pay-date="customerBranchFormData.payDate"
v-model:pay-date-en="customerBranchFormData.payDateEN" v-model:pay-date-e-n="customerBranchFormData.payDateEN"
v-model:wage-rate="customerBranchFormData.wageRate" v-model:wage-rate="customerBranchFormData.wageRate"
v-model:wage-rate-text="customerBranchFormData.wageRateText" v-model:wage-rate-text="customerBranchFormData.wageRateText"
/> />
@ -995,7 +984,7 @@ onMounted(async () => {
v-model:email="customerBranchFormData.email" v-model:email="customerBranchFormData.email"
v-model:contact-tel="customerBranchFormData.contactTel" v-model:contact-tel="customerBranchFormData.contactTel"
v-model:office-tel="customerBranchFormData.officeTel" v-model:office-tel="customerBranchFormData.officeTel"
v-model:agent-user-id="customerBranchFormData.agentUserId" v-model:agent="customerBranchFormData.agent"
/> />
</div> </div>
</div> </div>
@ -1011,18 +1000,6 @@ onMounted(async () => {
/> />
</template> </template>
</DialogFormContainer> </DialogFormContainer>
<DialogEmployee
:fetch-list-employee="fetchEmployee"
:fetch-image-list="fetchImageList"
current-tab="employer"
/>
<DrawerEmployee
:fetch-list-employee="fetchEmployee"
:fetch-image-list="fetchImageList"
current-tab="employer"
/>
</template> </template>
<style scoped> <style scoped>

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

View file

@ -1,514 +0,0 @@
<script setup lang="ts">
import { computed, onMounted, ref, watch } from 'vue';
import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n';
import { storeToRefs } from 'pinia';
import {
PaginationComponent,
PaginationPageSize,
ImageUploadDialog,
DialogForm,
NoData,
} from 'src/components';
import DialogEmployee from 'src/components/03_customer-management/DialogEmployee.vue';
import HistoryEditComponent from 'src/components/03_customer-management/HistoryEditComponent.vue';
import TableEmpoloyee from 'src/components/03_customer-management/TableEmpoloyee.vue';
import DrawerEmployee from 'src/components/03_customer-management/DrawerEmployee.vue';
import useFlowStore from 'src/stores/flow';
import useEmployeeStore from 'src/stores/employee';
import { Status } from 'src/stores/types';
import { Employee, EmployeeHistory } from 'src/stores/employee/types';
import { baseUrl, dialog, canAccess } from 'src/stores/utils';
import { calculateAge, toISOStringWithTimezone } from 'src/utils/datetime';
import { useCustomerForm, useEmployeeForm } from './form';
import { columnsEmployee } from './constant';
const $q = useQuasar();
const flowStore = useFlowStore();
const employeeStore = useEmployeeStore();
const employeeFormStore = useEmployeeForm();
const customerFormStore = useCustomerForm();
const { t } = useI18n();
const { state: employeeFormState, currentFromDataEmployee } =
storeToRefs(employeeFormStore);
const { deleteEmployeeById } = employeeFormStore;
const { state: customerFormState } = storeToRefs(customerFormStore);
const employeeStats = defineModel('employeeStats', { default: 0 });
const props = defineProps<{
currentTab: 'employee' | 'employer';
currentStatus: Status | 'All';
gridView: boolean;
inputSearch: string;
searchDate: string[];
fieldSelected: string[];
fetchImageList: (
id: string,
selectedName: string,
type: 'customer' | 'employee',
) => Promise<void>;
triggerChangeStatus: (
id: string,
status: string,
employeeName?: string,
) => void;
}>();
defineExpose({
openSpecificEmployee,
fetchListEmployee,
toggleStatusEmployee,
});
const listEmployee = ref<Employee[]>([]);
const currentPageEmployee = ref<number>(1);
const maxPageEmployee = ref<number>(1);
const pageSize = ref<number>(30);
const employeeHistoryDialog = ref(false);
const employeeHistory = ref<EmployeeHistory[]>();
const splitPercent = computed(() => ($q.screen.lt.md ? 0 : 15));
async function fetchListEmployee(opt?: {
fetchStats?: boolean;
page?: number;
pageSize?: number;
customerId?: string;
mobileFetch?: boolean;
}) {
const resultListEmployee = await employeeStore.fetchList({
customerId: opt?.customerId,
page: opt
? opt.mobileFetch
? 1
: opt.page || currentPageEmployee.value
: currentPageEmployee.value,
pageSize: opt
? opt.mobileFetch
? listEmployee.value.length +
(employeeStats.value === listEmployee.value.length ? 1 : 0)
: opt.pageSize || pageSize.value
: pageSize.value,
status:
props.currentStatus === 'All'
? undefined
: props.currentStatus === 'ACTIVE'
? 'ACTIVE'
: 'INACTIVE',
query: props.inputSearch,
passport: true,
visa: true,
startDate: props.searchDate[0],
endDate: props.searchDate[1],
});
if (resultListEmployee) {
maxPageEmployee.value = Math.ceil(
resultListEmployee.total / pageSize.value,
);
$q.screen.xs && !(opt && opt.mobileFetch)
? listEmployee.value.push(...resultListEmployee.result)
: (listEmployee.value = resultListEmployee.result);
}
if (opt && opt.fetchStats)
employeeStats.value = await employeeStore.getStatsEmployee();
}
async function openHistory(id: string) {
const res = await employeeStore.getEditHistory(id);
employeeHistory.value = res.reverse();
employeeHistoryDialog.value = true;
}
async function editEmployeeFormPersonal(id: string) {
await employeeFormStore.assignFormDataEmployee(id);
await props.fetchImageList(
id,
currentFromDataEmployee.value.selectedImage || '',
'employee',
);
employeeFormState.value.isEmployeeEdit = true;
employeeFormState.value.dialogType = 'edit';
employeeFormState.value.drawerModal = true;
}
async function openSpecificEmployee(id: string) {
await employeeFormStore.assignFormDataEmployee(id);
await props.fetchImageList(
id,
currentFromDataEmployee.value.selectedImage || '',
'employee',
);
employeeFormState.value.dialogType = 'info';
employeeFormState.value.drawerModal = true;
}
async function toggleStatusEmployee(
id: string,
status: boolean,
employeeName: string,
) {
const res = await employeeStore.editById(id, {
status: !status ? 'ACTIVE' : 'INACTIVE',
firstNameEN: employeeName,
});
if (res && employeeFormState.value.drawerModal) {
currentFromDataEmployee.value.status = res.status;
}
await employeeFormStore.assignFormDataEmployee(id);
await fetchListEmployee({ mobileFetch: $q.screen.xs });
flowStore.rotate();
}
watch(
() => [
props.inputSearch,
props.searchDate,
props.currentStatus,
pageSize.value,
],
async () => {
currentPageEmployee.value = 1;
listEmployee.value = [];
await fetchListEmployee({ fetchStats: true });
customerFormState.value.currentCustomerId = undefined;
flowStore.rotate();
},
);
watch(
() => employeeFormState.value.currentCustomerBranch,
(e) => {
if (!e) return;
if (employeeFormState.value.formDataEmployeeSameAddr) {
currentFromDataEmployee.value.address = e.address;
currentFromDataEmployee.value.addressEN = e.addressEN;
currentFromDataEmployee.value.provinceId = e.provinceId;
currentFromDataEmployee.value.districtId = e.districtId;
currentFromDataEmployee.value.subDistrictId = e.subDistrictId;
}
currentFromDataEmployee.value.customerBranchId = e.id;
},
);
watch(
() => employeeFormState.value.formDataEmployeeSameAddr,
(isSame) => {
if (!employeeFormState.value.currentCustomerBranch) return;
if (isSame) {
currentFromDataEmployee.value.address =
employeeFormState.value.currentCustomerBranch.address;
currentFromDataEmployee.value.addressEN =
employeeFormState.value.currentCustomerBranch.addressEN;
currentFromDataEmployee.value.provinceId =
employeeFormState.value.currentCustomerBranch.provinceId;
currentFromDataEmployee.value.districtId =
employeeFormState.value.currentCustomerBranch.districtId;
currentFromDataEmployee.value.subDistrictId =
employeeFormState.value.currentCustomerBranch.subDistrictId;
}
currentFromDataEmployee.value.customerBranchId =
employeeFormState.value.currentCustomerBranch.id;
},
);
watch(
() => currentFromDataEmployee.value.dateOfBirth,
(v) => {
const isEdit =
employeeFormState.value.drawerModal &&
employeeFormState.value.isEmployeeEdit;
let currentFormDate = v && toISOStringWithTimezone(new Date(v));
let currentDate: string = '';
if (isEdit && employeeFormState.value.currentEmployee) {
currentDate = toISOStringWithTimezone(
new Date(employeeFormState.value.currentEmployee.dateOfBirth),
);
}
if (
employeeFormState.value.dialogModal ||
(isEdit && currentFormDate !== currentDate)
) {
const age = calculateAge(
currentFromDataEmployee.value.dateOfBirth,
'year',
);
if (currentFromDataEmployee.value.dateOfBirth && Number(age) < 15) {
dialog({
color: 'warning',
icon: 'mdi-alert',
title: t('dialog.title.youngWorker15'),
cancelText: t('general.edit'),
persistent: true,
message: t('dialog.message.youngWorker15'),
cancel: async () => {
currentFromDataEmployee.value.dateOfBirth = null;
return;
},
});
}
if (
currentFromDataEmployee.value.dateOfBirth &&
Number(age) > 15 &&
Number(age) <= 18
) {
dialog({
color: 'warning',
icon: 'mdi-alert',
title: t('dialog.title.youngWorker18'),
cancelText: t('general.cancel'),
actionText: t('general.confirm'),
persistent: true,
message: t('dialog.message.youngWorker18'),
action: () => {},
cancel: async () => {
currentFromDataEmployee.value.dateOfBirth = null;
return;
},
});
}
}
},
);
watch(
() => currentFromDataEmployee.value.image,
() => {
if (currentFromDataEmployee.value.image !== null)
employeeFormState.value.isImageEdit = true;
},
);
onMounted(async () => {
currentPageEmployee.value = 1;
listEmployee.value = [];
await fetchListEmployee({ fetchStats: true });
});
</script>
<template>
<q-splitter
v-model="splitPercent"
:limits="[0, 100]"
class="col full-width"
before-class="overflow-hidden"
after-class="overflow-hidden"
:disable="$q.screen.lt.sm"
>
<template v-slot:before>
<div
class="column q-pa-md surface-1 full-height full-width"
style="gap: var(--size-1)"
>
<q-item
active
dense
active-class="employer-active"
class="no-padding items-center rounded full-width"
v-close-popup
clickable
>
<span class="q-px-md ellipsis">
{{ $t('general.all') }}
</span>
</q-item>
</div>
</template>
<template v-slot:after>
<div class="column full-height no-wrap">
<!-- employee -->
<template
v-if="listEmployee && employeeStats > 0 && currentTab === 'employee'"
>
<div
v-if="listEmployee.length === 0"
class="row col full-width items-center justify-center"
style="min-height: 250px"
>
<NoData :not-found="!!inputSearch" />
</div>
<article
v-if="listEmployee.length !== 0"
class="column scroll q-pa-md col"
>
<q-infinite-scroll
:offset="10"
@load="
(_, done) => {
if (
$q.screen.gt.xs ||
currentPageEmployee === maxPageEmployee
)
return;
currentPageEmployee = currentPageEmployee + 1;
fetchListEmployee().then(() =>
done(currentPageEmployee >= maxPageEmployee),
);
}
"
>
<TableEmpoloyee
:hide-delete="!canAccess('customer', 'edit')"
v-model:page-size="pageSize"
v-model:current-page="currentPageEmployee"
:grid-view="gridView"
:list-employee="listEmployee"
:columns-employee="columnsEmployee"
:field-selected="fieldSelected"
@history="
(item: any) => {
openHistory(item.id);
}
"
@view="
async (item: any) => {
employeeFormState.drawerModal = true;
employeeFormState.isEmployeeEdit = false;
employeeFormStore.assignFormDataEmployee(item.id);
await fetchImageList(
item.id,
item.selectedImage || '',
'employee',
);
}
"
@edit="(item: any) => editEmployeeFormPersonal(item.id)"
@delete="
(item: any) => {
deleteEmployeeById({
id: item.id,
fetch: async () =>
await fetchListEmployee(
currentTab === 'employer'
? {
page: 1,
pageSize: 999,
customerId: customerFormState.currentCustomerId,
}
: {
fetchStats: true,
mobileFetch: $q.screen.xs,
},
),
});
}
"
@toggle-status="
async (item: any) => {
triggerChangeStatus(item.id, item.status, item.firstNameEN);
}
"
/>
<template v-slot:loading>
<div
v-if="
$q.screen.lt.sm && currentPageEmployee !== maxPageEmployee
"
class="row justify-center"
>
<q-spinner-dots color="primary" size="40px" />
</div>
</template>
</q-infinite-scroll>
</article>
<footer
v-if="listEmployee.length !== 0 && $q.screen.gt.xs"
class="row justify-between items-center q-px-md q-py-sm"
>
<div class="row col-4 items-center">
<div
class="app-text-muted"
style="width: 80px"
v-if="$q.screen.gt.sm"
>
{{ $t('general.recordPerPage') }}
</div>
<div><PaginationPageSize v-model="pageSize" /></div>
</div>
<div class="col-4 flex justify-center app-text-muted">
{{
$q.screen.gt.sm
? $t('general.recordsPage', {
resultcurrentPage: listEmployee.length,
total: employeeStats,
})
: $t('general.ofPage', {
current: listEmployee.length,
total: employeeStats,
})
}}
</div>
<div class="col-4 flex justify-end">
<PaginationComponent
v-model:current-page="currentPageEmployee"
v-model:max-page="maxPageEmployee"
:fetch-data="
async () => {
await fetchListEmployee();
flowStore.rotate();
}
"
/>
</div>
</footer>
</template>
</div>
</template>
</q-splitter>
<!-- add employee -->
<DialogEmployee
:fetch-list-employee="fetchListEmployee"
:fetch-image-list="fetchImageList"
:current-tab="currentTab"
/>
<!-- กจาง edit employee -->
<DrawerEmployee
:fetch-list-employee="fetchListEmployee"
:fetch-image-list="fetchImageList"
:current-tab="currentTab"
@change-status="
(s) =>
triggerChangeStatus(
currentFromDataEmployee.id,
s,
currentFromDataEmployee.firstNameEN,
)
"
/>
<DialogForm
:title="$t('general.historyEdit')"
hide-footer
v-model:modal="employeeHistoryDialog"
>
<div class="q-pa-md">
<HistoryEditComponent
v-if="employeeHistory"
v-model:history-list="employeeHistory"
/>
</div>
</DialogForm>
</template>
<style scoped>
.employer-active {
background-color: hsla(var(--info-bg) / 0.1);
color: hsl(var(--info-bg));
}
</style>

View file

@ -140,7 +140,7 @@ watch(
:rules="[ :rules="[
(val) => !!val || $t('form.error.required'), (val) => !!val || $t('form.error.required'),
(val) => (val) =>
/^[A-Za-z0-9ก-๙\s&.,'()-]+$/.test(val) || /^[A-Za-z0-9ก-๙\s&.,'-]+$/.test(val) ||
$t('form.error.branchNameField'), $t('form.error.branchNameField'),
]" ]"
/> />
@ -336,7 +336,6 @@ watch(
(v) => (typeof v === 'string' ? (prefixName = v) : '') (v) => (typeof v === 'string' ? (prefixName = v) : '')
" "
@clear="prefixName = ''" @clear="prefixName = ''"
:rules="[(val: string) => !!val || $t('form.error.required')]"
> >
<template v-slot:no-option> <template v-slot:no-option>
<q-item> <q-item>

View file

@ -2,7 +2,6 @@
import useOptionStore from 'stores/options'; import useOptionStore from 'stores/options';
import SelectBranch from 'src/components/shared/select/SelectBranch.vue'; import SelectBranch from 'src/components/shared/select/SelectBranch.vue';
import { isRoleInclude } from 'src/stores/utils'; import { isRoleInclude } from 'src/stores/utils';
import SelectBusinessType from 'src/components/shared/select/SelectBusinessType.vue';
withDefaults( withDefaults(
defineProps<{ defineProps<{
@ -143,10 +142,15 @@ const telephoneNo = defineModel<string>('telephoneNo', { default: '' });
for="input-tax" for="input-tax"
v-model="legalPersonNo" v-model="legalPersonNo"
/> />
<SelectBusinessType <q-input
dense
outlined
:readonly="readonly"
hide-bottom-space
class="col-6 col-md-3" class="col-6 col-md-3"
v-model:value="businessType" :label="$t('customer.table.businessTypePure')"
:readonly for="input-business-type"
:model-value="optionStore.mapOption(businessType)"
/> />
</div> </div>
@ -175,10 +179,15 @@ const telephoneNo = defineModel<string>('telephoneNo', { default: '' });
:label="$t('personnel.form.citizenId')" :label="$t('personnel.form.citizenId')"
for="input-citizen-id" for="input-citizen-id"
/> />
<SelectBusinessType <q-input
dense
outlined
:readonly="readonly"
hide-bottom-space
class="col-6 col-md-3" class="col-6 col-md-3"
v-model:value="businessType" :label="$t('customer.table.businessTypePure')"
:readonly for="input-first-name-en"
:model-value="optionStore.mapOption(businessType)"
/> />
</div> </div>

View file

@ -7,8 +7,6 @@ import { CustomerCreate } from 'stores/customer/types';
import EmployerFormAbout from './EmployerFormAbout.vue'; import EmployerFormAbout from './EmployerFormAbout.vue';
import EmployerFormAuthorized from './EmployerFormAuthorized.vue'; import EmployerFormAuthorized from './EmployerFormAuthorized.vue';
import { waitAll } from 'src/stores/utils'; import { waitAll } from 'src/stores/utils';
import FormEmployeePassport from 'src/components/03_customer-management/FormEmployeePassport.vue';
import FormEmployeeVisa from 'src/components/03_customer-management/FormEmployeeVisa.vue';
import { import {
FormCitizen, FormCitizen,
CorpFormBusinessRegistration, CorpFormBusinessRegistration,

View file

@ -10,25 +10,10 @@ import { useI18n } from 'vue-i18n';
import ThaiBahtText from 'thai-baht-text'; import ThaiBahtText from 'thai-baht-text';
import SelectBusinessType from 'src/components/shared/select/SelectBusinessType.vue'; import SelectBusinessType from 'src/components/shared/select/SelectBusinessType.vue';
import BusinessTypeDialog from 'src/pages/16_business-type-management/BusinessTypeDialog.vue';
import useBusinessTypeStore from 'src/stores/business-type';
const { locale } = useI18n({ useScope: 'global' }); const { locale } = useI18n({ useScope: 'global' });
const rawOption = ref(); const rawOption = ref();
const businessTypeDialog = ref<boolean>(false);
const formDataBusinessType = ref<{
name: string;
nameEN: string;
}>({
name: '',
nameEN: '',
});
const useBusinessType = useBusinessTypeStore();
const businessTypeId = defineModel<string>('businessTypeId'); const businessTypeId = defineModel<string>('businessTypeId');
const jobPosition = defineModel<string>('jobPosition'); const jobPosition = defineModel<string>('jobPosition');
const jobDescription = defineModel<string>('jobDescription'); const jobDescription = defineModel<string>('jobDescription');
@ -44,8 +29,6 @@ const typeBusinessENOption = ref([]);
const jobPositionOption = ref([]); const jobPositionOption = ref([]);
const jobPositionENOption = ref([]); const jobPositionENOption = ref([]);
const keySelect = ref<number>(0);
defineProps<{ defineProps<{
title?: string; title?: string;
dense?: boolean; dense?: boolean;
@ -55,20 +38,6 @@ defineProps<{
showTitle?: boolean; showTitle?: boolean;
}>(); }>();
function resetFormBusinessType() {
businessTypeDialog.value = false;
formDataBusinessType.value = { name: '', nameEN: '' };
}
async function submitBusinessType() {
const res = await useBusinessType.create(formDataBusinessType.value);
if (res) {
businessTypeId.value = res.id;
resetFormBusinessType();
keySelect.value++;
}
}
onMounted(async () => { onMounted(async () => {
const resultOption = await fetch('/option/option.json'); const resultOption = await fetch('/option/option.json');
rawOption.value = await resultOption.json(); rawOption.value = await resultOption.json();
@ -152,24 +121,16 @@ let jobPositionENFilter = selectFilterOptionRefMod(
</div> </div>
<SelectBusinessType <SelectBusinessType
:key="keySelect"
class="col-md-6 col-12" class="col-md-6 col-12"
v-model:value="businessTypeId" v-model:value="businessTypeId"
:readonly :readonly
creatable
lang="tha" lang="tha"
:rules="[(val: string) => !!val || $t('form.error.required')]"
@create="() => (businessTypeDialog = true)"
/> />
<SelectBusinessType <SelectBusinessType
:key="keySelect"
class="col-md-6 col-12" class="col-md-6 col-12"
v-model:value="businessTypeId" v-model:value="businessTypeId"
:readonly
creatable
lang="eng" lang="eng"
:rules="[(val: string) => !!val || $t('form.error.required')]" :readonly
@create="() => (businessTypeDialog = true)"
/> />
<q-select <q-select
@ -321,12 +282,4 @@ let jobPositionENFilter = selectFilterOptionRefMod(
" "
/> />
</div> </div>
<BusinessTypeDialog
ref="refBusinessTypeDialog"
@close="resetFormBusinessType()"
@submit="submitBusinessType()"
v-model="businessTypeDialog"
v-model:data="formDataBusinessType"
/>
</template> </template>

View file

@ -9,6 +9,8 @@ const contactName = defineModel<string>('contactName');
const email = defineModel<string>('email'); const email = defineModel<string>('email');
const contactTel = defineModel<string>('contactTel'); const contactTel = defineModel<string>('contactTel');
const officeTel = defineModel<string>('officeTel'); const officeTel = defineModel<string>('officeTel');
const agent = defineModel<string>('agent');
const agentUserId = defineModel<string>('agentUserId'); const agentUserId = defineModel<string>('agentUserId');
</script> </script>
@ -107,6 +109,7 @@ const agentUserId = defineModel<string>('agentUserId');
/> />
</template> </template>
</q-input> </q-input>
<SelectAgent <SelectAgent
:label="$t('customer.form.agent')" :label="$t('customer.form.agent')"
v-model:value="agentUserId" v-model:value="agentUserId"

View file

@ -22,10 +22,6 @@ import { useRoute } from 'vue-router';
export const useCustomerForm = defineStore('form-customer', () => { export const useCustomerForm = defineStore('form-customer', () => {
const customerStore = useCustomerStore(); const customerStore = useCustomerStore();
const onCreateImageList = ref<{
selectedImage: string;
list: { url: string; imgFile: File | null; name: string }[];
}>({ selectedImage: '', list: [] });
const { t } = useI18n(); const { t } = useI18n();
const flowStore = useFlowStore(); const flowStore = useFlowStore();
@ -34,8 +30,6 @@ export const useCustomerForm = defineStore('form-customer', () => {
const registerAbleBranchOption = ref<{ id: string; name: string }[]>(); const registerAbleBranchOption = ref<{ id: string; name: string }[]>();
const currentBranchRootId = ref<string>('');
const tabFieldRequired = ref<{ const tabFieldRequired = ref<{
[key: string]: (keyof CustomerBranchCreate)[]; [key: string]: (keyof CustomerBranchCreate)[];
}>({ }>({
@ -88,7 +82,6 @@ export const useCustomerForm = defineStore('form-customer', () => {
formDataOcr: Record<string, any>; formDataOcr: Record<string, any>;
isImageEdit: boolean; isImageEdit: boolean;
currentCustomerId?: string; currentCustomerId?: string;
imageList: { list: string[]; selectedImage: string };
}>({ }>({
dialogType: 'info', dialogType: 'info',
dialogOpen: false, dialogOpen: false,
@ -105,7 +98,6 @@ export const useCustomerForm = defineStore('form-customer', () => {
treeFile: [], treeFile: [],
formDataOcr: {}, formDataOcr: {},
isImageEdit: false, isImageEdit: false,
imageList: { list: [], selectedImage: '' },
}); });
watch( watch(
@ -168,7 +160,6 @@ export const useCustomerForm = defineStore('form-customer', () => {
state.value.editCustomerCode = data.code; state.value.editCustomerCode = data.code;
state.value.customerImageUrl = `${baseUrl}/customer/${id}/image/${data.selectedImage}`; state.value.customerImageUrl = `${baseUrl}/customer/${id}/image/${data.selectedImage}`;
state.value.defaultCustomerImageUrl = `${baseUrl}/customer/${id}/image/${data.selectedImage}`; state.value.defaultCustomerImageUrl = `${baseUrl}/customer/${id}/image/${data.selectedImage}`;
currentBranchRootId.value = data.branch[0].id || '';
resetFormData.registeredBranchId = data.registeredBranchId; resetFormData.registeredBranchId = data.registeredBranchId;
resetFormData.status = data.status; resetFormData.status = data.status;
@ -264,6 +255,7 @@ export const useCustomerForm = defineStore('form-customer', () => {
async function addCurrentCustomerBranch() { async function addCurrentCustomerBranch() {
if (currentFormData.value.customerBranch?.some((v) => !v.id)) return; if (currentFormData.value.customerBranch?.some((v) => !v.id)) return;
currentFormData.value.customerBranch?.push({ currentFormData.value.customerBranch?.push({
id: '',
customerId: '', customerId: '',
branchCode: branchCode:
currentFormData.value.customerBranch.length !== 0 currentFormData.value.customerBranch.length !== 0
@ -503,14 +495,11 @@ export const useCustomerForm = defineStore('form-customer', () => {
} }
return { return {
onCreateImageList,
tabFieldRequired, tabFieldRequired,
registerAbleBranchOption, registerAbleBranchOption,
state, state,
resetFormData, resetFormData,
currentFormData, currentFormData,
currentBranchRootId,
isFormDataDifferent, isFormDataDifferent,
resetForm, resetForm,
assignFormData, assignFormData,
@ -790,7 +779,6 @@ export const useEmployeeForm = defineStore('form-employee', () => {
} }
| undefined; | undefined;
ocr: boolean; ocr: boolean;
imageList: { list: string[]; selectedImage: string };
}>({ }>({
currentBranchId: '', currentBranchId: '',
isImageEdit: false, isImageEdit: false,
@ -815,7 +803,6 @@ export const useEmployeeForm = defineStore('form-employee', () => {
infoEmployeePersonCard: [], infoEmployeePersonCard: [],
formDataEmployeeOwner: undefined, formDataEmployeeOwner: undefined,
ocr: false, ocr: false,
imageList: { list: [], selectedImage: '' },
}); });
const defaultFormData: EmployeeCreate & { image?: File } = { const defaultFormData: EmployeeCreate & { image?: File } = {
@ -968,7 +955,6 @@ export const useEmployeeForm = defineStore('form-employee', () => {
state.value.currentIndexVisa = -1; state.value.currentIndexVisa = -1;
state.value.currentIndexCheckup = -1; state.value.currentIndexCheckup = -1;
state.value.currentIndexWorkHistory = -1; state.value.currentIndexWorkHistory = -1;
state.value.imageList = { list: [], selectedImage: '' };
// state.value.currentTab = 'personalInfo'; // state.value.currentTab = 'personalInfo';
if (clean) { if (clean) {
state.value.formDataEmployeeOwner = undefined; state.value.formDataEmployeeOwner = undefined;
@ -1398,10 +1384,12 @@ export const useEmployeeForm = defineStore('form-employee', () => {
statusSave: true, statusSave: true,
})), })),
), ),
employeeOtherInfo: structuredClone({ employeeOtherInfo: structuredClone(
...(payload.employeeOtherInfo ?? {}), {
statusSave: true, ...payload.employeeOtherInfo,
}), statusSave: !!payload.employeeOtherInfo?.id ? true : false,
} || {},
),
employeeWork: structuredClone( employeeWork: structuredClone(
payload.employeeWork?.length === 0 payload.employeeWork?.length === 0
? state.value.dialogModal ? state.value.dialogModal
@ -1675,7 +1663,6 @@ export const useEmployeeForm = defineStore('form-employee', () => {
state, state,
currentFromDataEmployee, currentFromDataEmployee,
resetEmployeeData, resetEmployeeData,
addPassport, addPassport,
addVisa, addVisa,
addCheckup, addCheckup,

View file

@ -102,8 +102,6 @@ const {
deleteWork, deleteWork,
importProduct, importProduct,
productExport,
} = productServiceStore; } = productServiceStore;
const { workNameItems } = storeToRefs(productServiceStore); const { workNameItems } = storeToRefs(productServiceStore);
@ -1171,7 +1169,6 @@ function clearFormService() {
profileSubmit.value = false; profileSubmit.value = false;
imageProduct.value = undefined; imageProduct.value = undefined;
profileFileImg.value = null; profileFileImg.value = null;
serviceTab.value = 1;
} }
function sameFormService() { function sameFormService() {
@ -1899,25 +1896,6 @@ async function submitWorkName(
else await editWork(workId, data); else await editWork(workId, data);
} }
async function triggerExport() {
productExport({
pageSize: 100_000,
productGroupId: currentIdGroup.value,
query: !!inputSearchProductAndService.value
? inputSearchProductAndService.value
: undefined,
status:
currentStatus.value === 'INACTIVE'
? 'INACTIVE'
: currentStatus.value === 'ACTIVE'
? 'ACTIVE'
: undefined,
startDate: searchDate.value[0] ? new Date(searchDate.value[0]) : undefined,
endDate: searchDate.value[1] ? new Date(searchDate.value[1]) : undefined,
});
}
watch( watch(
() => formService.value.attributes.workflowId, () => formService.value.attributes.workflowId,
async (a, b) => { async (a, b) => {
@ -2251,6 +2229,7 @@ watch(
</AdvanceSearch> </AdvanceSearch>
</template> </template>
</q-input> </q-input>
<div class="row col-md-6" style="white-space: nowrap"> <div class="row col-md-6" style="white-space: nowrap">
<q-select <q-select
v-show="$q.screen.gt.sm" v-show="$q.screen.gt.sm"
@ -2801,13 +2780,6 @@ watch(
} }
" "
/> />
<SaveButton
icon-only
:icon="'material-symbols:download'"
color="var(--info-bg)"
@click.stop="triggerExport()"
/>
</div> </div>
<div class="row col-md-6" style="white-space: nowrap"> <div class="row col-md-6" style="white-space: nowrap">
@ -4385,7 +4357,6 @@ watch(
<!-- add service --> <!-- add service -->
<DialogForm <DialogForm
v-if="dialogService"
hide-footer hide-footer
no-address no-address
no-app-box no-app-box
@ -4751,7 +4722,6 @@ watch(
<!-- edit service edit package--> <!-- edit service edit package-->
<!-- :edit="!(formDataProductService.status === 'INACTIVE')" --> <!-- :edit="!(formDataProductService.status === 'INACTIVE')" -->
<DialogForm <DialogForm
v-if="dialogServiceEdit"
hide-footer hide-footer
no-address no-address
height="95vh" height="95vh"

View file

@ -1,5 +1,5 @@
<script lang="ts" setup> <script lang="ts" setup>
import { onMounted, onUnmounted, reactive, ref, watch, computed } from 'vue'; import { onMounted, reactive, ref, watch, computed } from 'vue';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
@ -60,6 +60,7 @@ const flowStore = useFlowStore();
const userBranch = useMyBranch(); const userBranch = useMyBranch();
const navigatorStore = useNavigator(); const navigatorStore = useNavigator();
const customerStore = useCustomerStore(); const customerStore = useCustomerStore();
const { const {
fetchListOfOptionBranch, fetchListOfOptionBranch,
customerFormUndo, customerFormUndo,
@ -75,7 +76,6 @@ const {
const { const {
state: customerFormState, state: customerFormState,
currentFormData: customerFormData, currentFormData: customerFormData,
currentBranchRootId,
registerAbleBranchOption, registerAbleBranchOption,
tabFieldRequired, tabFieldRequired,
} = storeToRefs(customerFormStore); } = storeToRefs(customerFormStore);
@ -89,8 +89,6 @@ const fieldSelectedOption = computed(() => {
value: v.name, value: v.name,
})); }));
}); });
const keyAddDialog = ref<number>(0);
const special = ref(false); const special = ref(false);
const branchId = ref(''); const branchId = ref('');
const agentPrice = ref<boolean>(false); const agentPrice = ref<boolean>(false);
@ -275,10 +273,6 @@ const customerNameInfo = computed(() => {
return name || '-'; return name || '-';
}); });
function handleWindowFocus() {
fetchQuotationList();
}
onMounted(async () => { onMounted(async () => {
pageState.gridView = $q.screen.lt.md ? true : false; pageState.gridView = $q.screen.lt.md ? true : false;
navigatorStore.current.title = 'quotation.title'; navigatorStore.current.title = 'quotation.title';
@ -316,12 +310,6 @@ onMounted(async () => {
} }
flowStore.rotate(); flowStore.rotate();
window.addEventListener('focus', handleWindowFocus);
});
onUnmounted(() => {
window.removeEventListener('focus', handleWindowFocus);
}); });
async function fetchQuotationList(mobileFetch?: boolean) { async function fetchQuotationList(mobileFetch?: boolean) {
@ -777,13 +765,8 @@ async function filterBySellerId() {
:worker-count="item.row._count.worker" :worker-count="item.row._count.worker"
:worker-max="item.row.workerMax || item.row._count.worker" :worker-max="item.row.workerMax || item.row._count.worker"
:customer-name=" :customer-name="
item.row.customerBranch.customer.customerType === 'CORP' item.row.customerBranch.registerName ||
? $i18n.locale === 'tha' `${item.row.customerBranch.firstName || '-'} ${item.row.customerBranch.lastName || ''}`
? item.row.customerBranch.registerName
: item.row.customerBranch.registerNameEN
: $i18n.locale === 'tha'
? `${item.row.customerBranch.firstName || '-'} ${item.row.customerBranch.lastName || ''}`
: `${item.row.customerBranch.firstNameEN || '-'} ${item.row.customerBranch.lastNameEN || ''}`
" "
:reporter=" :reporter="
$i18n.locale === 'eng' $i18n.locale === 'eng'
@ -882,7 +865,6 @@ async function filterBySellerId() {
<!-- NOTE: START - Quotation Form, Add Quotation --> <!-- NOTE: START - Quotation Form, Add Quotation -->
<DialogForm <DialogForm
:key="keyAddDialog"
:title="$t('general.add', { text: $t('quotation.title') })" :title="$t('general.add', { text: $t('quotation.title') })"
v-model:modal="pageState.addModal" v-model:modal="pageState.addModal"
:submit-label="$t('general.add', { text: $t('quotation.title') })" :submit-label="$t('general.add', { text: $t('quotation.title') })"
@ -892,14 +874,13 @@ async function filterBySellerId() {
:submit=" :submit="
() => { () => {
quotationFormState.mode = 'create'; quotationFormState.mode = 'create';
quotationFormData.customerBranchId = currentBranchRootId;
triggerQuotationDialog({ statusDialog: 'create' }); triggerQuotationDialog({ statusDialog: 'create' });
} }
" "
:close=" :close="
() => { () => {
branchId = ''; branchId = '';
currentBranchRootId = ''; quotationFormData.customerBranchId = '';
} }
" "
:beforeClose=" :beforeClose="
@ -949,7 +930,7 @@ async function filterBySellerId() {
v-model:agent-price="agentPrice" v-model:agent-price="agentPrice"
v-model:branch-id="branchId" v-model:branch-id="branchId"
v-model:special="special" v-model:special="special"
v-model:customer-branch-id="currentBranchRootId" v-model:customer-branch-id="quotationFormData.customerBranchId"
@add-customer="triggerSelectTypeCustomerd()" @add-customer="triggerSelectTypeCustomerd()"
/> />
</div> </div>
@ -1018,7 +999,6 @@ async function filterBySellerId() {
() => { () => {
customerFormState.dialogModal = false; customerFormState.dialogModal = false;
onCreateImageList = { selectedImage: '', list: [] }; onCreateImageList = { selectedImage: '', list: [] };
keyAddDialog++;
} }
" "
> >
@ -1210,7 +1190,7 @@ async function filterBySellerId() {
customerFormData.customerBranch[0].legalPersonNo customerFormData.customerBranch[0].legalPersonNo
" "
v-model:business-type=" v-model:business-type="
customerFormData.customerBranch[0].businessTypeId customerFormData.customerBranch[0].businessType
" "
v-model:job-position=" v-model:job-position="
customerFormData.customerBranch[0].jobPosition customerFormData.customerBranch[0].jobPosition
@ -1291,6 +1271,7 @@ async function filterBySellerId() {
res = await customerStore.createBranch({ res = await customerStore.createBranch({
...customerFormData.customerBranch[idx], ...customerFormData.customerBranch[idx],
customerId: customerFormState.editCustomerId || '', customerId: customerFormState.editCustomerId || '',
id: undefined,
}); });
} }
if (res) { if (res) {

View file

@ -1,9 +1,15 @@
<script setup lang="ts"> <script setup lang="ts">
import { onMounted, reactive, ref } from 'vue'; import { baseUrl, dialog } from 'stores/utils';
import { QFile, QMenu } from 'quasar';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useConfigStore } from 'stores/config';
import { formatNumberDecimal } from 'stores/utils';
import { MainButton } from 'components/button';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
const { t } = useI18n();
import { reactive, ref } from 'vue';
import { useQuotationPayment } from 'src/stores/quotations'; import { useQuotationPayment } from 'src/stores/quotations';
import { import {
PaymentPayload, PaymentPayload,
@ -12,25 +18,16 @@ import {
QuotationPaymentData, QuotationPaymentData,
} from 'src/stores/quotations/types'; } from 'src/stores/quotations/types';
import { dateFormatJS } from 'src/utils/datetime'; import { dateFormatJS } from 'src/utils/datetime';
import { DebitNote } from 'src/stores/debit-note'; import { QFile, QMenu } from 'quasar';
import { baseUrl, dialog, formatNumberDecimal } from 'stores/utils';
import { useConfigStore } from 'stores/config';
import useBranchStore from 'src/stores/branch';
import useOptionStore from 'src/stores/options';
import UploadFileCard from 'src/components/upload-file/UploadFileCard.vue'; import UploadFileCard from 'src/components/upload-file/UploadFileCard.vue';
import SelectInput from 'src/components/shared/SelectInput.vue'; import { onMounted } from 'vue';
import { SaveButton, EditButton, UndoButton } from 'components/button'; import { DebitNote } from 'src/stores/debit-note';
const { t } = useI18n();
const { fetchListBankByBranch } = useBranchStore();
const { mapOption } = useOptionStore();
const configStore = useConfigStore(); const configStore = useConfigStore();
const quotationPayment = useQuotationPayment(); const quotationPayment = useQuotationPayment();
const { data: config } = storeToRefs(configStore); const { data: config } = storeToRefs(configStore);
const prop = defineProps<{ const prop = defineProps<{
branchId: string;
data?: Quotation | QuotationFull | DebitNote; data?: Quotation | QuotationFull | DebitNote;
readonly?: boolean; readonly?: boolean;
isDebitNote?: boolean; isDebitNote?: boolean;
@ -41,16 +38,8 @@ const firstCodePayment = defineModel<string>('firstCodePayment');
const refQFile = ref<InstanceType<typeof QFile>[]>([]); const refQFile = ref<InstanceType<typeof QFile>[]>([]);
const refQMenu = ref<InstanceType<typeof QMenu>[]>([]); const refQMenu = ref<InstanceType<typeof QMenu>[]>([]);
const paymentData = ref<QuotationPaymentData[]>([]); const paymentData = ref<QuotationPaymentData[]>([]);
const accountOpt = ref<{ label: string; value: string }[]>([]); const submitPaymentData = ref<QuotationPaymentData[]>([]);
const formPaymentMethod = ref< const payAll = ref<boolean>(false);
{
id: string;
channel: string | null;
reference: string | null;
account: string | null;
isEdit?: boolean;
}[]
>([]);
const paymentStatusOpts = [ const paymentStatusOpts = [
{ {
status: 'PaymentInProcess', status: 'PaymentInProcess',
@ -183,7 +172,7 @@ async function selectStatus(
payment.paymentStatus = status; payment.paymentStatus = status;
const payload = { const payload = {
paymentStatus: payment.paymentStatus, paymentStatus: payment.paymentStatus,
date: new Date(), date: new Date(payment.date),
amount: payment.amount, amount: payment.amount,
}; };
const res = await quotationPayment.updateQuotationPayment( const res = await quotationPayment.updateQuotationPayment(
@ -199,30 +188,18 @@ async function selectStatus(
refQMenu.value[index].hide(); refQMenu.value[index].hide();
} }
async function triggerSubmit(id: string) { async function triggerSubmit() {
const index = paymentData.value.findIndex((p) => p.id === id); submitPaymentData.value.forEach(async (p) => {
if (index === -1) return;
const p = paymentData.value[index];
const payload: PaymentPayload = { const payload: PaymentPayload = {
paymentStatus: p.paymentStatus, paymentStatus: p.paymentStatus,
date: new Date(p.date), date: new Date(p.date),
amount: p.amount, amount: p.amount,
account: formPaymentMethod.value[index].account,
channel: formPaymentMethod.value[index].channel,
reference: formPaymentMethod.value[index].reference,
}; };
await quotationPayment.updateQuotationPayment(p.id, payload); await quotationPayment.updateQuotationPayment(p.id, payload);
formPaymentMethod.value[index].isEdit = false; });
setTimeout(async () => {
await fetchData();
await getSlipList(paymentData.value[index], index);
}, 300);
} }
async function fetchData() { onMounted(async () => {
if (!prop.data) return; if (!prop.data) return;
const ret = await quotationPayment.getQuotationPayment({ const ret = await quotationPayment.getQuotationPayment({
quotationId: prop.isDebitNote === true ? undefined : prop.data.id, quotationId: prop.isDebitNote === true ? undefined : prop.data.id,
@ -232,13 +209,6 @@ async function fetchData() {
}); });
if (ret) { if (ret) {
paymentData.value = ret.result; paymentData.value = ret.result;
formPaymentMethod.value = ret.result.map((p, i) => ({
id: p.id,
channel: p.channel,
reference: p.reference,
account: p.account,
isEdit: formPaymentMethod.value[i]?.isEdit || false,
}));
slipFile.value = paymentData.value.map((v, i) => { slipFile.value = paymentData.value.map((v, i) => {
if (i === 0) { if (i === 0) {
firstCodePayment.value = v.code; firstCodePayment.value = v.code;
@ -249,31 +219,17 @@ async function fetchData() {
}; };
}); });
} }
}
async function fetchBankOption() {
const bankOption = await fetchListBankByBranch(prop.branchId);
accountOpt.value = bankOption
.map((b) => {
const name =
`${b.accountName} ${mapOption(b.bankName)} ${mapOption(b.accountType)} ${b.accountNumber}`.trim();
if (!name) return;
return {
label: name,
value: name,
};
})
.filter((i) => !!i);
}
onMounted(async () => {
await fetchData();
await fetchBankOption();
}); });
</script> </script>
<template> <template>
<div class="column no-wrap"> <q-form
greedy
@submit.prevent
@validation-success="triggerSubmit"
class="column full-height"
v-if="data"
>
<div class="col column no-wrap">
<!-- PRICE DETAIL --> <!-- PRICE DETAIL -->
<section class="bordered rounded surface-1" style="overflow: hidden"> <section class="bordered rounded surface-1" style="overflow: hidden">
<q-expansion-item <q-expansion-item
@ -337,8 +293,10 @@ onMounted(async () => {
<span class="q-ml-auto" style="color: var(--foreground)"> <span class="q-ml-auto" style="color: var(--foreground)">
฿ ฿
{{ {{
formatNumberDecimal(data.totalPrice - data.totalDiscount, 2) || formatNumberDecimal(
'0.00' data.totalPrice - data.totalDiscount,
2,
) || '0.00'
}} }}
</span> </span>
</span> </span>
@ -529,7 +487,7 @@ onMounted(async () => {
<!-- payment item --> <!-- payment item -->
<section class="row"> <section class="row">
<div <div
v-for="(payment, i) in paymentData" v-for="(p, i) in paymentData"
:key="i" :key="i"
class="bordered rounded surface-1 q-mb-md col-12" class="bordered rounded surface-1 q-mb-md col-12"
style="overflow: hidden" style="overflow: hidden"
@ -538,7 +496,7 @@ onMounted(async () => {
hide-expand-icon hide-expand-icon
v-model="state.payExpansion[i]" v-model="state.payExpansion[i]"
header-style="padding:0px" header-style="padding:0px"
@before-show="getSlipList(payment, i)" @before-show="getSlipList(p, i)"
> >
<template v-slot:header> <template v-slot:header>
<div class="column full-width"> <div class="column full-width">
@ -547,7 +505,7 @@ onMounted(async () => {
> >
<span class="text-weight-medium column"> <span class="text-weight-medium column">
{{ $t('quotation.periodNo') }} {{ i + 1 }} {{ $t('quotation.periodNo') }} {{ i + 1 }}
{{ monthDisplay(payment.createdAt) }} {{ monthDisplay(p.createdAt) }}
<!-- {{ monthDisplay(p.date) }} --> <!-- {{ monthDisplay(p.date) }} -->
<!-- <span --> <!-- <span -->
<!-- class="text-caption app-text-muted-2" --> <!-- class="text-caption app-text-muted-2" -->
@ -571,29 +529,25 @@ onMounted(async () => {
padding="4px 8px" padding="4px 8px"
class="rounded text-capitalize text-weight-regular payment-wait row items-center" class="rounded text-capitalize text-weight-regular payment-wait row items-center"
:class="{ :class="{
'payment-pending': 'payment-pending': p.paymentStatus === 'PaymentWait',
payment.paymentStatus === 'PaymentWait',
'payment-process': 'payment-process':
payment.paymentStatus === 'PaymentInProcess', p.paymentStatus === 'PaymentInProcess',
'payment-retry': 'payment-retry': p.paymentStatus === 'PaymentRetry',
payment.paymentStatus === 'PaymentRetry',
'payment-success': 'payment-success':
payment.paymentStatus === 'PaymentSuccess', p.paymentStatus === 'PaymentSuccess',
}" }"
> >
<q-icon <q-icon
size="xs" size="xs"
:name=" :name="
paymentStatusOpts.find( paymentStatusOpts.find(
(s) => s.status === payment.paymentStatus, (s) => s.status === p.paymentStatus,
)?.icon || 'mdi-hand-coin-outline' )?.icon || 'mdi-hand-coin-outline'
" "
class="q-pr-sm" class="q-pr-sm"
/> />
<span> <span>
{{ {{ $t(`quotation.receiptDialog.${p.paymentStatus}`) }}
$t(`quotation.receiptDialog.${payment.paymentStatus}`)
}}
</span> </span>
<q-icon <q-icon
v-if="!readonly" v-if="!readonly"
@ -614,16 +568,16 @@ onMounted(async () => {
<q-item <q-item
:id="`btn-payment-${opts.status}`" :id="`btn-payment-${opts.status}`"
v-if=" v-if="
(payment.paymentStatus === 'PaymentWait' && (p.paymentStatus === 'PaymentWait' &&
opts.status !== 'PaymentRetry') || opts.status !== 'PaymentRetry') ||
(payment.paymentStatus === 'PaymentInProcess' && (p.paymentStatus === 'PaymentInProcess' &&
opts.status !== 'PaymentInProcess') || opts.status !== 'PaymentInProcess') ||
(payment.paymentStatus === 'PaymentRetry' && (p.paymentStatus === 'PaymentRetry' &&
opts.status !== 'PaymentRetry') opts.status !== 'PaymentRetry')
" "
clickable clickable
class="row items-center" class="row items-center"
@click="selectStatus(payment, opts.status, i)" @click="selectStatus(p, opts.status, i)"
> >
<q-icon <q-icon
:name="opts.icon" :name="opts.icon"
@ -645,7 +599,7 @@ onMounted(async () => {
class="q-px-sm q-ml-auto" class="q-px-sm q-ml-auto"
style="color: var(--foreground)" style="color: var(--foreground)"
> >
{{ formatNumberDecimal(payment.amount, 2) }} {{ formatNumberDecimal(p.amount, 2) }}
</span> </span>
<q-btn <q-btn
dense dense
@ -654,125 +608,15 @@ onMounted(async () => {
padding="0" padding="0"
id="btn-show-file" id="btn-show-file"
:icon="`mdi-chevron-${state.payExpansion[i] ? 'down' : 'up'}`" :icon="`mdi-chevron-${state.payExpansion[i] ? 'down' : 'up'}`"
@click.stop="state.payExpansion[i] = !state.payExpansion[i]" @click.stop="
state.payExpansion[i] = !state.payExpansion[i]
"
/> />
</section> </section>
</div> </div>
</template> </template>
<!-- การรบชำระ -->
<template v-if="payment.paymentStatus === 'PaymentSuccess'">
<section
class="q-px-md q-py-xs text-weight-medium row items-center"
style="background-color: hsla(var(--info-bg) / 0.1)"
>
{{ $t('quotation.receiptDialog.paymentMethod') }}
</section>
<q-form
class="column full-height"
@submit.prevent
@submit="triggerSubmit(payment.id)"
>
<div <div
class="surface-2 q-px-md q-py-sm row q-col-gutter-sm items-center"
>
<SelectInput
id="input-payment-channel"
for="input-payment-channel"
:readonly="
readonly ||
(!formPaymentMethod[i].isEdit && !!payment.channel)
"
v-model="formPaymentMethod[i].channel"
class="col-md-2 col-6"
:rules="[
(val: string) => !!val || $t('form.error.required'),
]"
:option="[
{
label: $t('creditNote.label.Cash'),
value: 'Cash',
},
{
label: $t('creditNote.label.BankTransfer'),
value: 'BankTransfer',
},
]"
:label="$t('quotation.receiptDialog.paymentMethod')"
@update:model-value="
() => {
if (formPaymentMethod[i].channel === 'Cash') {
formPaymentMethod[i].reference = null;
formPaymentMethod[i].account = null;
}
}
"
/>
<q-input
v-if="formPaymentMethod[i].channel === 'BankTransfer'"
dense
outlined
class="col-md-3 col-6"
v-model="formPaymentMethod[i].reference"
:readonly="
readonly ||
(!formPaymentMethod[i].isEdit && !!payment.channel)
"
:label="$t('quotation.refNo')"
:rules="[
(val: string) => !!val || $t('form.error.required'),
]"
hide-bottom-space
/>
<SelectInput
v-if="formPaymentMethod[i].channel === 'BankTransfer'"
id="select-payment-account"
for="select-payment-account"
:readonly="
readonly ||
(!formPaymentMethod[i].isEdit && !!payment.channel)
"
v-model="formPaymentMethod[i].account"
class="col"
:option="accountOpt"
:label="$t('quotation.bankAccount')"
:rules="[
(val: string) => !!val || $t('form.error.required'),
]"
/>
<div class="q-ml-auto">
<UndoButton
v-if="formPaymentMethod[i].isEdit"
icon-only
@click="
() => {
formPaymentMethod[i].isEdit = false;
formPaymentMethod[i].channel = payment.channel;
formPaymentMethod[i].reference = payment.reference;
formPaymentMethod[i].account = payment.account;
}
"
/>
<SaveButton
v-if="!payment.channel || formPaymentMethod[i].isEdit"
icon-only
type="submit"
/>
<EditButton
v-if="
payment.channel && formPaymentMethod[i].isEdit === false
"
icon-only
@click="formPaymentMethod[i].isEdit = true"
/>
</div>
</div>
</q-form>
</template>
<!-- ปโหลดใบเสร -->
<section
class="q-px-md q-py-xs text-weight-medium row items-center" class="q-px-md q-py-xs text-weight-medium row items-center"
style="background-color: hsla(var(--info-bg) / 0.1)" style="background-color: hsla(var(--info-bg) / 0.1)"
> >
@ -781,7 +625,7 @@ onMounted(async () => {
msg: $t('quotation.receiptDialog.slip'), msg: $t('quotation.receiptDialog.slip'),
}) })
}} }}
</section> </div>
<div class="surface-2 q-pa-md"> <div class="surface-2 q-pa-md">
<div <div
class="upload-section column rounded q-py-md full-height items-center justify-center no-wrap" class="upload-section column rounded q-py-md full-height items-center justify-center no-wrap"
@ -808,9 +652,7 @@ onMounted(async () => {
ref="refQFile" ref="refQFile"
v-show="false" v-show="false"
v-model="slipFile[i].file" v-model="slipFile[i].file"
@update:model-value=" @update:model-value="triggerUpload(p, i, slipFile[i].file)"
triggerUpload(payment, i, slipFile[i].file)
"
/> />
</div> </div>
</div> </div>
@ -830,12 +672,12 @@ onMounted(async () => {
:name="d.name" :name="d.name"
:progress="d.progress" :progress="d.progress"
:uploading="{ loaded: d.loaded, total: d.total }" :uploading="{ loaded: d.loaded, total: d.total }"
:url="`/payment/${payment.id}/attachment/${d.name}`" :url="`/quotation/${data.id}/payment/${p.id}/attachment/${d.name}`"
icon="mdi-file-image-outline" icon="mdi-file-image-outline"
color="hsl(var(--text-mute))" color="hsl(var(--text-mute))"
clickable clickable
@click="triggerViewSlip(payment, d.name)" @click="triggerViewSlip(p, d.name)"
@close="triggerDelete(payment, d.name, i)" @close="triggerDelete(p, d.name, i)"
/> />
</div> </div>
</section> </section>
@ -844,6 +686,7 @@ onMounted(async () => {
</section> </section>
</section> </section>
</div> </div>
</q-form>
</template> </template>
<style scoped> <style scoped>
.bg-color-orange { .bg-color-orange {

View file

@ -80,8 +80,6 @@ import { RouterLink, useRoute } from 'vue-router';
import { initLang, initTheme, Lang } from 'src/utils/ui'; import { initLang, initTheme, Lang } from 'src/utils/ui';
import { convertTemplate } from 'src/utils/string-template'; import { convertTemplate } from 'src/utils/string-template';
import { CustomerBranch } from 'src/stores/customer';
type Node = { type Node = {
[key: string]: any; [key: string]: any;
opened?: boolean; opened?: boolean;
@ -96,8 +94,6 @@ type ProductGroupId = string;
const API_BASE_URL = import.meta.env.VITE_API_BASE_URL; const API_BASE_URL = import.meta.env.VITE_API_BASE_URL;
const customerBranchOption = ref<CustomerBranch>();
const employeeStore = useEmployeeStore(); const employeeStore = useEmployeeStore();
const route = useRoute(); const route = useRoute();
const useReceiptStore = useReceipt(); const useReceiptStore = useReceipt();
@ -161,7 +157,49 @@ const selectedWorker = ref<
}[]; }[];
})[] })[]
>([]); >([]);
const selectedWorkerItem = ref([]); const selectedWorkerItem = computed(() => {
return [
...selectedWorker.value.map((e) => ({
foreignRefNo: e.employeePassport
? e.employeePassport[0]?.number || '-'
: '-',
employeeName:
locale.value === Lang.English
? `${e.firstNameEN} ${e.lastNameEN}`
: e.firstName
? `${e.firstName} ${e.lastName}`
: `${e.firstNameEN} ${e.lastNameEN}`,
birthDate: dateFormatJS({ date: e.dateOfBirth }),
gender: e.gender,
age: calculateAge(e.dateOfBirth),
nationality: optionStore.mapOption(e.nationality),
documentExpireDate:
e.employeePassport !== undefined &&
e.employeePassport[0]?.expireDate !== undefined
? dateFormatJS({ date: e.employeePassport[0]?.expireDate })
: '-',
imgUrl: e.selectedImage
? `${API_BASE_URL}/employee/${e.id}/image/${e.selectedImage}`
: '',
status: e.status,
})),
...newWorkerList.value.map((v: any) => ({
foreignRefNo: v.passportNo,
employeeName:
locale.value === Lang.English
? `${v.firstNameEN} ${v.lastNameEN}`
: `${v.firstName} ${v.lastName}`,
birthDate: dateFormatJS({ date: v.dateOfBirth }),
gender: v.gender,
age: calculateAge(v.dateOfBirth),
nationality: optionStore.mapOption(v.nationality),
documentExpireDate: '-',
imgUrl: '',
status: 'CREATED',
})),
];
});
const firstCodePayment = ref(''); const firstCodePayment = ref('');
const selectedProductGroup = ref(''); const selectedProductGroup = ref('');
const selectedInstallmentNo = ref<number[]>([]); const selectedInstallmentNo = ref<number[]>([]);
@ -196,7 +234,7 @@ function getPrice(
) { ) {
if (filterHook) list = list.filter(filterHook); if (filterHook) list = list.filter(filterHook);
const value = list.reduce( return list.reduce(
(a, c) => { (a, c) => {
if ( if (
selectedInstallmentNo.value.length > 0 && selectedInstallmentNo.value.length > 0 &&
@ -206,25 +244,32 @@ function getPrice(
return a; return a;
} }
const originalPrice = c.pricePerUnit;
const finalPriceWithVat = precisionRound(
originalPrice * (1 + (config.value?.vat || 0.07)),
);
const finalPriceNoVat =
finalPriceWithVat / (1 + (config.value?.vat || 0.07));
const price = finalPriceNoVat * c.amount;
const vat =
(finalPriceNoVat * c.amount - c.discount) * (config.value?.vat || 0.07);
const calcVat = const calcVat =
c.product[agentPrice.value ? 'agentPriceCalcVat' : 'calcVat']; c.product[agentPrice.value ? 'agentPriceCalcVat' : 'calcVat'];
const vatFactor = calcVat ? (config.value?.vat ?? 0.07) : 0;
const pricePerUnit = a.totalPrice = precisionRound(a.totalPrice + price);
precisionRound(c.pricePerUnit * (1 + vatFactor)) / (1 + vatFactor); a.totalDiscount = precisionRound(a.totalDiscount + Number(c.discount));
a.vat = calcVat ? precisionRound(a.vat + vat) : a.vat;
const price =
(pricePerUnit * c.amount * (1 + vatFactor) - c.discount) /
(1 + vatFactor);
const vat = price * vatFactor;
a.totalPrice = precisionRound(a.totalPrice + price + c.discount);
a.totalDiscount = precisionRound(a.totalDiscount + c.discount);
a.vat = precisionRound(a.vat + vat);
a.vatExcluded = calcVat a.vatExcluded = calcVat
? a.vatExcluded ? a.vatExcluded
: precisionRound(a.vatExcluded + price); : precisionRound(a.vatExcluded + price);
a.finalPrice = precisionRound(a.totalPrice - a.totalDiscount + a.vat); a.finalPrice = precisionRound(
a.totalPrice -
a.totalDiscount +
a.vat -
Number(quotationFormData.value.discount || 0),
);
return a; return a;
}, },
@ -236,8 +281,6 @@ function getPrice(
finalPrice: 0, finalPrice: 0,
}, },
); );
return value;
} }
const summaryPrice = computed(() => getPrice(productServiceList.value)); const summaryPrice = computed(() => getPrice(productServiceList.value));
@ -516,7 +559,7 @@ async function convertDataToFormSubmit() {
), ),
); );
selectedWorkerItem.value.forEach((v, i) => { selectedWorker.value.forEach((v, i) => {
if (v.attachment !== undefined) { if (v.attachment !== undefined) {
v.attachment.forEach((value) => { v.attachment.forEach((value) => {
fileItemNewWorker.value.push({ fileItemNewWorker.value.push({
@ -533,7 +576,7 @@ async function convertDataToFormSubmit() {
quotationFormData.value.worker = JSON.parse( quotationFormData.value.worker = JSON.parse(
JSON.stringify([ JSON.stringify([
...selectedWorkerItem.value.map((v) => { ...selectedWorker.value.map((v) => {
{ {
return v.id; return v.id;
} }
@ -676,13 +719,19 @@ function handleUpdateProductTable(
// handleChangePayType(quotationFormData.value.payCondition); // handleChangePayType(quotationFormData.value.payCondition);
// calc price // calc price
const calc = (c: QuotationPayload['productServiceList'][number]) => { const calc = (c: QuotationPayload['productServiceList'][number]) => {
const calcVat = const originalPrice = c.pricePerUnit || 0;
c.product[agentPrice.value ? 'agentPriceCalcVat' : 'calcVat']; const finalPriceWithVat = precisionRound(
const vatFactor = calcVat ? (config.value?.vat ?? 0.07) : 0; originalPrice * (1 + (config.value?.vat || 0.07)),
const pricePerUnit = );
precisionRound(c.pricePerUnit * (1 + vatFactor)) / (1 + vatFactor); const finalPriceNoVat =
const price = pricePerUnit * c.amount * (1 + vatFactor) - c.discount; finalPriceWithVat / (1 + (config.value?.vat || 0.07));
return precisionRound(price);
const price = finalPriceNoVat * c.amount;
const vat = c.product.calcVat
? (finalPriceNoVat * c.amount - (c.discount || 0)) *
(config.value?.vat || 0.07)
: 0;
return precisionRound(price + vat);
}; };
// installment // installment
@ -735,16 +784,21 @@ function toggleDeleteProduct(index: number) {
// cal curr amount // cal curr amount
if (currPaySplit && currTempPaySplit) { if (currPaySplit && currTempPaySplit) {
const calcVat = const price = agentPrice.value
currProduct.product[agentPrice.value ? 'agentPriceCalcVat' : 'calcVat']; ? currProduct.product.agentPrice
const vatFactor = calcVat ? (config.value?.vat ?? 0.07) : 0; : currProduct.product.price;
const pricePerUnit = currProduct.product.vatIncluded
? price / (1 + (config.value?.vat || 0.07))
: price;
const vat =
(pricePerUnit * currProduct.amount - currProduct.discount) *
(config.value?.vat || 0.07);
const finalPrice =
pricePerUnit * currProduct.amount +
vat -
Number(currProduct.discount || 0);
const price = precisionRound( currTempPaySplit.amount = currPaySplit.amount - finalPrice;
currProduct.pricePerUnit * currProduct.amount * (1 + vatFactor) -
currProduct.discount,
);
currTempPaySplit.amount = currPaySplit.amount - price;
currPaySplit.amount = currTempPaySplit.amount; currPaySplit.amount = currTempPaySplit.amount;
} }
@ -766,40 +820,7 @@ function toggleDeleteProduct(index: number) {
} }
async function assignWorkerToSelectedWorker() { async function assignWorkerToSelectedWorker() {
selectedWorkerItem.value = quotationFormData.value.worker.map((e) => { selectedWorker.value = quotationFormData.value.worker;
return {
id: e.id,
foreignRefNo: e.employeePassport
? e.employeePassport[0]?.number || '-'
: '-',
employeeName:
locale.value === Lang.English
? `${e.firstNameEN} ${e.lastNameEN}`
: `${e.firstName || e.firstNameEN} ${e.lastName || e.lastNameEN}`,
birthDate: dateFormatJS({ date: e.dateOfBirth }),
gender: e.gender,
age: calculateAge(e.dateOfBirth),
nationality: optionStore.mapOption(e.nationality),
documentExpireDate:
e.employeePassport !== undefined &&
e.employeePassport[0]?.expireDate !== undefined
? dateFormatJS({ date: e.employeePassport[0]?.expireDate })
: '-',
imgUrl: e.selectedImage
? `${API_BASE_URL}/employee/${e.id}/image/${e.selectedImage}`
: '',
employeePassport: e.employeePassport,
status: e.status,
workerNew: false,
lastNameEN: e.lastNameEN,
lastName: e.lastName,
middleNameEN: e.middleNameEN,
middleName: e.middleName,
firstNameEN: e.firstNameEN,
firstName: e.firstName,
namePrefix: e.namePrefix,
};
});
} }
function convertToTable(nodes: Node[]) { function convertToTable(nodes: Node[]) {
@ -832,7 +853,8 @@ function convertToTable(nodes: Node[]) {
tempTableProduct.value = JSON.parse(JSON.stringify(list)); tempTableProduct.value = JSON.parse(JSON.stringify(list));
if (nodes.length > 0) { if (nodes.length > 0) {
quotationFormData.value.paySplit = Array.from( quotationFormData.value.paySplit = Array.apply(
null,
new Array(quotationFormData.value.paySplitCount), new Array(quotationFormData.value.paySplitCount),
).map((_, i) => ({ ).map((_, i) => ({
no: i + 1, no: i + 1,
@ -858,21 +880,21 @@ function convertToTable(nodes: Node[]) {
function convertEmployeeToTable(selected: Employee[]) { function convertEmployeeToTable(selected: Employee[]) {
productServiceList.value.forEach((v) => { productServiceList.value.forEach((v) => {
if (selectedWorkerItem.value.length === 0 && v.amount === 1) v.amount -= 1; if (selectedWorker.value.length === 0 && v.amount === 1) v.amount -= 1;
v.amount = Math.max( v.amount = Math.max(
v.amount + selected.length - selectedWorkerItem.value.length, v.amount + selected.length - selectedWorker.value.length,
1, 1,
); );
const oldWorkerId: string[] = []; const oldWorkerId: string[] = [];
const newWorkerIndex: number[] = []; const newWorkerIndex: number[] = [];
selectedWorkerItem.value.forEach((item, i) => { selectedWorker.value.forEach((item, i) => {
if (v.workerIndex.includes(i)) oldWorkerId.push(item.id); if (v.workerIndex.includes(i)) oldWorkerId.push(item.id);
}); });
selected.forEach((item, i) => { selected.forEach((item, i) => {
if (selectedWorkerItem.value.find((n) => item.id === n.id)) return; if (selectedWorker.value.find((n) => item.id === n.id)) return;
newWorkerIndex.push(i); newWorkerIndex.push(i);
}); });
@ -885,7 +907,7 @@ function convertEmployeeToTable(selected: Employee[]) {
pageState.employeeModal = false; pageState.employeeModal = false;
quotationFormData.value.workerMax = Math.max( quotationFormData.value.workerMax = Math.max(
quotationFormData.value.workerMax || 1, quotationFormData.value.workerMax || 1,
selectedWorkerItem.value.length, selectedWorker.value.length,
); );
} }
@ -958,71 +980,6 @@ function viewProductFile(data: ProductRelation) {
pageState.imageDialogUrl = base64 ? base64[1] : ''; pageState.imageDialogUrl = base64 ? base64[1] : '';
} }
function combineWorker(newWorker: any, oldWorker: any) {
selectedWorkerItem.value = [
...oldWorker.map((e) => ({
id: e.id,
foreignRefNo: e.employeePassport
? e.employeePassport[0]?.number || '-'
: '-',
employeeName:
locale.value === Lang.English
? `${e.firstNameEN} ${e.lastNameEN}`
: `${e.firstName || e.firstNameEN} ${e.lastName || e.lastNameEN}`,
birthDate: dateFormatJS({ date: e.dateOfBirth }),
gender: e.gender,
age: calculateAge(e.dateOfBirth),
nationality: optionStore.mapOption(e.nationality),
documentExpireDate:
e.employeePassport !== undefined &&
e.employeePassport[0]?.expireDate !== undefined
? dateFormatJS({ date: e.employeePassport[0]?.expireDate })
: '-',
imgUrl: e.selectedImage
? `${API_BASE_URL}/employee/${e.id}/image/${e.selectedImage}`
: '',
employeePassport: e.employeePassport,
status: e.status,
workerNew: false,
lastNameEN: e.lastNameEN,
lastName: e.lastName,
middleNameEN: e.middleNameEN,
middleName: e.middleName,
firstNameEN: e.firstNameEN,
firstName: e.firstName,
namePrefix: e.namePrefix,
})),
...newWorker.map((v: any) => ({
id: v.id,
foreignRefNo: v.passportNo || '-',
employeeName:
locale.value === Lang.English
? `${v.firstNameEN} ${v.lastNameEN}`
: `${v.firstName || v.firstNameEN} ${v.lastName || v.lastNameEN}`,
birthDate: dateFormatJS({ date: v.dateOfBirth }),
gender: v.gender,
age: calculateAge(v.dateOfBirth),
nationality: optionStore.mapOption(v.nationality),
documentExpireDate: '-',
imgUrl: '',
status: 'CREATED',
lastNameEN: v.lastNameEN,
lastName: v.lastName,
middleNameEN: v.middleNameEN,
middleName: v.middleName,
firstNameEN: v.firstNameEN,
firstName: v.firstName,
namePrefix: v.namePrefix,
dateOfBirth: v.dateOfBirth,
workerNew: true,
})),
];
}
const sessionData = ref<Record<string, any>>(); const sessionData = ref<Record<string, any>>();
onMounted(async () => { onMounted(async () => {
@ -1094,7 +1051,7 @@ watch(
() => quotationFormData.value.customerBranchId, () => quotationFormData.value.customerBranchId,
async (v) => { async (v) => {
if (!v) return; if (!v) return;
selectedWorkerItem.value = []; selectedWorker.value = [];
}, },
); );
@ -1110,15 +1067,6 @@ watch(
const productServiceNodes = ref<ProductTree>([]); const productServiceNodes = ref<ProductTree>([]);
watch(customerBranchOption, () => {
if (!customerBranchOption.value) return;
quotationFormData.value.contactName =
customerBranchOption.value.contactName || '';
quotationFormData.value.contactTel =
customerBranchOption.value.contactTel || '';
});
watch( watch(
() => productServiceList.value, () => productServiceList.value,
() => { () => {
@ -1126,13 +1074,6 @@ watch(
}, },
); );
watch(customerBranchOption, () => {
if (!customerBranchOption.value) return;
quotationFormData.value.contactName = customerBranchOption.value.contactName;
quotationFormData.value.contactTel = customerBranchOption.value.contactTel;
});
// async function searchEmployee(text: string) { // async function searchEmployee(text: string) {
// let query: string | undefined = text; // let query: string | undefined = text;
// let pageSize = 50; // let pageSize = 50;
@ -1154,19 +1095,7 @@ watch(customerBranchOption, () => {
// } // }
function storeDataLocal() { function storeDataLocal() {
const tempProductService = productService.value.map((v) => { quotationFormData.value.productServiceList = productService.value;
return {
...v,
vat: v.product[agentPrice ? 'agentPriceCalcVat' : 'calcVat']
? precisionRound(
((v.pricePerUnit * (1 + (config?.value.vat || 0.07)) * v.amount -
v.discount) /
(1 + (config?.value.vat || 0.07))) *
0.07,
)
: 0,
};
});
localStorage.setItem( localStorage.setItem(
'quotation-preview', 'quotation-preview',
@ -1175,7 +1104,7 @@ function storeDataLocal() {
codeInvoice: code.value, codeInvoice: code.value,
codePayment: firstCodePayment.value, codePayment: firstCodePayment.value,
...quotationFormData.value, ...quotationFormData.value,
productServiceList: tempProductService, productServiceList: productService.value,
}, },
meta: { meta: {
source: { source: {
@ -1195,7 +1124,7 @@ function storeDataLocal() {
workName: quotationFormData.value.workName, workName: quotationFormData.value.workName,
dueDate: quotationFormData.value.dueDate, dueDate: quotationFormData.value.dueDate,
}, },
selectedWorker: selectedWorkerItem.value, selectedWorker: selectedWorker.value,
createdBy: quotationFormState.value.createdBy('tha'), createdBy: quotationFormState.value.createdBy('tha'),
agentPrice: agentPrice.value, agentPrice: agentPrice.value,
}, },
@ -1280,10 +1209,10 @@ async function getWorkerFromCriteria(
if (!ret) return false; // error, do not close dialog if (!ret) return false; // error, do not close dialog
const deduplicate = ret.result.filter( const deduplicate = ret.result.filter(
(a) => !selectedWorkerItem.value.find((b) => a.id === b.id), (a) => !selectedWorker.value.find((b) => a.id === b.id),
); );
convertEmployeeToTable([...deduplicate, ...selectedWorkerItem.value]); convertEmployeeToTable([...deduplicate, ...selectedWorker.value]);
return true; return true;
} }
@ -1590,7 +1519,6 @@ function covertToNode() {
v-model:customer-branch-id=" v-model:customer-branch-id="
quotationFormData.customerBranchId quotationFormData.customerBranchId
" "
v-model:customer-branch-option="customerBranchOption"
:readonly="readonly" :readonly="readonly"
/> />
</template> </template>
@ -1673,15 +1601,15 @@ function covertToNode() {
(v) => (v) =>
(quotationFormData.workerMax = Math.max( (quotationFormData.workerMax = Math.max(
v, v,
selectedWorkerItem.length, selectedWorker.length,
)) ))
" "
:employee-amount=" :employee-amount="
quotationFormData.workerMax || selectedWorkerItem.length quotationFormData.workerMax || selectedWorker.length
" "
:readonly="readonly" :readonly="readonly"
:rows="selectedWorkerItem" :rows="selectedWorkerItem"
@delete="(i) => deleteItem(selectedWorkerItem, i)" @delete="(i) => deleteItem(selectedWorker, i)"
/> />
</div> </div>
</q-expansion-item> </q-expansion-item>
@ -1925,10 +1853,10 @@ function covertToNode() {
installments: quotationFormData.paySplit, installments: quotationFormData.paySplit,
}, },
'quotation-labor': { 'quotation-labor': {
name: selectedWorkerItem.map( name: selectedWorker.map(
(v, i) => (v, i) =>
`${i + 1}. ` + `${i + 1}. ` +
`${v.employeePassport.length !== 0 ? v.employeePassport[0].number + '_' : ''}${v.namePrefix}.${v.firstNameEN ? `${v.firstNameEN} ${v.lastNameEN}` : `${v.firstName} ${v.lastName}`} `.toUpperCase(), `${v.employeePassport.length !== 0 ? v.employeePassport[0].number + '_' : ''} ${v.namePrefix}.${v.firstNameEN ? `${v.firstNameEN} ${v.lastNameEN}` : `${v.firstName} ${v.lastName}`} `.toUpperCase(),
), ),
}, },
}) })
@ -2018,7 +1946,6 @@ function covertToNode() {
view !== View.Receipt && view !== View.Receipt &&
view !== View.Complete view !== View.Complete
" "
:branch-id="quotationFull.registeredBranchId"
:readonly=" :readonly="
isRoleInclude(['sale', 'head_of_sale']) || isRoleInclude(['sale', 'head_of_sale']) ||
!canAccess('quotation', 'edit') !canAccess('quotation', 'edit')
@ -2469,12 +2396,13 @@ function covertToNode() {
<!-- add employee quotation --> <!-- add employee quotation -->
<QuotationFormWorkerSelect <QuotationFormWorkerSelect
:preselect-worker="selectedWorkerItem" :preselect-worker="selectedWorker"
:customerBranchId="quotationFormData.customerBranchId" :customerBranchId="quotationFormData.customerBranchId"
v-model:open="pageState.employeeModal" v-model:open="pageState.employeeModal"
v-model:new-worker-list="newWorkerList"
@success=" @success="
(v) => { (v) => {
combineWorker(v.newWorker, v.worker); selectedWorker = v.worker;
} }
" "
/> />
@ -2517,7 +2445,7 @@ function covertToNode() {
<!-- add Worker --> <!-- add Worker -->
<QuotationFormWorkerAddDialog <QuotationFormWorkerAddDialog
v-if="quotationFormState.source" v-if="quotationFormState.source"
:disabled-worker-id="selectedWorkerItem.map((v) => v.id)" :disabled-worker-id="selectedWorker.map((v) => v.id)"
:product-service-list="quotationFormState.source.productServiceList" :product-service-list="quotationFormState.source.productServiceList"
:quotation-id="quotationFormState.source.id" :quotation-id="quotationFormState.source.id"
:customer-branch-id="quotationFormState.source.customerBranchId" :customer-branch-id="quotationFormState.source.customerBranchId"

View file

@ -234,7 +234,6 @@ watch(
<section class="row q-col-gutter-sm col-12 items-center"> <section class="row q-col-gutter-sm col-12 items-center">
<SelectInput <SelectInput
class="col-md-6 col-12" class="col-md-6 col-12"
id="select-pay-type"
:label="$t('quotation.payType')" :label="$t('quotation.payType')"
:option=" :option="
taskOrder taskOrder
@ -242,6 +241,7 @@ watch(
: payTypeOption : payTypeOption
" "
:readonly="readonly || debitNote" :readonly="readonly || debitNote"
id="pay-type"
:model-value="payType" :model-value="payType"
@update:model-value=" @update:model-value="
(v) => { (v) => {
@ -275,7 +275,6 @@ watch(
</div> </div>
<q-input <q-input
v-model="paySplitCount" v-model="paySplitCount"
id="input-pay-split-count"
:readonly="readonly || payType === 'Split'" :readonly="readonly || payType === 'Split'"
class="col-3" class="col-3"
type="number" type="number"
@ -312,7 +311,6 @@ watch(
<q-input <q-input
:readonly="readonly" :readonly="readonly"
:label="$t('general.name')" :label="$t('general.name')"
:id="`input-period-name-${i}`"
v-if="payType === 'SplitCustom'" v-if="payType === 'SplitCustom'"
v-model="period.name" v-model="period.name"
class="col q-mx-sm" class="col q-mx-sm"
@ -322,7 +320,6 @@ watch(
<q-input <q-input
:readonly="readonly || payType === 'Split'" :readonly="readonly || payType === 'Split'"
class="col q-mx-sm" class="col q-mx-sm"
:id="`input-period-amount-${i}`"
:label="$t('quotation.amount')" :label="$t('quotation.amount')"
:model-value=" :model-value="
amount4Show[i] || commaInput(period.amount.toString()) amount4Show[i] || commaInput(period.amount.toString())
@ -380,7 +377,6 @@ watch(
<DatePicker <DatePicker
v-if="payType === 'BillFull'" v-if="payType === 'BillFull'"
id="datepicker-bill-date"
:readonly :readonly
class="col-12" class="col-12"
:label="$t('quotation.callDueDate')" :label="$t('quotation.callDueDate')"
@ -450,9 +446,7 @@ watch(
<span class="q-ml-auto"> <span class="q-ml-auto">
{{ {{
formatNumberDecimal( formatNumberDecimal(
summaryPrice.totalPrice - summaryPrice.totalPrice - summaryPrice.totalDiscount,
summaryPrice.totalDiscount -
summaryPrice.vatExcluded,
2, 2,
) )
}} }}
@ -488,11 +482,7 @@ watch(
<div class="q-pa-sm row surface-2 items-center text-weight-bold"> <div class="q-pa-sm row surface-2 items-center text-weight-bold">
{{ $t('quotation.totalPriceBaht') }} {{ $t('quotation.totalPriceBaht') }}
<span <span class="q-ml-auto" style="color: var(--brand-1)">
class="q-ml-auto"
style="color: var(--brand-1)"
id="value-final-price"
>
{{ {{
payType === 'SplitCustom' && view === View.Invoice payType === 'SplitCustom' && view === View.Invoice
? formatNumberDecimal(Math.max(installmentAmount || 0, 0), 2) || 0 ? formatNumberDecimal(Math.max(installmentAmount || 0, 0), 2) || 0

View file

@ -51,8 +51,6 @@ const emit = defineEmits<{
const selectedProductGroup = defineModel<string>('selectedProductGroup', { const selectedProductGroup = defineModel<string>('selectedProductGroup', {
default: '', default: '',
}); });
const selectedProductGroupOption = ref<ProductGroup | undefined>();
const model = defineModel<boolean>(); const model = defineModel<boolean>();
const inputSearch = defineModel<string>('inputSearch'); const inputSearch = defineModel<string>('inputSearch');
const productGroup = defineModel<ProductGroup[]>('productGroup', { const productGroup = defineModel<ProductGroup[]>('productGroup', {
@ -571,18 +569,14 @@ watch(
{{ {{
productGroup.find( productGroup.find(
(g) => g.id === selectedProductGroup, (g) => g.id === selectedProductGroup,
)?.name || )?.name || '-'
selectedProductGroupOption?.name ||
'-'
}} }}
</span> </span>
<span class="text-caption app-text-muted"> <span class="text-caption app-text-muted">
{{ {{
productGroup.find( productGroup.find(
(g) => g.id === selectedProductGroup, (g) => g.id === selectedProductGroup,
)?.code || )?.code || '-'
selectedProductGroupOption?.code ||
'-'
}} }}
</span> </span>
</div> </div>
@ -868,13 +862,13 @@ watch(
<span class="q-pr-sm"> <span class="q-pr-sm">
{{ $t('productService.group.title') }} {{ $t('productService.group.title') }}
</span> </span>
<SelectProductGroup <SelectProductGroup
class="col-md-4 col-12" class="col-md-4 col-12"
:class="{ 'q-mb-sm': $q.screen.lt.md }" :class="{ 'q-mb-sm': $q.screen.lt.md }"
id="product-group-select" id="product-group-select"
clearable clearable
v-model:value="selectedProductGroup" v-model:value="selectedProductGroup"
v-model:value-option="selectedProductGroupOption"
:placeholder=" :placeholder="
!selectedProductGroup !selectedProductGroup
? $t('general.select', { ? $t('general.select', {

View file

@ -341,13 +341,12 @@ watch(() => state.search, getWorkerList);
> >
<div <div
style="display: inline-block; margin-inline: auto" style="display: inline-block; margin-inline: auto"
v-if="workerList.length === 0 && state.search" v-if="workerList.length === 0"
> >
<NoData :not-found="!!state.search" /> <NoData :not-found="!!state.search" />
</div> </div>
<TableWorker <TableWorker
v-else
v-model:selected="workerSelected" v-model:selected="workerSelected"
:rows="workerList" :rows="workerList"
:disabledWorkerId :disabledWorkerId

View file

@ -113,7 +113,7 @@ const props = withDefaults(
defineProps<{ defineProps<{
customerBranchId?: string; customerBranchId?: string;
disabledWorkerId?: string[]; disabledWorkerId?: string[];
preselectWorker?: (Employee & { workerNew: boolean })[]; preselectWorker?: Employee[];
}>(), }>(),
{}, {},
); );
@ -133,7 +133,7 @@ const optionStore = useOptionStore();
const employeeStore = useEmployeeStore(); const employeeStore = useEmployeeStore();
const open = defineModel<boolean>('open', { default: false }); const open = defineModel<boolean>('open', { default: false });
const newWorkerList = ref< const newWorkerList = defineModel<
(EmployeeWorker & { (EmployeeWorker & {
attachment?: { attachment?: {
name?: string; name?: string;
@ -143,7 +143,7 @@ const newWorkerList = ref<
_meta?: Record<string, any>; _meta?: Record<string, any>;
}[]; }[];
})[] })[]
>([]); >('newWorkerList', { default: [] });
const workerSelected = ref<Employee[]>([]); const workerSelected = ref<Employee[]>([]);
const workerList = ref<Employee[]>([]); const workerList = ref<Employee[]>([]);
const importWorkerCriteria = ref<{ const importWorkerCriteria = ref<{
@ -208,13 +208,7 @@ function getEmployeeImageUrl(item: Employee) {
function init() { function init() {
if (props.preselectWorker) { if (props.preselectWorker) {
workerSelected.value = JSON.parse( workerSelected.value = JSON.parse(JSON.stringify(props.preselectWorker));
JSON.stringify(props.preselectWorker.filter((v) => !v.workerNew)),
);
newWorkerList.value = JSON.parse(
JSON.stringify(props.preselectWorker.filter((v) => v.workerNew)),
);
} }
getWorkerList(); getWorkerList();
} }
@ -614,14 +608,11 @@ watch(
solid solid
id="btn-success" id="btn-success"
@click=" @click="
() => { (emits('success', {
$emit('success', {
worker: workerSelected, worker: workerSelected,
newWorker: newWorkerList, newWorker: newWorkerList,
}); }),
(open = false))
open = false;
}
" "
> >
{{ $t('general.select', { msg: $t('quotation.employeeList') }) }} {{ $t('general.select', { msg: $t('quotation.employeeList') }) }}
@ -643,11 +634,9 @@ watch(
if (employeeFormState.currentTab === 'personalInfo') { if (employeeFormState.currentTab === 'personalInfo') {
const currentEmployeeId = const currentEmployeeId =
await employeeFormStore.submitPersonal(onCreateImageList); await employeeFormStore.submitPersonal(onCreateImageList);
newWorkerList.push(
quotationForm.injectNewEmployee({ quotationForm.injectNewEmployee({
data: { ...currentFromDataEmployee, id: currentEmployeeId }, data: { ...currentFromDataEmployee, id: currentEmployeeId },
}), });
);
employeeFormState.isEmployeeEdit = false; employeeFormState.isEmployeeEdit = false;
employeeFormState.dialogType = 'info'; employeeFormState.dialogType = 'info';
} }

View file

@ -69,7 +69,6 @@ export const useQuotationForm = defineStore('form-quotation', () => {
file?: File; file?: File;
_meta?: Record<string, any>; _meta?: Record<string, any>;
}[]; }[];
workerNew: boolean;
})[] })[]
>([]); >([]);
@ -221,7 +220,7 @@ export const useQuotationForm = defineStore('form-quotation', () => {
}, },
callback?: () => void, callback?: () => void,
) { ) {
const temp = { newWorkerList.value.push({
//passportNo: obj.data.passportNo, //passportNo: obj.data.passportNo,
//documentExpireDate: obj.data.documentExpireDate, //documentExpireDate: obj.data.documentExpireDate,
id: obj.data.id, id: obj.data.id,
@ -236,12 +235,9 @@ export const useQuotationForm = defineStore('form-quotation', () => {
gender: obj.data.gender, gender: obj.data.gender,
dateOfBirth: obj.data.dateOfBirth, dateOfBirth: obj.data.dateOfBirth,
attachment: obj.data.attachment, attachment: obj.data.attachment,
workerNew: true, });
};
callback?.(); callback?.();
return temp;
} }
function dialogDelete(callback: () => void) { function dialogDelete(callback: () => void) {

View file

@ -1,6 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { onMounted, nextTick, ref, watch, toRaw } from 'vue'; import { onMounted, nextTick, ref, watch } from 'vue';
import { precisionRound } from 'src/utils/arithmetic'; import { precisionRound } from 'src/utils/arithmetic';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import ThaiBahtText from 'thai-baht-text'; import ThaiBahtText from 'thai-baht-text';
@ -175,8 +175,6 @@ enum View {
const view = ref<View>(View.Quotation); const view = ref<View>(View.Quotation);
onMounted(async () => { onMounted(async () => {
await configStore.getConfig();
const currentDocumentType = new URL(window.location.href).searchParams.get( const currentDocumentType = new URL(window.location.href).searchParams.get(
'type', 'type',
); );
@ -261,6 +259,18 @@ onMounted(async () => {
productList.value = productList.value =
(data?.value?.productServiceList ?? data.value?.productServiceList).map( (data?.value?.productServiceList ?? data.value?.productServiceList).map(
(v) => { (v) => {
const originalPrice = v.pricePerUnit;
const finalPriceWithVat = precisionRound(
originalPrice * (1 + (config.value?.vat || 0.07)),
);
const finalPriceNoVat =
finalPriceWithVat / (1 + (config.value?.vat || 0.07));
const price = finalPriceNoVat * v.amount - v.discount;
const vat =
(finalPriceNoVat * v.amount - v.discount) *
(config.value?.vat || 0.07);
return { return {
id: v.product.id, id: v.product.id,
code: v.product.code, code: v.product.code,
@ -269,8 +279,8 @@ onMounted(async () => {
pricePerUnit: v.pricePerUnit || 0, pricePerUnit: v.pricePerUnit || 0,
discount: v.discount || 0, discount: v.discount || 0,
vat: v.vat || 0, vat: v.vat || 0,
value: 0, value: precisionRound(price + (v.product.calcVat ? vat : 0)),
calcVat: v.vat > 0, calcVat: v.product.calcVat,
product: v.product, product: v.product,
}; };
}, },
@ -282,17 +292,23 @@ onMounted(async () => {
[] []
).reduce( ).reduce(
(a, c) => { (a, c) => {
const calcVat = c.vat > 0; const originalPrice = c.pricePerUnit;
const vatFactor = calcVat ? (config.value?.vat ?? 0.07) : 0; const finalPriceWithVat = precisionRound(
const pricePerUnit = originalPrice * (1 + (config.value?.vat || 0.07)),
precisionRound(c.pricePerUnit * (1 + vatFactor)) / (1 + vatFactor); );
const price = const finalPriceNoVat =
(pricePerUnit * c.amount * (1 + vatFactor) - c.discount) / finalPriceWithVat / (1 + (config.value?.vat || 0.07));
(1 + vatFactor);
a.totalPrice = precisionRound(a.totalPrice + price + c.discount); const price = finalPriceNoVat * c.amount;
const vat =
(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.totalDiscount = precisionRound(a.totalDiscount + Number(c.discount)); a.totalDiscount = precisionRound(a.totalDiscount + Number(c.discount));
a.vat = calcVat ? precisionRound(a.vat + c.vat) : a.vat; a.vat = calcVat ? precisionRound(a.vat + vat) : a.vat;
a.vatExcluded = calcVat a.vatExcluded = calcVat
? a.vatExcluded ? a.vatExcluded
: precisionRound(a.vatExcluded + price); : precisionRound(a.vatExcluded + price);
@ -318,12 +334,16 @@ onMounted(async () => {
function calcPrice(c: Product) { function calcPrice(c: Product) {
const originalPrice = c.pricePerUnit; const originalPrice = c.pricePerUnit;
const finalPricePerUnit = precisionRound( const finalPriceWithVat = precisionRound(
originalPrice + originalPrice + originalPrice * (config.value?.vat || 0.07),
(c.calcVat ? originalPrice * (config.value?.vat || 0.07) : 0),
); );
const price = finalPricePerUnit * c.amount - c.discount; const finalPriceNoVat = finalPriceWithVat / (1 + (config.value?.vat || 0.07));
return precisionRound(price);
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);
} }
async function closeTab() { async function closeTab() {
@ -407,15 +427,31 @@ 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.pricePerUnit, 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">
<template v-if="v.discount !== 0"> {{ formatNumberDecimal(v.discount, 2) }}
{{ formatNumberDecimal(v.discount, 2) }} ฿
</template>
</td> </td>
<td style="text-align: right"> <td style="text-align: right">
{{ Math.round((v.vat > 0 ? config?.vat || 0.07 : 0) * 100) }}% {{
formatNumberDecimal(
v.product[agentPrice ? 'agentPriceCalcVat' : 'calcVat']
? precisionRound(
(v.pricePerUnit * v.amount - v.discount) *
(config?.vat || 0.07),
)
: 0,
2,
)
}}
</td> </td>
<td style="text-align: right"> <td style="text-align: right">
{{ formatNumberDecimal(calcPrice(v), 2) }} {{ formatNumberDecimal(calcPrice(v), 2) }}
@ -475,9 +511,7 @@ function print() {
<td class="text-right"> <td class="text-right">
{{ {{
formatNumberDecimal( formatNumberDecimal(
summaryPrice.totalPrice - summaryPrice.totalPrice - summaryPrice.totalDiscount,
summaryPrice.totalDiscount -
summaryPrice.vatExcluded,
2, 2,
) )
}} }}
@ -566,7 +600,7 @@ function print() {
details?.worker.map( details?.worker.map(
(v, i) => (v, i) =>
`${i + 1}. ` + `${i + 1}. ` +
`${v.employeePassport.length !== 0 ? v.employeePassport[0].number + '_' : ''}${v.namePrefix}. ${v.firstNameEN ? `${v.firstNameEN} ${v.lastNameEN}` : `${v.firstName} ${v.lastName}`} `.toUpperCase(), `${v.namePrefix}. ${v.firstNameEN ? `${v.firstNameEN} ${v.lastNameEN}` : `${v.firstName} ${v.lastName}`} `.toUpperCase(),
) || [], ) || [],
}, },
}) || '-' }) || '-'

View file

@ -79,8 +79,8 @@ function titleMode(mode: View): string {
}) })
}} }}
</span> </span>
<span>{{ $t('branch.form.taxNo') }} {{ branch.taxNo }}</span> <span>เลขประจำตวผเสยภาษ {{ branch.taxNo }}</span>
<span>{{ $t('taskOrder.telephone') }} {{ branch.telephoneNo }}</span> <span>เบอรโทร {{ branch.telephoneNo }}</span>
<span>{{ branch.webUrl }}</span> <span>{{ branch.webUrl }}</span>
</article> </article>
<article> <article>
@ -105,18 +105,8 @@ function titleMode(mode: View): string {
}) })
}} }}
</span> </span>
<span> <span>เลขประจำตวผเสยภาษ {{ customer.citizenId }}</span>
{{ <span>เบอรโทร {{ customer.telephoneNo }}</span>
customer.customer.customerType === 'CORP'
? `${$t('customer.form.legalPersonNo')} `
: `${$t('customer.form.taxpayyerNo')} `
}}{{
customer.customer.customerType === 'CORP'
? customer.codeCustomer
: customer.citizenId
}}
</span>
<span>{{ $t('taskOrder.telephone') }} {{ customer.telephoneNo }}</span>
</article> </article>
</section> </section>
<section class="detail-quotation-info"> <section class="detail-quotation-info">

View file

@ -61,7 +61,6 @@ const props = withDefaults(
readonly?: boolean; readonly?: boolean;
listDocument: string[]; listDocument: string[];
currentId: { customer: string; employee: string }; currentId: { customer: string; employee: string };
prefix?: string;
}>(), }>(),
{ {
listDocument: () => [], listDocument: () => [],
@ -245,14 +244,14 @@ function changeCustomerTab(opts: { tab: 'customer' | 'employee' }) {
<nav class="q-ml-auto row" v-if="!readonly"> <nav class="q-ml-auto row" v-if="!readonly">
<CancelButton <CancelButton
v-if="state.isEdit" v-if="state.isEdit"
:id="`btn-docs-${props.prefix || 'nome'}-info-basic-undo`" id="btn-info-basic-undo"
icon-only icon-only
type="button" type="button"
@click.stop="triggerCancel" @click.stop="triggerCancel"
/> />
<EditButton <EditButton
v-if="!state.isEdit" v-if="!state.isEdit"
:id="`btn-docs-${props.prefix || 'nome'}-info-basic-edit`" id="btn-info-basic-edit"
icon-only icon-only
@click.stop="triggerEdit" @click.stop="triggerEdit"
type="button" type="button"

View file

@ -11,7 +11,6 @@ import { useRequestList } from 'src/stores/request-list';
const props = defineProps<{ const props = defineProps<{
readonly?: boolean; readonly?: boolean;
step: Step; step: Step;
prefix?: string;
}>(); }>();
const requestListStore = useRequestList(); const requestListStore = useRequestList();
@ -101,21 +100,21 @@ function assignToForm() {
<nav class="q-ml-auto row" v-if="!readonly"> <nav class="q-ml-auto row" v-if="!readonly">
<UndoButton <UndoButton
v-if="state.isEdit" v-if="state.isEdit"
:id="`btn-duty-${props.prefix || 'nome'}-info-basic-undo`" id="btn-info-basic-undo"
icon-only icon-only
type="button" type="button"
@click.stop="triggerUndo" @click.stop="triggerUndo"
/> />
<SaveButton <SaveButton
v-if="state.isEdit" v-if="state.isEdit"
:id="`btn-duty-${props.prefix || 'nome'}-info-basic-save`" id="btn-info-basic-save"
icon-only icon-only
type="submit" type="submit"
@click.stop="triggerSubmit" @click.stop="triggerSubmit"
/> />
<EditButton <EditButton
v-if="!state.isEdit" v-if="!state.isEdit"
:id="`btn-duty-${props.prefix || 'nome'}-info-basic-edit`" id="btn-info-basic-edit"
icon-only icon-only
@click.stop="triggerEdit" @click.stop="triggerEdit"
type="button" type="button"

View file

@ -12,7 +12,6 @@ const props = defineProps<{
readonly?: boolean; readonly?: boolean;
step: Step; step: Step;
requestWorkId: string; requestWorkId: string;
prefix?: string;
}>(); }>();
const requestListStore = useRequestList(); const requestListStore = useRequestList();
@ -91,21 +90,21 @@ function assignToForm() {
<nav class="q-ml-auto row" v-if="!readonly"> <nav class="q-ml-auto row" v-if="!readonly">
<UndoButton <UndoButton
v-if="state.isEdit" v-if="state.isEdit"
:id="`btn-form-${props.prefix || 'nome'}-info-basic-undo`" id="btn-info-basic-undo"
icon-only icon-only
type="button" type="button"
@click.stop="triggerUndo" @click.stop="triggerUndo"
/> />
<SaveButton <SaveButton
v-if="state.isEdit" v-if="state.isEdit"
:id="`btn-form-${props.prefix || 'nome'}-info-basic-save`" id="btn-info-basic-save"
icon-only icon-only
type="submit" type="submit"
@click.stop="triggerSubmit" @click.stop="triggerSubmit"
/> />
<EditButton <EditButton
v-if="!state.isEdit" v-if="!state.isEdit"
:id="`btn-form-${props.prefix || 'nome'}-info-basic-edit`" id="btn-info-basic-edit"
icon-only icon-only
@click.stop="triggerEdit" @click.stop="triggerEdit"
type="button" type="button"

View file

@ -12,7 +12,6 @@ const responsibleUserId = defineModel<string>('responsibleUserId', {
defineProps<{ defineProps<{
readonly?: boolean; readonly?: boolean;
districtId?: string; districtId?: string;
prefix?: string;
}>(); }>();
watch(responsibleUserLocal, (lhs, rhs) => { watch(responsibleUserLocal, (lhs, rhs) => {
@ -28,8 +27,6 @@ watch(responsibleUserLocal, (lhs, rhs) => {
:label="$t('requestList.localEmployee')" :label="$t('requestList.localEmployee')"
:disable="readonly" :disable="readonly"
class="col" class="col"
:id="`${prefix || 'nome'}-radio-local-employee`"
:for="`${prefix || 'nome'}-radio-local-employee`"
/> />
<q-radio <q-radio
v-model="responsibleUserLocal" v-model="responsibleUserLocal"
@ -37,8 +34,6 @@ watch(responsibleUserLocal, (lhs, rhs) => {
:label="$t('requestList.nonLocalEmployee')" :label="$t('requestList.nonLocalEmployee')"
:disable="readonly" :disable="readonly"
class="col" class="col"
:id="`${prefix || 'nome'}-radio-non-local-employee`"
:for="`${prefix || 'nome'}-radio-non-local-employee`"
/> />
<div class="col" /> <div class="col" />
<div class="offset-md-7"></div> <div class="offset-md-7"></div>
@ -57,8 +52,6 @@ watch(responsibleUserLocal, (lhs, rhs) => {
}" }"
:readonly :readonly
:label="$t('general.select', { msg: $t('personnel.MESSENGER') })" :label="$t('general.select', { msg: $t('personnel.MESSENGER') })"
:id="`${prefix || 'nome'}-select-user`"
:for="`${prefix || 'nome'}-select-user`"
/> />
</div> </div>
</div> </div>

View file

@ -95,7 +95,6 @@ function triggerCancel(id: string) {
const res = await requestListStore.cancelRequest(id); const res = await requestListStore.cancelRequest(id);
if (res) { if (res) {
fetchList(); fetchList();
fetchStats();
} }
}, },
}); });

View file

@ -14,7 +14,6 @@ const props = defineProps<{
step: Step; step: Step;
responsibleAreaDistrictId?: string; responsibleAreaDistrictId?: string;
defaultMessenger?: string; defaultMessenger?: string;
prefix?: string;
}>(); }>();
const emit = defineEmits<{ const emit = defineEmits<{
@ -117,21 +116,21 @@ function assignToForm() {
<nav class="q-ml-auto row" v-if="!readonly"> <nav class="q-ml-auto row" v-if="!readonly">
<UndoButton <UndoButton
v-if="state.isEdit" v-if="state.isEdit"
:id="`btn-messenger-${props.prefix || 'nome'}-info-basic-undo`" id="btn-info-basic-undo"
icon-only icon-only
type="button" type="button"
@click.stop="triggerUndo" @click.stop="triggerUndo"
/> />
<SaveButton <SaveButton
v-if="state.isEdit" v-if="state.isEdit"
:id="`btn-messenger-${props.prefix || 'nome'}-info-basic-save`" id="btn-info-basic-save"
icon-only icon-only
type="submit" type="submit"
@click.stop="(e) => refForm?.submit(e)" @click.stop="(e) => refForm?.submit(e)"
/> />
<EditButton <EditButton
v-if="!state.isEdit" v-if="!state.isEdit"
:id="`btn-messenger-${props.prefix || 'nome'}-info-basic-edit`" id="btn-info-basic-edit"
icon-only icon-only
@click.stop="triggerEdit" @click.stop="triggerEdit"
type="button" type="button"
@ -158,7 +157,6 @@ function assignToForm() {
v-model:responsible-user-local="formData.responsibleUserLocal" v-model:responsible-user-local="formData.responsibleUserLocal"
v-model:responsible-user-id="formData.responsibleUserId" v-model:responsible-user-id="formData.responsibleUserId"
:district-id="responsibleAreaDistrictId" :district-id="responsibleAreaDistrictId"
:prefix
/> />
</q-form> </q-form>
</section> </section>

View file

@ -83,8 +83,6 @@ function changeableStatus(currentStatus?: RequestWorkStatus) {
</script> </script>
<template> <template>
<q-expansion-item <q-expansion-item
:id="`expansion-${product?.name || name}`"
:for="`expansion-${product?.name || name}`"
dense dense
:class="{ 'status-unpaid': !paySuccess }" :class="{ 'status-unpaid': !paySuccess }"
class="overflow-hidden" class="overflow-hidden"
@ -148,8 +146,6 @@ function changeableStatus(currentStatus?: RequestWorkStatus) {
<div class="q-ml-auto q-gutter-y-xs"> <div class="q-ml-auto q-gutter-y-xs">
<div class="justify-end flex"> <div class="justify-end flex">
<q-btn-dropdown <q-btn-dropdown
:id="`btn-dropdown-${product?.name || name}`"
:for="`btn-dropdown-${product?.name || name}`"
:disable=" :disable="
readonly || changeableStatus(status?.workStatus).length === 0 readonly || changeableStatus(status?.workStatus).length === 0
" "
@ -201,8 +197,6 @@ function changeableStatus(currentStatus?: RequestWorkStatus) {
<q-list dense> <q-list dense>
<q-item <q-item
v-for="(value, index) in changeableStatus(status?.workStatus)" v-for="(value, index) in changeableStatus(status?.workStatus)"
:id="`btn-dropdown-${product?.name || name}-${value}`"
:for="`btn-dropdown-${product?.name || name}-${value}`"
:key="index" :key="index"
clickable clickable
v-close-popup v-close-popup
@ -278,14 +272,14 @@ function changeableStatus(currentStatus?: RequestWorkStatus) {
:deep( :deep(
i.q-icon.mdi.mdi-chevron-down-circle.q-expansion-item__toggle-icon.q-expansion-item__toggle-icon--rotated i.q-icon.mdi.mdi-chevron-down-circle.q-expansion-item__toggle-icon.q-expansion-item__toggle-icon--rotated
) { ) {
color: var(--brand-1); color: var(--brand-1);
} }
:deep( :deep(
.q-item.q-item-type.row.no-wrap.q-item--dense.q-item--clickable.q-link.cursor-pointer.q-focusable.q-hoverable.surface-1 .q-item.q-item-type.row.no-wrap.q-item--dense.q-item--clickable.q-link.cursor-pointer.q-focusable.q-hoverable.surface-1
.q-focus-helper .q-focus-helper
) { ) {
visibility: hidden; visibility: hidden;
} }

View file

@ -28,7 +28,6 @@ const props = withDefaults(
readonly?: boolean; readonly?: boolean;
propertiesToShow: (PropString | PropNumber | PropDate | PropOptions)[]; propertiesToShow: (PropString | PropNumber | PropDate | PropOptions)[];
requestListData: RequestData; requestListData: RequestData;
prefix?: string;
}>(), }>(),
{ {
id: '', id: '',
@ -129,7 +128,7 @@ defineEmits<{
<nav class="q-ml-auto row" v-if="!readonly"> <nav class="q-ml-auto row" v-if="!readonly">
<UndoButton <UndoButton
v-if="state.isEdit" v-if="state.isEdit"
:id="`btn-properties-${props.prefix || 'nome'}-info-basic-undo`" id="btn-info-basic-undo"
icon-only icon-only
type="button" type="button"
@click.stop="triggerUndo" @click.stop="triggerUndo"
@ -137,14 +136,13 @@ defineEmits<{
<SaveButton <SaveButton
v-if="state.isEdit" v-if="state.isEdit"
id="btn-info-basic-save" id="btn-info-basic-save"
:id="`btn-properties-${props.prefix || 'nome'}-info-basic-save`"
icon-only icon-only
type="submit" type="submit"
@click.stop="triggerSubmit" @click.stop="triggerSubmit"
/> />
<EditButton <EditButton
v-if="!state.isEdit" v-if="!state.isEdit"
:id="`btn-properties-${props.prefix || 'nome'}-info-basic-edit`" id="btn-info-basic-edit"
icon-only icon-only
@click.stop="triggerEdit" @click.stop="triggerEdit"
type="button" type="button"

View file

@ -881,7 +881,6 @@ function toEmployee(employee: RequestData['employee']) {
:readonly="value._readonly" :readonly="value._readonly"
ref="refDocumentExpansion" ref="refDocumentExpansion"
:attributes="value.attributes" :attributes="value.attributes"
:prefix="value.productService.product.name"
@change-status=" @change-status="
(opt) => { (opt) => {
triggerChangeStatusFile({ triggerChangeStatusFile({
@ -929,7 +928,6 @@ function toEmployee(employee: RequestData['employee']) {
<MessengerExpansion <MessengerExpansion
v-if="value._messengerExpansion" v-if="value._messengerExpansion"
:readonly="value._readonly" :readonly="value._readonly"
:prefix="value.productService.product.name"
:default-messenger=" :default-messenger="
value.stepStatus[pageState.currentStep - 1] value.stepStatus[pageState.currentStep - 1]
? undefined ? undefined
@ -955,7 +953,6 @@ function toEmployee(employee: RequestData['employee']) {
<DutyExpansion <DutyExpansion
v-if="value._dutyExpansion" v-if="value._dutyExpansion"
:readonly="value._readonly" :readonly="value._readonly"
:prefix="value.productService.product.name"
:step="{ :step="{
step: pageState.currentStep, step: pageState.currentStep,
requestWorkId: value.id || '', requestWorkId: value.id || '',
@ -969,7 +966,6 @@ function toEmployee(employee: RequestData['employee']) {
v-if="value._formExpansion" v-if="value._formExpansion"
:request-work-id="value.id" :request-work-id="value.id"
:readonly="value._readonly" :readonly="value._readonly"
:prefix="value.productService.product.name"
:step="{ :step="{
step: pageState.currentStep, step: pageState.currentStep,
requestWorkId: value.id || '', requestWorkId: value.id || '',
@ -983,7 +979,6 @@ function toEmployee(employee: RequestData['employee']) {
:request-list-data="data" :request-list-data="data"
:id="value.id" :id="value.id"
:readonly="value._readonly" :readonly="value._readonly"
:prefix="value.productService.product.name"
:properties-to-show=" :properties-to-show="
value.productService.work?.attributes.workflowStep[ value.productService.work?.attributes.workflowStep[
pageState.currentStep - 1 pageState.currentStep - 1
@ -1013,7 +1008,6 @@ function toEmployee(employee: RequestData['employee']) {
v-if=" v-if="
productsList.some((v) => v._messengerExpansion) && productsList.some((v) => v._messengerExpansion) &&
!( !(
data.requestDataStatus === RequestDataStatus.Completed ||
data.requestDataStatus === RequestDataStatus.Canceled || data.requestDataStatus === RequestDataStatus.Canceled ||
(responsibleList && (responsibleList &&
!responsibleList[pageState.currentStep]?.user.find( !responsibleList[pageState.currentStep]?.user.find(

View file

@ -190,8 +190,6 @@ function handleCheckAll() {
> >
<q-th v-if="checkable"> <q-th v-if="checkable">
<q-checkbox <q-checkbox
:for="`list-checkbox-all`"
:id="`list-checkbox-all`"
v-if="selected.length > 0" v-if="selected.length > 0"
:model-value=" :model-value="
selected.length === selected.length ===
@ -231,8 +229,6 @@ function handleCheckAll() {
> >
<q-td v-if="checkable"> <q-td v-if="checkable">
<q-checkbox <q-checkbox
:for="`list-checkbox-${props.rowIndex + 1}`"
:id="`list-checkbox-${props.rowIndex + 1}`"
:disable=" :disable="
selected.length > 0 && selected.length > 0 &&
!listSameArea.includes( !listSameArea.includes(
@ -318,7 +314,7 @@ function handleCheckAll() {
/> --> /> -->
<AvatarGroup <AvatarGroup
:data="[ :data="[
...(responsiblePerson(props.row.quotation)?.user.map((v) => ({ ...responsiblePerson(props.row.quotation).user.map((v) => ({
name: name:
$i18n.locale === 'eng' $i18n.locale === 'eng'
? `${v.firstNameEN} ${v.lastNameEN}` ? `${v.firstNameEN} ${v.lastNameEN}`
@ -328,11 +324,11 @@ function handleCheckAll() {
? `/no-img-man.png` ? `/no-img-man.png`
: `/no-img-female.png` : `/no-img-female.png`
: `${baseUrl}/user/${v.id}/profile-image/${v.selectedImage}`, : `${baseUrl}/user/${v.id}/profile-image/${v.selectedImage}`,
})) || []), })),
...(responsiblePerson(props.row.quotation)?.group.map((g) => ({ ...responsiblePerson(props.row.quotation).group.map((g) => ({
name: `${$t('general.group')} ${g.group}`, name: `${$t('general.group')} ${g.group}`,
imgUrl: '/img-group.png', imgUrl: '/img-group.png',
})) || []), })),
]" ]"
></AvatarGroup> ></AvatarGroup>
</q-td> </q-td>

View file

@ -1,6 +1,6 @@
<script lang="ts" setup> <script lang="ts" setup>
// NOTE: Library // NOTE: Library
import { computed, onMounted, onUnmounted, reactive, watch, ref } from 'vue'; import { computed, onMounted, reactive, watch, ref } from 'vue';
import { storeToRefs } from 'pinia'; import { storeToRefs } from 'pinia';
import { useQuasar } from 'quasar'; import { useQuasar } from 'quasar';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
@ -86,7 +86,6 @@ async function fetchTaskOrderList(opts?: { page?: number; pageSize?: number }) {
}); });
} }
if (res) { if (res) {
taskOrderStore.getTaskOrderStats();
data.value = res.result; data.value = res.result;
pageState.total = res.total; pageState.total = res.total;
pageMax.value = Math.ceil(res.total / pageSize.value); pageMax.value = Math.ceil(res.total / pageSize.value);
@ -147,10 +146,6 @@ async function deleteTaskOrder(id: string) {
}); });
} }
function handleWindowFocus() {
fetchTaskOrderList();
}
onMounted(async () => { onMounted(async () => {
pageState.gridView = $q.screen.lt.md ? true : false; pageState.gridView = $q.screen.lt.md ? true : false;
navigatorStore.current.title = 'taskOrder.title'; navigatorStore.current.title = 'taskOrder.title';
@ -161,12 +156,6 @@ onMounted(async () => {
if (route.query['tab'] && typeof route.query['tab'] === 'string') { if (route.query['tab'] && typeof route.query['tab'] === 'string') {
pageState.currentTab = route.query['tab']; pageState.currentTab = route.query['tab'];
} }
window.addEventListener('focus', handleWindowFocus);
});
onUnmounted(() => {
window.removeEventListener('focus', handleWindowFocus);
}); });
watch( watch(
@ -229,11 +218,7 @@ watch(
color: hsl(var(--info-bg)); color: hsl(var(--info-bg));
" "
> >
{{ {{ Object.values(stats).reduce((s, v) => s + v, 0) }}
pageState.isMessenger
? pageState.total
: stats[pageState.currentTab as TaskOrderStatus]
}}
</q-badge> </q-badge>
<q-btn <q-btn
class="q-ml-sm" class="q-ml-sm"

View file

@ -295,7 +295,6 @@ function assignTempGroup() {
class="bordered-b" class="bordered-b"
> >
<q-expansion-item <q-expansion-item
:id="`expansion-product-${product.code}`"
dense dense
class="overflow-hidden" class="overflow-hidden"
switch-toggle-side switch-toggle-side
@ -349,7 +348,6 @@ function assignTempGroup() {
</FormGroupHead> </FormGroupHead>
<div class="q-pa-md full-width"> <div class="q-pa-md full-width">
<TableEmployee <TableEmployee
:id="`table-employee-${product.code}`"
checkbox-on checkbox-on
check-all check-all
select-ready select-ready
@ -373,15 +371,9 @@ function assignTempGroup() {
</section> </section>
<template #footer> <template #footer>
<CancelButton <CancelButton class="q-ml-auto" outlined @click="close" />
id="btn-dialog-cancel"
class="q-ml-auto"
outlined
@click="close"
/>
<SaveButton <SaveButton
:label="$t('general.select')" :label="$t('general.select')"
id="btn-dialog-select"
class="q-ml-sm" class="q-ml-sm"
icon="mdi-check" icon="mdi-check"
solid solid

View file

@ -225,7 +225,6 @@ function disableCheckAll() {
<template> <template>
<q-table <q-table
flat flat
id="table-employee"
bordered bordered
row-key="id" row-key="id"
v-bind="props" v-bind="props"
@ -274,7 +273,6 @@ function disableCheckAll() {
> >
<q-th v-if="checkboxOn" class="relative-position"> <q-th v-if="checkboxOn" class="relative-position">
<q-checkbox <q-checkbox
id="checkbox-check-all"
v-if="checkAll" v-if="checkAll"
:disable="disableCheckAll()" :disable="disableCheckAll()"
:model-value=" :model-value="
@ -307,7 +305,6 @@ function disableCheckAll() {
class="absolute-right row items-center" class="absolute-right row items-center"
> >
<q-btn <q-btn
id="btn-change-all-status"
flat flat
dense dense
rounded rounded
@ -342,7 +339,6 @@ function disableCheckAll() {
{{ $t(`taskOrder.status.Complete`) }} {{ $t(`taskOrder.status.Complete`) }}
</q-item> </q-item>
<q-item <q-item
id="menu-item-redo"
clickable clickable
v-close-popup v-close-popup
class="items-center" class="items-center"
@ -362,7 +358,6 @@ function disableCheckAll() {
{{ $t(`taskOrder.status.Redo`) }} {{ $t(`taskOrder.status.Redo`) }}
</q-item> </q-item>
<q-item <q-item
id="menu-item-restart"
clickable clickable
v-close-popup v-close-popup
class="items-center" class="items-center"
@ -384,7 +379,6 @@ function disableCheckAll() {
</q-list> </q-list>
<q-list v-if="!validate" dense> <q-list v-if="!validate" dense>
<q-item <q-item
id="menu-item-success"
clickable clickable
v-close-popup v-close-popup
class="items-center" class="items-center"
@ -404,7 +398,6 @@ function disableCheckAll() {
{{ $t(`taskOrder.status.Success`) }} {{ $t(`taskOrder.status.Success`) }}
</q-item> </q-item>
<q-item <q-item
id="menu-item-failed"
clickable clickable
v-close-popup v-close-popup
class="items-center" class="items-center"
@ -487,7 +480,6 @@ function disableCheckAll() {
<q-td> <q-td>
<span <span
class="cursor-pointer link" class="cursor-pointer link"
:id="`link-request-list-${props.row.request.code}`"
@click="goToRequestList(props.row.request.id)" @click="goToRequestList(props.row.request.id)"
> >
{{ props.row.request.code }} {{ props.row.request.code }}
@ -497,7 +489,6 @@ function disableCheckAll() {
<q-td> <q-td>
<span <span
class="cursor-pointer link" class="cursor-pointer link"
:id="`link-quotation-${props.row.request.quotation?.code}`"
@click="goToQuotation(props.row.request.quotation)" @click="goToQuotation(props.row.request.quotation)"
> >
{{ props.row.request.quotation?.code }} {{ props.row.request.quotation?.code }}
@ -505,11 +496,7 @@ function disableCheckAll() {
</q-td> </q-td>
<q-td v-if="stepOn" class="text-left"> <q-td v-if="stepOn" class="text-left">
<div <div v-if="props.row._template" class="column text-left">
v-if="props.row._template"
class="column text-left"
:id="`template-step-${props.row.request.code}`"
>
<span>{{ props.row._template.templateName }}</span> <span>{{ props.row._template.templateName }}</span>
<span class="app-text-muted text-caption"> <span class="app-text-muted text-caption">
{{ $t('flow.stepNo', { msg: props.row._template.step }) }} {{ $t('flow.stepNo', { msg: props.row._template.step }) }}
@ -535,10 +522,7 @@ function disableCheckAll() {
</template> </template>
</q-img> </q-img>
</q-avatar> </q-avatar>
<div <div class="column text-left q-ml-sm">
class="column text-left q-ml-sm"
:id="`employee-name-${props.row.request.employee?.code}`"
>
<div> <div>
{{ getEmployeeName(props.row, { locale: $i18n.locale }) }} {{ getEmployeeName(props.row, { locale: $i18n.locale }) }}
</div> </div>
@ -548,7 +532,6 @@ function disableCheckAll() {
</div> </div>
</div> </div>
<Icon <Icon
:id="`icon-gender-${props.row.request.employee?.code}`"
class="q-ml-md" class="q-ml-md"
:class="`app-text-${props.row.request.employee?.gender}`" :class="`app-text-${props.row.request.employee?.gender}`"
:icon="`material-symbols:${props.row.request.employee?.gender}`" :icon="`material-symbols:${props.row.request.employee?.gender}`"
@ -556,18 +539,13 @@ function disableCheckAll() {
/> />
</div> </div>
</q-td> </q-td>
<q-td :id="`employee-age-${props.row.request.employee?.code}`"> <q-td>{{ calculateAge(props.row.request.employee?.dateOfBirth) }}</q-td>
{{ calculateAge(props.row.request.employee?.dateOfBirth) }} <q-td>
</q-td>
<q-td :id="`employee-nationality-${props.row.request.employee?.code}`">
{{ {{
useOptionStore().mapOption(props.row.request.employee?.nationality) useOptionStore().mapOption(props.row.request.employee?.nationality)
}} }}
</q-td> </q-td>
<q-td <q-td>
:id="`quotation-due-date-${props.row.request.quotation?.code}`"
v-if="!statusOn"
>
{{ {{
dateFormatJS({ dateFormatJS({
date: props.row.request.quotation?.dueDate, date: props.row.request.quotation?.dueDate,
@ -577,16 +555,13 @@ function disableCheckAll() {
}) })
}} }}
</q-td> </q-td>
<q-td <q-td>
:id="`expiration-date-${props.row.request.quotation?.code}`"
v-if="!statusOn"
>
<ExpirationDate <ExpirationDate
:expiration-date="new Date(props.row.request.quotation?.dueDate)" :expiration-date="new Date(props.row.request.quotation?.dueDate)"
/> />
</q-td> </q-td>
<q-td v-if="!statusOn"> <q-td>
<BadgeComponent <BadgeComponent
v-if="props.row.request.quotation?.urgent" v-if="props.row.request.quotation?.urgent"
icon="mdi-fire" icon="mdi-fire"

View file

@ -114,7 +114,6 @@ const emit = defineEmits<{
</script> </script>
<template> <template>
<q-table <q-table
id="table-task-order"
v-bind="props" v-bind="props"
:columns="column" :columns="column"
bordered bordered
@ -134,11 +133,7 @@ const emit = defineEmits<{
:props="props" :props="props"
> >
<q-th v-if="selection !== 'none'"> <q-th v-if="selection !== 'none'">
<q-checkbox <q-checkbox v-model="props.selected" size="sm" />
id="checkbox-select-all-task"
v-model="props.selected"
size="sm"
/>
</q-th> </q-th>
<q-th v-for="col in props.cols" :key="col.name" :props="props"> <q-th v-for="col in props.cols" :key="col.name" :props="props">
{{ col.label && $t(col.label) }} {{ col.label && $t(col.label) }}
@ -154,21 +149,13 @@ const emit = defineEmits<{
> >
<q-tr class="text-center" :class="{ urgent: props.row.urgent }"> <q-tr class="text-center" :class="{ urgent: props.row.urgent }">
<q-td v-if="selection !== 'none'"> <q-td v-if="selection !== 'none'">
<q-checkbox <q-checkbox v-model="props.selected" size="sm" />
:id="`checkbox-task-${props.row.code}`"
v-model="props.selected"
size="sm"
/>
</q-td> </q-td>
<q-td v-if="visibleColumns.includes('order')"> <q-td v-if="visibleColumns.includes('order')">
{{ props.rowIndex + 1 }} {{ props.rowIndex + 1 }}
</q-td> </q-td>
<q-td <q-td v-if="visibleColumns.includes('taskName')" class="text-left">
v-if="visibleColumns.includes('taskName')" <div>
class="text-left"
:id="`task-name-${props.row.code}`"
>
<div :id="`task-name-div-${props.row.code}`">
{{ props.row.taskName || '-' }} {{ props.row.taskName || '-' }}
<q-tooltip :delay="300"> <q-tooltip :delay="300">
{{ props.row.taskName || '-' }} {{ props.row.taskName || '-' }}
@ -183,51 +170,31 @@ const emit = defineEmits<{
}} }}
</div> </div>
</q-td> </q-td>
<q-td <q-td v-if="visibleColumns.includes('issueBranch')">
v-if="visibleColumns.includes('issueBranch')"
:id="`task-issue-branch-${props.row.code}`"
>
{{ {{
$i18n.locale === 'eng' $i18n.locale === 'eng'
? props.row.registeredBranch.nameEN || '-' ? props.row.registeredBranch.nameEN || '-'
: props.row.registeredBranch.name || '-' : props.row.registeredBranch.name || '-'
}} }}
</q-td> </q-td>
<q-td <q-td v-if="visibleColumns.includes('institution')">
v-if="visibleColumns.includes('institution')"
:id="`task-institution-${props.row.code}`"
>
{{ {{
$i18n.locale === 'eng' $i18n.locale === 'eng'
? props.row.institution.nameEN || '-' ? props.row.institution.nameEN || '-'
: props.row.institution.name || '-' : props.row.institution.name || '-'
}} }}
</q-td> </q-td>
<q-td <q-td v-if="visibleColumns.includes('createdAt')">
v-if="visibleColumns.includes('createdAt')"
:id="`task-created-at-${props.row.code}`"
>
{{ dateFormatJS({ date: props.row.createdAt }) || '-' }} {{ dateFormatJS({ date: props.row.createdAt }) || '-' }}
{{ dateFormatJS({ date: props.row.createdAt, timeOnly: true }) }} {{ dateFormatJS({ date: props.row.createdAt, timeOnly: true }) }}
</q-td> </q-td>
<q-td <q-td v-if="visibleColumns.includes('createdBy')" class="text-left">
v-if="visibleColumns.includes('createdBy')"
class="text-left"
:id="`task-created-by-${props.row.code}`"
>
{{ getCreatedByName(props.row, $i18n) }} {{ getCreatedByName(props.row, $i18n) }}
</q-td> </q-td>
<q-td <q-td v-if="visibleColumns.includes('contactTel')">
v-if="visibleColumns.includes('contactTel')"
:id="`task-contact-tel-${props.row.code}`"
>
{{ props.row.contactTel || '-' }} {{ props.row.contactTel || '-' }}
</q-td> </q-td>
<q-td <q-td v-if="visibleColumns.includes('contactName')" class="text-left">
v-if="visibleColumns.includes('contactName')"
class="text-left"
:id="`task-contact-name-${props.row.code}`"
>
{{ props.row.contactName || '-' }} {{ props.row.contactName || '-' }}
</q-td> </q-td>
<q-td v-if="visibleColumns.includes('taskStatus')"> <q-td v-if="visibleColumns.includes('taskStatus')">
@ -235,12 +202,11 @@ const emit = defineEmits<{
hide-icon hide-icon
:hsla-color="taskOrderStatus(props.row.taskOrderStatus, 'color')" :hsla-color="taskOrderStatus(props.row.taskOrderStatus, 'color')"
:title="$t(taskOrderStatus(props.row.taskOrderStatus, 'status'))" :title="$t(taskOrderStatus(props.row.taskOrderStatus, 'status'))"
:id="`badge-task-status-${props.row.code}`"
/> />
</q-td> </q-td>
<q-td v-if="selection === 'none'"> <q-td v-if="selection === 'none'">
<q-btn <q-btn
:id="`btn-view-task-${props.row.code}`" :id="`btn-eye-${props.row.taskName}`"
icon="mdi-eye-outline" icon="mdi-eye-outline"
size="sm" size="sm"
dense dense
@ -255,7 +221,7 @@ const emit = defineEmits<{
canAccess('taskOrder', 'edit') canAccess('taskOrder', 'edit')
" "
:hide-delete="!canAccess('taskOrder', 'create')" :hide-delete="!canAccess('taskOrder', 'create')"
:idName="`btn-kebab-${props.row.code}`" :idName="`btn-kebab-${props.row.taskName}`"
status="'ACTIVE'" status="'ACTIVE'"
hide-toggle hide-toggle
@view="$emit('view', props.row)" @view="$emit('view', props.row)"
@ -267,7 +233,6 @@ const emit = defineEmits<{
<q-btn <q-btn
dense dense
flat flat
:id="`btn-sub-row-${props.row.code}`"
class="rounded" class="rounded"
@click.stop=" @click.stop="
() => { () => {

View file

@ -60,25 +60,16 @@ function inactiveCheck() {
</script> </script>
<template> <template>
<div <div
:id="`readonly-status-${status}`"
:for="`readonly-status-${status}`"
v-if="readonly" v-if="readonly"
class="row rounded bordered surface-2 items-center justify-center q-pa-xs no-wrap" class="row rounded bordered surface-2 items-center justify-center q-pa-xs no-wrap"
:style="`color: hsl(var(--${currStatus?.color}-bg))`" :style="`color: hsl(var(--${currStatus?.color}-bg))`"
> >
<q-icon <q-icon :name="currStatus?.icon" class="q-pr-xs" size="xs" />
:id="`readonly-status-icon-${status}`" {{ $t(`taskOrder.status.${status}`) }}
:name="currStatus?.icon"
class="q-pr-xs"
size="xs"
/>
<span :id="`readonly-status-label-${status}`">{{ $t(`taskOrder.status.${status}`) }}</span>
</div> </div>
<div v-else class="row items-center justify-center no-wrap"> <div v-else class="row items-center justify-center no-wrap">
<q-btn-dropdown <q-btn-dropdown
:id="`btn-dropdown-status-${status}`"
:for="`btn-dropdown-status-${status}`"
dense dense
unelevated unelevated
:label=" :label="
@ -119,8 +110,6 @@ function inactiveCheck() {
> >
<q-list v-if="!noAction" dense> <q-list v-if="!noAction" dense>
<q-item <q-item
:for="`menu-item-status-${v.value}`"
:id="`menu-item-status-${v.value}`"
v-for="(v, index) in type === 'order' v-for="(v, index) in type === 'order'
? { ? {
Success: taskStatusOrderToggle.filter( Success: taskStatusOrderToggle.filter(
@ -158,8 +147,6 @@ function inactiveCheck() {
</q-btn-dropdown> </q-btn-dropdown>
<q-btn <q-btn
:id="`btn-failed-remark-${status}`"
:for="`btn-failed-remark-${status}`"
v-if="currStatus?.value === TaskStatus.Failed" v-if="currStatus?.value === TaskStatus.Failed"
flat flat
dense dense
@ -201,7 +188,7 @@ function inactiveCheck() {
:deep( :deep(
.hide-icon .hide-icon
i.q-icon.mdi.mdi-chevron-down.q-btn-dropdown__arrow.q-btn-dropdown__arrow-container i.q-icon.mdi.mdi-chevron-down.q-btn-dropdown__arrow.q-btn-dropdown__arrow-container
) { ) {
display: none; display: none;
} }
</style> </style>

View file

@ -202,44 +202,40 @@ onMounted(async () => {
}) })
.reduce( .reduce(
(a, c) => { (a, c) => {
const vatFactor = c.product.serviceChargeCalcVat const priceNoVat = c.product.serviceChargeVatIncluded
? (config.value?.vat ?? 0.07) ? c.pricePerUnit / (1 + (config.value?.vat || 0.07))
: 0; : c.pricePerUnit;
const adjustedPriceWithVat = precisionRound(
priceNoVat * (1 + (config.value?.vat || 0.07)),
);
const adjustedPriceNoVat =
adjustedPriceWithVat / (1 + (config.value?.vat || 0.07));
const priceDiscountNoVat = adjustedPriceNoVat * c.amount - c.discount;
const pricePerUnit = const rawVatTotal = priceDiscountNoVat * (config.value?.vat || 0.07);
precisionRound(c.product.serviceCharge * (1 + vatFactor)) / const rawVat = rawVatTotal / c.amount;
(1 + vatFactor);
const amount = c.amount;
const discount = c.discount || 0;
const price =
(pricePerUnit * amount * (1 + vatFactor) - discount) /
(1 + vatFactor);
const vat = price * vatFactor;
product.value.push({ product.value.push({
id: c.product.id, id: c.product.id,
code: c.product.code, code: c.product.code,
detail: c.product.name, detail: c.product.name,
priceUnit: precisionRound(pricePerUnit), priceUnit: precisionRound(priceNoVat),
amount: c.amount, amount: c.amount,
discount: c.discount, discount: c.discount,
vat: c.product.serviceChargeCalcVat ? precisionRound(vat) : 0, vat: c.product.serviceChargeCalcVat ? precisionRound(rawVat) : 0,
value: precisionRound( value: precisionRound(
pricePerUnit * c.amount + priceNoVat * c.amount +
(c.product.serviceChargeCalcVat ? vat : 0), (c.product.serviceChargeCalcVat ? rawVatTotal : 0),
), ),
}); });
a.totalPrice = precisionRound(a.totalPrice + price + discount); a.totalPrice = a.totalPrice + priceDiscountNoVat;
a.totalDiscount = precisionRound(a.totalDiscount + discount); a.totalDiscount = a.totalDiscount + Number(c.discount);
a.vat = precisionRound(a.vat + vat); a.vat = c.product.calcVat ? a.vat + rawVatTotal : a.vat;
a.vatExcluded = c.product.serviceChargeCalcVat a.vatExcluded = c.product.calcVat
? a.vatExcluded ? a.vatExcluded
: precisionRound(a.vatExcluded + price); : precisionRound(a.vatExcluded + priceDiscountNoVat);
a.finalPrice = precisionRound(a.totalPrice - a.totalDiscount + a.vat); a.finalPrice = a.totalPrice - a.totalDiscount + a.vat;
return a; return a;
}, },
{ {
@ -315,7 +311,7 @@ function closeAble() {
border-bottom: 2px solid var(--main); border-bottom: 2px solid var(--main);
" "
> >
{{ $t('preview.productList') }} {{ $t('taskOrder.goodReceipt') }}
</span> </span>
<table ref="elements" class="q-mb-sm" cellpadding="0" style="width: 100%"> <table ref="elements" class="q-mb-sm" cellpadding="0" style="width: 100%">
@ -339,9 +335,7 @@ function closeAble() {
{{ formatNumberDecimal(v.priceUnit, 2) }} {{ formatNumberDecimal(v.priceUnit, 2) }}
</td> </td>
<td style="text-align: right"> <td style="text-align: right">
<template v-if="v.discount !== 0"> {{ formatNumberDecimal(v.discount, 2) }}
{{ formatNumberDecimal(v.discount, 2) }} ฿
</template>
</td> </td>
<td style="text-align: right"> <td style="text-align: right">
{{ formatNumberDecimal(v.vat, 2) }} {{ formatNumberDecimal(v.vat, 2) }}
@ -352,6 +346,7 @@ function closeAble() {
</tr> </tr>
</tbody> </tbody>
</table> </table>
<table <table
style="width: 40%; margin-left: auto" style="width: 40%; margin-left: auto"
class="q-mb-md" class="q-mb-md"
@ -403,9 +398,7 @@ function closeAble() {
<td class="text-right"> <td class="text-right">
{{ {{
formatNumberDecimal( formatNumberDecimal(
summaryPrice.totalPrice - summaryPrice.totalPrice - summaryPrice.totalDiscount,
summaryPrice.totalDiscount -
summaryPrice.vatExcluded,
2, 2,
) )
}} }}

View file

@ -64,8 +64,8 @@ defineProps<{
}) })
}} }}
</span> </span>
<span>{{ $t('branch.form.taxNo') }} {{ branch.taxNo }}</span> <span>เลขประจำตวผเสยภาษ {{ branch.taxNo }}</span>
<span>{{ $t('taskOrder.telephone') }} {{ branch.telephoneNo }}</span> <span>เบอรโทร {{ branch.telephoneNo }}</span>
<span>{{ branch.webUrl }}</span> <span>{{ branch.webUrl }}</span>
</article> </article>
<article> <article>

View file

@ -25,7 +25,6 @@ const fileData = defineModel<
</script> </script>
<template> <template>
<q-expansion-item <q-expansion-item
id="expansion-additional-file"
dense dense
class="overflow-hidden bordered full-width" class="overflow-hidden bordered full-width"
switch-toggle-side switch-toggle-side
@ -42,7 +41,6 @@ const fileData = defineModel<
<main class="q-px-md q-py-sm surface-1"> <main class="q-px-md q-py-sm surface-1">
<UploadFileSection <UploadFileSection
id="upload-additional-file"
multiple multiple
:layout="$q.screen.gt.sm ? 'column' : 'row'" :layout="$q.screen.gt.sm ? 'column' : 'row'"
:readonly :readonly

View file

@ -22,7 +22,6 @@ const contactTel = defineModel<string>('contactTel');
</script> </script>
<template> <template>
<q-expansion-item <q-expansion-item
id="expansion-document"
default-opened default-opened
dense dense
class="overflow-hidden bordered full-width" class="overflow-hidden bordered full-width"
@ -39,16 +38,13 @@ const contactTel = defineModel<string>('contactTel');
<main class="q-px-md q-py-sm surface-1 row q-col-gutter-sm"> <main class="q-px-md q-py-sm surface-1 row q-col-gutter-sm">
<SelectBranch <SelectBranch
id="select-issue-branch"
:readonly :readonly
required
class="col-md-4 col-12" class="col-md-4 col-12"
:label="`${$t('taskOrder.issueBranch')}${$i18n.locale === 'tha' ? $t('taskOrder.title') : ''}`" :label="`${$t('taskOrder.issueBranch')}${$i18n.locale === 'tha' ? $t('taskOrder.title') : ''}`"
v-model:value="registeredBranchId" v-model:value="registeredBranchId"
auto-select-on-single auto-select-on-single
/> />
<SelectInstitution <SelectInstitution
id="select-agencies"
:readonly :readonly
required required
class="col-md-4 col-12" class="col-md-4 col-12"
@ -59,7 +55,6 @@ const contactTel = defineModel<string>('contactTel');
auto-select-on-single auto-select-on-single
/> />
<DatePicker <DatePicker
id="datepicker-issue-date"
:label="$t('taskOrder.issueDate')" :label="$t('taskOrder.issueDate')"
class="col-md-2 col-6" class="col-md-2 col-6"
:model-value="issueDate || new Date(Date.now())" :model-value="issueDate || new Date(Date.now())"
@ -67,7 +62,6 @@ const contactTel = defineModel<string>('contactTel');
:disabled="!readonly" :disabled="!readonly"
/> />
<q-input <q-input
id="input-task-code"
:label="$t('taskOrder.code')" :label="$t('taskOrder.code')"
outlined outlined
dense dense
@ -78,7 +72,6 @@ const contactTel = defineModel<string>('contactTel');
/> />
<q-input <q-input
id="input-task-name"
:readonly :readonly
:label="$t('general.name', { msg: $t('taskOrder.title') })" :label="$t('general.name', { msg: $t('taskOrder.title') })"
outlined outlined
@ -87,7 +80,6 @@ const contactTel = defineModel<string>('contactTel');
v-model="taskName" v-model="taskName"
/> />
<q-input <q-input
id="input-contact-name"
:readonly :readonly
:label="$t('taskOrder.contactName')" :label="$t('taskOrder.contactName')"
outlined outlined
@ -96,7 +88,6 @@ const contactTel = defineModel<string>('contactTel');
v-model="contactName" v-model="contactName"
/> />
<q-input <q-input
id="input-contact-tel"
:readonly :readonly
:label="$t('general.telephone')" :label="$t('general.telephone')"
outlined outlined
@ -105,7 +96,6 @@ const contactTel = defineModel<string>('contactTel');
v-model="contactTel" v-model="contactTel"
/> />
<q-input <q-input
id="input-made-by"
:readonly :readonly
:label="$t('taskOrder.madeBy')" :label="$t('taskOrder.madeBy')"
outlined outlined

View file

@ -32,7 +32,6 @@ const summaryPrice = defineModel<{
</script> </script>
<template> <template>
<q-expansion-item <q-expansion-item
id="expansion-payment"
dense dense
class="overflow-hidden bordered full-width" class="overflow-hidden bordered full-width"
switch-toggle-side switch-toggle-side
@ -48,7 +47,6 @@ const summaryPrice = defineModel<{
<main class="q-px-md q-py-sm surface-1"> <main class="q-px-md q-py-sm surface-1">
<QuotationFormInfo <QuotationFormInfo
id="form-info-payment"
task-order task-order
:task-order-complete="complete" :task-order-complete="complete"
v-model:pay-type="payType" v-model:pay-type="payType"

View file

@ -14,10 +14,6 @@ import { storeToRefs } from 'pinia';
import BadgeComponent from 'src/components/BadgeComponent.vue'; import BadgeComponent from 'src/components/BadgeComponent.vue';
import { TaskStatus } from 'src/stores/task-order/types'; import { TaskStatus } from 'src/stores/task-order/types';
import { useTaskOrderForm } from '../form';
const taskOrderFormStore = useTaskOrderForm();
const currentBtnOpen = ref<boolean[]>([]); const currentBtnOpen = ref<boolean[]>([]);
const configStore = useConfigStore(); const configStore = useConfigStore();
const { data: config } = storeToRefs(configStore); const { data: config } = storeToRefs(configStore);
@ -65,28 +61,60 @@ function openList(index: number) {
} }
function calcPricePerUnit(product: RequestWork['productService']['product']) { function calcPricePerUnit(product: RequestWork['productService']['product']) {
return product.serviceCharge; const val = props.creditNote
? props.agentPrice
? product.agentPrice
: product.price
: product.serviceCharge;
if (
product[
props.creditNote
? props.agentPrice
? 'agentPriceCalcVat'
: 'calcVat'
: 'serviceChargeCalcVat'
]
) {
return val / (1 + (config.value?.vat || 0.07));
} else {
return val;
}
} }
function calcPrice( function calcPrice(
product: RequestWork['productService']['product'], product: RequestWork['productService']['product'],
amount: number, amount: number,
) { ) {
const vatFactor = product.serviceChargeCalcVat const pricePerUnit = props.creditNote
? (config.value?.vat ?? 0.07) ? props.agentPrice
: 0; ? product.agentPrice
: product.price
: 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[
props.creditNote
? props.agentPrice
? 'agentPriceVatIncluded'
: 'vatIncluded'
: 'serviceChargeVatIncluded'
]
? pricePerUnit / (1 + (config.value?.vat || 0.07))
: pricePerUnit;
const priceDiscountNoVat = priceNoVat * amount - discount;
const pricePerUnit = precisionRound(product.serviceCharge); const rawVatTotal = product[
props.creditNote
? props.agentPrice
? 'agentPriceCalcVat'
: 'calcVat'
: 'serviceChargeCalcVat'
]
? priceDiscountNoVat * (config.value?.vat || 0.07)
: 0;
const price = return precisionRound(priceNoVat * amount + rawVatTotal);
(pricePerUnit * amount * (1 + vatFactor) - discount) / (1 + vatFactor);
const vat = price * vatFactor;
return price + vat;
} }
function taskOrderStatus(value: TaskStatus) { function taskOrderStatus(value: TaskStatus) {
@ -111,7 +139,6 @@ function taskOrderStatus(value: TaskStatus) {
</script> </script>
<template> <template>
<q-expansion-item <q-expansion-item
id="expansion-product-list"
dense dense
class="overflow-hidden bordered full-width" class="overflow-hidden bordered full-width"
switch-toggle-side switch-toggle-side
@ -124,11 +151,9 @@ function taskOrderStatus(value: TaskStatus) {
<span <span
class="row items-center justify-between full-width" class="row items-center justify-between full-width"
style="min-height: 31.01px" style="min-height: 31.01px"
id="header-product-list"
> >
{{ $t('general.information', { msg: $t('taskOrder.productList') }) }} {{ $t('general.information', { msg: $t('taskOrder.productList') }) }}
<AddButton <AddButton
id="btn-add-product"
icon-only icon-only
@click.stop="$emit('addProduct')" @click.stop="$emit('addProduct')"
v-if="!readonly" v-if="!readonly"
@ -138,7 +163,6 @@ function taskOrderStatus(value: TaskStatus) {
<main class="q-px-md q-py-sm surface-1"> <main class="q-px-md q-py-sm surface-1">
<q-table <q-table
id="table-product-list"
:columns=" :columns="
creditNote creditNote
? productColumn.filter( ? productColumn.filter(
@ -184,7 +208,7 @@ function taskOrderStatus(value: TaskStatus) {
} & Omit<Parameters<QTableSlots['body']>[0], 'row'>" } & Omit<Parameters<QTableSlots['body']>[0], 'row'>"
> >
<q-tr class="text-center"> <q-tr class="text-center">
<q-td :id="`product-order-${props.rowIndex}`"> <q-td>
{{ props.rowIndex + 1 }} {{ props.rowIndex + 1 }}
</q-td> </q-td>
<q-td> <q-td>
@ -210,21 +234,20 @@ function taskOrderStatus(value: TaskStatus) {
</q-td> </q-td>
<q-td class="text-left" v-if="!creditNote"> <q-td class="text-left" v-if="!creditNote">
<BadgeComponent <BadgeComponent
:id="`badge-status-${props.row.product.code}`"
hide-icon hide-icon
:hsla-color="taskOrderStatus(props.row.product.taskStatus)" :hsla-color="taskOrderStatus(props.row.product.taskStatus)"
:title="`${$t(`taskOrder.status.${props.row.product.taskStatus}`)} ${!!props.row.product.totalNotStatusComplete ? $t('general.totalPeople', { meg: props.row.product.totalNotStatusComplete }) : ''}`" :title="`${$t(`taskOrder.status.${props.row.product.taskStatus}`)} ${!!props.row.product.totalNotStatusComplete ? $t('general.totalPeople', { meg: props.row.product.totalNotStatusComplete }) : ''}`"
/> />
</q-td> </q-td>
<q-td :id="`product-amount-${props.row.product.code}`"> <q-td>
{{ props.row.list.length }} {{ props.row.list.length }}
</q-td> </q-td>
<q-td :id="`product-price-per-unit-${props.row.product.code}`" class="text-right"> <q-td class="text-right">
{{ {{
formatNumberDecimal( formatNumberDecimal(
calcPricePerUnit(props.row.product) + calcPricePerUnit(props.row.product) +
(props.row.product.serviceChargeCalcVat (props.row.product.calcVat
? calcPricePerUnit(props.row.product) * ? calcPricePerUnit(props.row.product) *
(config?.vat || 0.07) (config?.vat || 0.07)
: 0), : 0),
@ -235,7 +258,6 @@ function taskOrderStatus(value: TaskStatus) {
<!-- TODO: display price detail --> <!-- TODO: display price detail -->
<q-td align="center" v-if="!creditNote"> <q-td align="center" v-if="!creditNote">
<q-input <q-input
:id="`input-discount-${props.row.product.code}`"
:readonly :readonly
:bg-color="readonly ? 'transparent' : ''" :bg-color="readonly ? 'transparent' : ''"
dense dense
@ -273,49 +295,37 @@ function taskOrderStatus(value: TaskStatus) {
/> />
</q-td> </q-td>
<!-- before vat --> <!-- before vat -->
<q-td <q-td class="text-right" v-if="!creditNote">
:id="`product-price-before-vat-${props.row.product.code}`"
class="text-right"
v-if="!creditNote"
>
{{ {{
formatNumberDecimal( formatNumberDecimal(
props.row.product.serviceChargeCalcVat calcPricePerUnit(props.row.product) * props.row.list.length -
? (calcPricePerUnit(props.row.product) * (taskProduct.find(
props.row.list.length *
(1 + (config?.vat || 0.07))) /
(1 + (config?.vat || 0.07))
: calcPricePerUnit(props.row.product) *
props.row.list.length -
taskProduct.find(
(v) => v.productId === props.row.product.id, (v) => v.productId === props.row.product.id,
)?.discount || 0, )?.discount || 0),
2,
) )
}} }}
</q-td> </q-td>
<!-- vat --> <!-- vat -->
<q-td <q-td class="text-right" v-if="!creditNote">
:id="`product-vat-${props.row.product.code}`"
class="text-right"
v-if="!creditNote"
>
{{ {{
formatNumberDecimal( formatNumberDecimal(
props.row.product.serviceChargeCalcVat props.row.product.calcVat
? ((calcPricePerUnit(props.row.product) * ? precisionRound(
props.row.list.length * (calcPricePerUnit(props.row.product) *
(1 + (config?.vat || 0.07)) - props.row.list.length -
taskProduct.find( (taskProduct.find(
(v) => v.productId === props.row.product.id, (v) => v.productId === props.row.product.id,
)?.discount || 0) / )?.discount || 0)) *
(1 + (config?.vat || 0.07))) * (config?.vat || 0.07),
0.07 )
: 0, : 0,
2,
) )
}} }}
</q-td> </q-td>
<!-- total --> <!-- total -->
<q-td :id="`product-total-price-${props.row.product.code}`" class="text-right"> <q-td class="text-right">
{{ {{
formatNumberDecimal( formatNumberDecimal(
calcPrice(props.row.product, props.row.list.length), calcPrice(props.row.product, props.row.list.length),
@ -325,7 +335,6 @@ function taskOrderStatus(value: TaskStatus) {
</q-td> </q-td>
<q-td> <q-td>
<q-btn <q-btn
:id="`btn-toggle-employee-${props.row.product.code}`"
dense dense
flat flat
class="rounded" class="rounded"
@ -349,7 +358,6 @@ function taskOrderStatus(value: TaskStatus) {
<q-tr v-show="currentBtnOpen[props.rowIndex]" :props="props"> <q-tr v-show="currentBtnOpen[props.rowIndex]" :props="props">
<q-td colspan="100%" style="padding: 16px"> <q-td colspan="100%" style="padding: 16px">
<TableEmployee <TableEmployee
:id="`table-employee-in-product-${props.row.product.code}`"
:step-on="!creditNote" :step-on="!creditNote"
:status-on="creditNote" :status-on="creditNote"
:rows="props.row.list" :rows="props.row.list"
@ -367,9 +375,7 @@ function taskOrderStatus(value: TaskStatus) {
}) })
}} }}
</span> </span>
<div class="surface-3 q-px-sm rounded" id="total-product-count"> <div class="surface-3 q-px-sm rounded">{{ taskList.length }}</div>
{{ taskList.length }}
</div>
</div> </div>
</main> </main>
</q-expansion-item> </q-expansion-item>

View file

@ -33,7 +33,6 @@ const getToolbarConfig = computed(() => {
</script> </script>
<template> <template>
<q-expansion-item <q-expansion-item
id="expansion-remark"
dense dense
class="overflow-hidden bordered full-width" class="overflow-hidden bordered full-width"
switch-toggle-side switch-toggle-side
@ -49,7 +48,6 @@ const getToolbarConfig = computed(() => {
<main class="surface-1 q-pa-md full-width"> <main class="surface-1 q-pa-md full-width">
<q-editor <q-editor
id="editor-remark"
dense dense
:readonly="readonly || !remarkWrite" :readonly="readonly || !remarkWrite"
:model-value=" :model-value="
@ -92,7 +90,6 @@ const getToolbarConfig = computed(() => {
<template v-if="!readonly" v-slot:toggle> <template v-if="!readonly" v-slot:toggle>
<div class="text-caption row no-wrap q-px-sm"> <div class="text-caption row no-wrap q-px-sm">
<MainButton <MainButton
id="btn-remark-view"
:solid="!remarkWrite" :solid="!remarkWrite"
icon="mdi-eye-outline" icon="mdi-eye-outline"
color="0 0% 40%" color="0 0% 40%"
@ -105,7 +102,6 @@ const getToolbarConfig = computed(() => {
{{ $t('general.view', { msg: $t('general.example') }) }} {{ $t('general.view', { msg: $t('general.example') }) }}
</MainButton> </MainButton>
<MainButton <MainButton
id="btn-remark-edit"
:solid="remarkWrite" :solid="remarkWrite"
icon="mdi-pencil-outline" icon="mdi-pencil-outline"
color="0 0% 40%" color="0 0% 40%"

View file

@ -47,17 +47,11 @@ defineProps<{
<div class="row q-col-gutter-sm q-px-md q-py-sm"> <div class="row q-col-gutter-sm q-px-md q-py-sm">
<DataDisplay <DataDisplay
class="col-md col-6" class="col-md col-6"
id="dd-recipient"
:label="$t('taskOrder.recipientOrSender')" :label="$t('taskOrder.recipientOrSender')"
> >
<template #value> <template #value>
<q-avatar size="md" class="q-mr-xs"> <q-avatar size="md" class="q-mr-xs">
<q-img <q-img class="text-center" :ratio="1" :src="contactUrl">
:id="`img-avatar-${contactName}`"
class="text-center"
:ratio="1"
:src="contactUrl"
>
<template #error> <template #error>
<div <div
class="no-padding full-width full-height flex items-center justify-center" class="no-padding full-width full-height flex items-center justify-center"
@ -65,7 +59,6 @@ defineProps<{
> >
<q-img <q-img
v-if="gender" v-if="gender"
:id="`img-gender-${contactName}`"
:src=" :src="
gender === 'male' gender === 'male'
? '/no-img-man.png' ? '/no-img-man.png'
@ -74,7 +67,6 @@ defineProps<{
/> />
<q-icon <q-icon
v-else v-else
:id="`icon-avatar-${contactName}`"
size="sm" size="sm"
name="mdi-account-outline" name="mdi-account-outline"
style="color: white" style="color: white"
@ -89,21 +81,18 @@ defineProps<{
<DataDisplay <DataDisplay
class="col-md col-6" class="col-md col-6"
id="dd-telephone"
:label="$t('general.telephone')" :label="$t('general.telephone')"
:value="contactTel || '-'" :value="contactTel || '-'"
/> />
<DataDisplay <DataDisplay
class="col-md col-6" class="col-md col-6"
id="dd-email"
:label="$t('form.email')" :label="$t('form.email')"
:value="email || '-'" :value="email || '-'"
/> />
<DataDisplay <DataDisplay
class="col-md col-6" class="col-md col-6"
id="dd-work-start-date"
:label="$t('taskOrder.workStartDate')" :label="$t('taskOrder.workStartDate')"
> >
<template #value> <template #value>
@ -117,7 +106,6 @@ defineProps<{
<DataDisplay <DataDisplay
class="col-md col-6" class="col-md col-6"
id="dd-work-submission-date"
:label="$t('taskOrder.workSubmissionDate')" :label="$t('taskOrder.workSubmissionDate')"
> >
<template #value> <template #value>
@ -129,14 +117,9 @@ defineProps<{
</template> </template>
</DataDisplay> </DataDisplay>
<DataDisplay <DataDisplay class="col-md col-6" :label="$t('general.status')">
id="dd-status"
class="col-md col-6"
:label="$t('general.status')"
>
<template #value> <template #value>
<BadgeComponent <BadgeComponent
:id="`badge-status-${contactName}`"
v-if="status" v-if="status"
hide-icon hide-icon
:title=" :title="

View file

@ -128,33 +128,32 @@ function getPrice(
})[]; })[];
}[], }[],
) { ) {
const value = list.reduce( return list.reduce(
(a, c) => { (a, c) => {
const vatFactor = c.product.serviceChargeCalcVat const pricePerUnit = c.product.serviceCharge;
? (config.value?.vat ?? 0.07)
: 0;
const pricePerUnit =
precisionRound(c.product.serviceCharge * (1 + vatFactor)) /
(1 + vatFactor);
const amount = c.list.length; const amount = c.list.length;
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.serviceChargeVatIncluded
? pricePerUnit / (1 + (config.value?.vat || 0.07))
: pricePerUnit;
const adjustedPriceWithVat = precisionRound(
priceNoVat * (1 + (config.value?.vat || 0.07)),
);
const adjustedPriceNoVat =
adjustedPriceWithVat / (1 + (config.value?.vat || 0.07));
const priceDiscountNoVat = adjustedPriceNoVat * amount - discount;
const price = const rawVatTotal = priceDiscountNoVat * (config.value?.vat || 0.07);
(pricePerUnit * amount * (1 + vatFactor) - discount) / (1 + vatFactor);
const vat = price * vatFactor; a.totalPrice = a.totalPrice + priceDiscountNoVat;
a.totalDiscount = a.totalDiscount + Number(discount);
a.totalPrice = precisionRound(a.totalPrice + price + discount); a.vat = c.product.serviceChargeCalcVat ? a.vat + rawVatTotal : a.vat;
a.totalDiscount = precisionRound(a.totalDiscount + discount);
a.vat = precisionRound(a.vat + vat);
a.vatExcluded = c.product.serviceChargeCalcVat a.vatExcluded = c.product.serviceChargeCalcVat
? a.vatExcluded ? a.vatExcluded
: precisionRound(a.vatExcluded + price); : precisionRound(a.vatExcluded + priceDiscountNoVat);
a.finalPrice = precisionRound(a.totalPrice - a.totalDiscount + a.vat); a.finalPrice = a.totalPrice - a.totalDiscount + a.vat;
return a; return a;
}, },
{ {
@ -165,8 +164,6 @@ function getPrice(
finalPrice: 0, finalPrice: 0,
}, },
); );
return value;
} }
function getTemplateData( function getTemplateData(
@ -814,7 +811,7 @@ watch(
> >
<section class="banner" :class="{ dark: $q.dark.isActive }"></section> <section class="banner" :class="{ dark: $q.dark.isActive }"></section>
<div style="flex: 1" class="row items-center"> <div style="flex: 1" class="row items-center">
<RouterLink to="/task-order" id="link-task-order"> <RouterLink to="/task-order">
<q-img src="/icons/favicon-512x512.png" width="3rem" /> <q-img src="/icons/favicon-512x512.png" width="3rem" />
</RouterLink> </RouterLink>
<span class="column text-h6 text-bold q-ml-md"> <span class="column text-h6 text-bold q-ml-md">
@ -863,7 +860,6 @@ watch(
<!-- TODO: replace step and status --> <!-- TODO: replace step and status -->
<StateButton <StateButton
v-for="i in statusTabForm" v-for="i in statusTabForm"
:id="`btn-status-${i.title}`"
:key="i.title" :key="i.title"
:label="$t(`taskOrder.${i.title}`)" :label="$t(`taskOrder.${i.title}`)"
:status-active="i.active?.()" :status-active="i.active?.()"
@ -917,7 +913,6 @@ watch(
" "
> >
<DocumentExpansion <DocumentExpansion
id="expansion-document"
:readonly="!['create', 'edit'].includes(state.mode || '')" :readonly="!['create', 'edit'].includes(state.mode || '')"
v-model:registered-branch-id="currentFormData.registeredBranchId" v-model:registered-branch-id="currentFormData.registeredBranchId"
v-model:institution-id="currentFormData.institutionId" v-model:institution-id="currentFormData.institutionId"
@ -937,7 +932,6 @@ watch(
/> />
</q-form> </q-form>
<ProductExpansion <ProductExpansion
id="expansion-product"
ref="refProductExpansion" ref="refProductExpansion"
v-if=" v-if="
view === TaskOrderStatus.Pending || view === TaskOrderStatus.Pending ||
@ -950,7 +944,6 @@ watch(
/> />
<PaymentExpansion <PaymentExpansion
id="expansion-payment"
v-model:summary-price="summaryPrice" v-model:summary-price="summaryPrice"
:complete="view === TaskOrderStatus.Complete" :complete="view === TaskOrderStatus.Complete"
v-if=" v-if="
@ -960,7 +953,6 @@ watch(
/> />
<AdditionalFileExpansion <AdditionalFileExpansion
id="expansion-additional-file"
:readonly="!['create', 'edit'].includes(state.mode || '')" :readonly="!['create', 'edit'].includes(state.mode || '')"
v-if=" v-if="
view === TaskOrderStatus.Pending || view === TaskOrderStatus.Pending ||
@ -1019,7 +1011,6 @@ watch(
/> />
<!-- TODO: blind remark, urgent --> <!-- TODO: blind remark, urgent -->
<RemarkExpansion <RemarkExpansion
id="expansion-remark"
v-if=" v-if="
view === TaskOrderStatus.Pending || view === TaskOrderStatus.Pending ||
view === TaskOrderStatus.Complete view === TaskOrderStatus.Complete
@ -1044,7 +1035,6 @@ watch(
" "
> >
<InfoMessengerExpansion <InfoMessengerExpansion
:id="`expansion-messenger-${messengerIndex}`"
v-for="(v, messengerIndex) in messengerListGroup" v-for="(v, messengerIndex) in messengerListGroup"
:key="messengerIndex" :key="messengerIndex"
:gender="getMessengerName(v.responsibleUser, { gender: true })" :gender="getMessengerName(v.responsibleUser, { gender: true })"
@ -1080,7 +1070,6 @@ watch(
class="bordered-b" class="bordered-b"
> >
<q-expansion-item <q-expansion-item
:id="`expansion-product-${productIndex}`"
dense dense
class="overflow-hidden" class="overflow-hidden"
switch-toggle-side switch-toggle-side
@ -1254,7 +1243,6 @@ watch(
<nav class="row justify-end"> <nav class="row justify-end">
<MainButton <MainButton
class="q-mr-auto" class="q-mr-auto"
id="btn-view-example"
v-if="currentFormData.id" v-if="currentFormData.id"
outlined outlined
icon="mdi-play-box-outline" icon="mdi-play-box-outline"
@ -1269,16 +1257,10 @@ watch(
{{ $t('general.view', { msg: $t('general.example') }) }} {{ $t('general.view', { msg: $t('general.example') }) }}
</MainButton> </MainButton>
<div class="row" style="gap: var(--size-2)"> <div class="row" style="gap: var(--size-2)">
<UndoButton <UndoButton outlined @click="undo()" v-if="state.mode === 'edit'" />
id="btn-undo"
outlined
@click="undo()"
v-if="state.mode === 'edit'"
/>
<CancelButton <CancelButton
v-if="state.mode !== 'edit'" v-if="state.mode !== 'edit'"
:label="$t('dialog.action.close')" :label="$t('dialog.action.close')"
id="btn-close"
outlined outlined
@click="closeTab()" @click="closeTab()"
/> />
@ -1290,14 +1272,12 @@ watch(
" "
> >
<SaveButton <SaveButton
id="btn-save"
v-if="state.mode && ['create', 'edit'].includes(state.mode)" v-if="state.mode && ['create', 'edit'].includes(state.mode)"
@click="() => formDocument.submit()" @click="() => formDocument.submit()"
solid solid
/> />
<EditButton <EditButton
v-else v-else
id="btn-edit"
class="no-print" class="no-print"
@click="state.mode = 'edit'" @click="state.mode = 'edit'"
solid solid
@ -1305,7 +1285,6 @@ watch(
</template> </template>
<SaveButton <SaveButton
v-if=" v-if="
canAccess('taskOrder', 'edit') &&
state.mode !== 'create' && state.mode !== 'create' &&
view === TaskOrderStatus.Validate && view === TaskOrderStatus.Validate &&
fullTaskOrder?.taskOrderStatus !== TaskOrderStatus.Pending && fullTaskOrder?.taskOrderStatus !== TaskOrderStatus.Pending &&
@ -1344,7 +1323,6 @@ watch(
" "
:label="$t('taskOrder.confirmEndWork')" :label="$t('taskOrder.confirmEndWork')"
icon="mdi-check" icon="mdi-check"
id="btn-confirm-end-work"
solid solid
></SaveButton> ></SaveButton>
</div> </div>
@ -1354,7 +1332,6 @@ watch(
<!-- SEC: Dialog --> <!-- SEC: Dialog -->
<SelectReadyRequestWork <SelectReadyRequestWork
id="dialog-select-request-work"
:task-list-group="taskListGroup" :task-list-group="taskListGroup"
:fetch-params="{ readyToTask: true }" :fetch-params="{ readyToTask: true }"
v-model:open="pageState.productDialog" v-model:open="pageState.productDialog"

View file

@ -98,7 +98,6 @@ function tooltip(id: string) {
<main class="q-py-sm q-px-md scroll"> <main class="q-py-sm q-px-md scroll">
<q-select <q-select
:readonly :readonly
id="select-fail-task"
dense dense
outlined outlined
multiple multiple
@ -132,7 +131,6 @@ function tooltip(id: string) {
> >
<template #selected-item="scope"> <template #selected-item="scope">
<q-chip <q-chip
:id="`chip-fail-task-${taskStatusList[scope.index].code}`"
dense dense
:removable="!readonly" :removable="!readonly"
@remove="scope.removeAtIndex(scope.index)" @remove="scope.removeAtIndex(scope.index)"
@ -172,7 +170,6 @@ function tooltip(id: string) {
<template #option="scope"> <template #option="scope">
<q-item <q-item
:id="`option-fail-task-${scope.opt.request.code}`"
clickable clickable
v-bind="scope.itemProps" v-bind="scope.itemProps"
class="row items-start col-12 no-padding" class="row items-start col-12 no-padding"
@ -233,7 +230,6 @@ function tooltip(id: string) {
<SelectInput <SelectInput
:readonly :readonly
id="select-fail-type"
:option="[ :option="[
{ {
label: $t('taskOrder.documentSubmitFailed'), label: $t('taskOrder.documentSubmitFailed'),
@ -264,7 +260,6 @@ function tooltip(id: string) {
<div v-if="failedType === 'other'" class="q-mt-sm rounded"> <div v-if="failedType === 'other'" class="q-mt-sm rounded">
<q-editor <q-editor
id="editor-fail-comment"
dense dense
flat flat
v-model="failedComment" v-model="failedComment"
@ -284,18 +279,8 @@ function tooltip(id: string) {
</main> </main>
<template #footer v-if="!readonly"> <template #footer v-if="!readonly">
<CancelButton <CancelButton class="q-ml-auto" outlined @click="$emit('close')" />
id="btn-fail-remark-cancel" <SaveButton class="q-ml-sm" solid @click="submit" />
class="q-ml-auto"
outlined
@click="$emit('close')"
/>
<SaveButton
id="btn-fail-remark-save"
class="q-ml-sm"
solid
@click="submit"
/>
</template> </template>
</DialogFormContainer> </DialogFormContainer>
</template> </template>

View file

@ -394,14 +394,8 @@ watch(
{ {
label: $t('general.customer'), label: $t('general.customer'),
value: value:
item.row.quotation.customerBranch.customer item.row.quotation.customerBranch.registerName ||
.customerType === 'CORP' `${item.row.quotation.customerBranch?.firstName || '-'} ${item.row.quotation.customerBranch?.lastName || ''}`,
? $i18n.locale === 'tha'
? item.row.quotation.customerBranch.registerName
: item.row.quotation.customerBranch.registerNameEN
: $i18n.locale === 'tha'
? `${item.row.quotation.customerBranch?.firstName || '-'} ${item.row.quotation.customerBranch?.lastName || ''}`
: `${item.row.quotation.customerBranch?.firstNameEN || '-'} ${item.row.quotation.customerBranch?.lastNameEN || ''}`,
}, },
{ {
label: $t('taskOrder.issueDate'), label: $t('taskOrder.issueDate'),

View file

@ -83,16 +83,11 @@ defineEmits<{
<!-- NOTE: custom column will starts with # --> <!-- NOTE: custom column will starts with # -->
<template v-if="!col.name.startsWith('#')"> <template v-if="!col.name.startsWith('#')">
<span> <span>
<template v-if="typeof col.field === 'function'">
{{ {{
typeof col.field(props.row) === 'object' typeof col.field === 'string'
? col.field(props.row)[$i18n.locale] ? props.row[col.field as keyof Invoice]
: col.field(props.row) : col.field(props.row)
}} }}
</template>
<template v-else>
{{ props.row[col.field as keyof Invoice] }}
</template>
</span> </span>
</template> </template>
<template v-if="col.name === '#order'"> <template v-if="col.name === '#order'">

View file

@ -29,16 +29,7 @@ export const columns = [
name: 'customer', name: 'customer',
align: 'center', align: 'center',
label: 'general.customer', label: 'general.customer',
field: (v: Invoice) => field: (v: Invoice) => v.quotation.customerBranch.registerName,
v.quotation.customerBranch.customer.customerType === 'CORP'
? {
tha: v.quotation.customerBranch.registerName,
eng: v.quotation.customerBranch.registerNameEN,
}
: {
tha: `${v.quotation.customerBranch.firstName || ''} ${v.quotation.customerBranch.lastName || ''}`,
eng: `${v.quotation.customerBranch.firstNameEN || ''} ${v.quotation.customerBranch.lastNameEN || ''}`,
},
}, },
{ {

View file

@ -31,7 +31,6 @@ 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';
@ -41,8 +40,6 @@ 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();
@ -52,7 +49,6 @@ 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>();
@ -210,23 +206,22 @@ function getPrice(
) { ) {
return list.reduce( return list.reduce(
(a, c) => { (a, c) => {
const value = creditNote.getfinalPriceCredit(c.list || []); const pricePerUnit =
c.product.pricePerUnit - c.product.discount / c.product.amount;
const amount = c.list.length;
const priceNoVat = pricePerUnit;
const priceDiscountNoVat = priceNoVat * amount;
a.totalPrice = precisionRound(a.totalPrice + value.totalPrice); const rawVatTotal = priceDiscountNoVat * (config.value?.vat || 0.07);
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,
}, },
); );
@ -301,8 +296,6 @@ 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

@ -134,11 +134,6 @@ onMounted(async () => {
creditNote.getCreditNoteStats().then((res) => res && (stats.value = res)); creditNote.getCreditNoteStats().then((res) => res && (stats.value = res));
getList(); getList();
window.addEventListener('focus', () => {
creditNote.getCreditNoteStats().then((res) => res && (stats.value = res));
getList();
});
}); });
watch( watch(
@ -411,12 +406,10 @@ watch(
value: value:
item.row.quotation.customerBranch.customer item.row.quotation.customerBranch.customer
.customerType === 'CORP' .customerType === 'CORP'
? $i18n.locale === 'tha'
? item.row.quotation.customerBranch.registerName ? item.row.quotation.customerBranch.registerName
: item.row.quotation.customerBranch.registerNameEN
: $i18n.locale === 'tha' : $i18n.locale === 'tha'
? `${item.row.quotation.customerBranch.firstName || ''} ${item.row.quotation.customerBranch.lastName || ''}` ? `${item.row.quotation.customerBranch.firstName} ${item.row.quotation.customerBranch.lastName}`
: `${item.row.quotation.customerBranch.firstNameEN || ''} ${item.row.quotation.customerBranch.lastNameEN || ''}`, : `${item.row.quotation.customerBranch.firstNameEN} ${item.row.quotation.customerBranch.lastNameEN}`,
}, },
{ {
label: $t('requestList.quotationCode'), label: $t('requestList.quotationCode'),

View file

@ -8,7 +8,7 @@ import { columns } from './constants';
import KebabAction from 'src/components/shared/KebabAction.vue'; import KebabAction from 'src/components/shared/KebabAction.vue';
const creditNote = useCreditNote(); const creditNote = useCreditNote();
const { data, page, pageSize } = storeToRefs(creditNote); const { data, page } = storeToRefs(creditNote);
const prop = defineProps<{ const prop = defineProps<{
grid: boolean; grid: boolean;
@ -26,14 +26,7 @@ const visible = computed(() =>
<template> <template>
<q-table <q-table
:rows-per-page-options="[0]" :rows-per-page-options="[0]"
:rows=" :rows="data.map((item, i) => ({ ...item, _index: i, _page: page }))"
data.map((item, i) => ({
...item,
_index: i,
_page: page,
_pageSize: pageSize,
}))
"
:columns="visible" :columns="visible"
:grid :grid
hide-bottom hide-bottom
@ -59,7 +52,7 @@ const visible = computed(() =>
<template <template
v-slot:body="props: { v-slot:body="props: {
row: CreditNote & { _index: number; _page: number; _pageSize: number }; row: CreditNote & { _index: number; _page: number };
} & Omit<Parameters<QTableSlots['body']>[0], 'row'>" } & Omit<Parameters<QTableSlots['body']>[0], 'row'>"
> >
<q-tr :class="{ dark: $q.dark.isActive }" class="text-center"> <q-tr :class="{ dark: $q.dark.isActive }" class="text-center">

View file

@ -32,9 +32,8 @@ export const columns = [
name: 'order', name: 'order',
align: 'center', align: 'center',
label: 'general.order', label: 'general.order',
field: ( field: (data: CreditNote & { _index: number; _page: number }) =>
data: CreditNote & { _index: number; _page: number; _pageSize: number }, data._page * (data._index + 1),
) => (data._page - 1) * data._pageSize + (data._index + 1),
}, },
{ {
name: 'code', name: 'code',

View file

@ -237,12 +237,30 @@ onMounted(async () => {
}); });
function calcPricePerUnit(product: RequestWork['productService']['product']) { function calcPricePerUnit(product: RequestWork['productService']['product']) {
return agentPrice.value ? product.agentPrice : product.price; return product.vatIncluded
? (agentPrice.value ? product.agentPrice : product.price) /
(1 + (config.value?.vat || 0.07))
: agentPrice.value
? product.agentPrice
: product.price;
} }
function calcPrice(c: RequestWork[]): number { function calcPrice(
const price = creditNoteStore.getfinalPriceCredit(c); product: RequestWork['productService']['product'],
return price.finalPrice; amount: number,
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, () => {});
@ -309,15 +327,31 @@ 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: right"> <td style="text-align: center">
{{ formatNumberDecimal(calcPricePerUnit(v.product.product), 2) }} {{
formatNumberDecimal(
calcPricePerUnit(v.product.product) +
(v.product.product.calcVat
? calcPricePerUnit(v.product.product) *
(config?.vat || 0.07)
: 0),
2,
)
}}
</td> </td>
<td style="text-align: right"> <td style="text-align: center">
{{ formatNumberDecimal(calcPrice(v.list), 2) }} {{
formatNumberDecimal(
calcPrice(v.product.product, v.list.length, v.product.vat),
2,
)
}}
</td> </td>
</tr> </tr>
</tbody> </tbody>
@ -375,8 +409,8 @@ function closeAble() {
{{ {{
formatNumberDecimal( formatNumberDecimal(
summaryPrice.totalPrice - summaryPrice.totalPrice -
summaryPrice.totalDiscount - summaryPrice.totalDiscount +
summaryPrice.vatExcluded, summaryPrice.vat,
2, 2,
) )
}} }}

View file

@ -79,8 +79,8 @@ function titleMode(mode: View): string {
}) })
}} }}
</span> </span>
<span>{{ $t('branch.form.taxNo') }} {{ branch.taxNo }}</span> <span>เลขประจำตวผเสยภาษ {{ branch.taxNo }}</span>
<span>{{ $t('taskOrder.telephone') }} {{ branch.telephoneNo }}</span> <span>เบอรโทร {{ branch.telephoneNo }}</span>
<span>{{ branch.webUrl }}</span> <span>{{ branch.webUrl }}</span>
</article> </article>
<article> <article>
@ -105,18 +105,8 @@ function titleMode(mode: View): string {
}) })
}} }}
</span> </span>
<span> <span>เลขประจำตวผเสยภาษ {{ customer.citizenId }}</span>
{{ <span>เบอรโทร {{ customer.telephoneNo }}</span>
customer.customer.customerType === 'CORP'
? `${$t('customer.form.legalPersonNo')} `
: `${$t('customer.form.taxpayyerNo')} `
}}{{
customer.customer.customerType === 'CORP'
? customer.codeCustomer
: customer.citizenId
}}
</span>
<span>{{ $t('taskOrder.telephone') }} {{ customer.telephoneNo }}</span>
</article> </article>
</section> </section>
<section class="detail-quotation-info"> <section class="detail-quotation-info">

View file

@ -12,13 +12,10 @@ 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;
@ -47,22 +44,15 @@ function calcPricePerUnit(product: RequestWork['productService']) {
return product.pricePerUnit - product.discount / product.amount; return product.pricePerUnit - product.discount / product.amount;
} }
function calcVat(c: RequestWork['productService']) { function calcPrice(c: RequestWork['productService'], amount: number) {
const vatFactor = c.product.serviceChargeCalcVat const pricePerUnit = c.pricePerUnit - c.discount / c.amount;
? (config.value?.vat ?? 0.07) const priceNoVat = pricePerUnit;
: 0; const priceDiscountNoVat = priceNoVat * amount;
const price = precisionRound( const rawVatTotal =
c.pricePerUnit * (1 + vatFactor) - c.discount / (1 + vatFactor), c.vat === 0 ? 0 : priceDiscountNoVat * (config.value?.vat || 0.07);
);
const vat = (price / (1 + vatFactor)) * vatFactor; return precisionRound(priceNoVat * amount + rawVatTotal);
return vat;
}
function calcPrice(c: RequestWork[]) {
const price = creditNote.getfinalPriceCredit(c);
return price.finalPrice;
} }
</script> </script>
<template> <template>
@ -172,7 +162,12 @@ function calcPrice(c: RequestWork[]) {
<!-- 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

@ -209,7 +209,47 @@ const selectedWorker = ref<
}[]; }[];
})[] })[]
>([]); >([]);
const selectedWorkerItem = ref([]); const selectedWorkerItem = computed(() => {
return [
...selectedWorker.value.map((e) => ({
foreignRefNo: e.employeePassport
? e.employeePassport[0]?.number || '-'
: '-',
employeeName:
locale.value === Lang.English
? `${e.firstNameEN} ${e.lastNameEN}`
: `${e.firstName || e.firstNameEN} ${e.lastName || e.lastNameEN}`,
birthDate: dateFormatJS({ date: e.dateOfBirth }),
gender: e.gender,
age: calculateAge(e.dateOfBirth),
nationality: optionStore.mapOption(e.nationality),
documentExpireDate:
e.employeePassport !== undefined &&
e.employeePassport[0]?.expireDate !== undefined
? dateFormatJS({ date: e.employeePassport[0]?.expireDate })
: '-',
imgUrl: e.selectedImage
? `${API_BASE_URL}/employee/${e.id}/image/${e.selectedImage}`
: '',
status: e.status,
})),
...newWorkerList.value.map((v: any) => ({
foreignRefNo: v.passportNo || '-',
employeeName:
locale.value === Lang.English
? `${v.firstNameEN} ${v.lastNameEN}`
: `${v.firstName || v.firstNameEN} ${v.lastName || v.lastNameEN}`,
birthDate: dateFormatJS({ date: v.dateOfBirth }),
gender: v.gender,
age: calculateAge(v.dateOfBirth),
nationality: optionStore.mapOption(v.nationality),
documentExpireDate: '-',
imgUrl: '',
status: 'CREATED',
})),
];
});
const selectedInstallmentNo = ref<number[]>([]); const selectedInstallmentNo = ref<number[]>([]);
const installmentAmount = ref<number>(0); const installmentAmount = ref<number>(0);
@ -334,39 +374,7 @@ function assignProductServiceList() {
function assignSelectedWorker() { function assignSelectedWorker() {
if (debitNoteData.value) if (debitNoteData.value)
selectedWorkerItem.value = debitNoteData.value.worker.map((e) => { selectedWorker.value = debitNoteData.value.worker.map((v) => v.employee);
return {
id: e.employee.id,
foreignRefNo: e.employee.employeePassport
? e.employee.employeePassport[0]?.number || '-'
: '-',
employeeName:
locale.value === Lang.English
? `${e.employee.firstNameEN} ${e.employee.lastNameEN}`
: `${e.employee.firstName || e.employee.firstNameEN} ${e.employee.lastName || e.employee.lastNameEN}`,
birthDate: dateFormatJS({ date: e.employee.dateOfBirth }),
gender: e.employee.gender,
age: calculateAge(e.employee.dateOfBirth),
nationality: optionStore.mapOption(e.employee.nationality),
documentExpireDate:
e.employee.employeePassport !== undefined &&
e.employee.employeePassport[0]?.expireDate !== undefined
? dateFormatJS({ date: e.employee.employeePassport[0]?.expireDate })
: '-',
imgUrl: e.employee.selectedImage
? `${API_BASE_URL}/employee/${e.id}/image/${e.employee.selectedImage}`
: '',
status: e.employee.status,
workerNew: false,
lastNameEN: e.employee.lastNameEN,
lastName: e.employee.lastName,
middleNameEN: e.employee.middleNameEN,
middleName: e.employee.middleName,
firstNameEN: e.employee.firstNameEN,
firstName: e.employee.firstName,
namePrefix: e.employee.namePrefix,
};
});
} }
async function assignFormData(id: string) { async function assignFormData(id: string) {
@ -378,7 +386,7 @@ async function assignFormData(id: string) {
selectedProductGroup.value = selectedProductGroup.value =
data.productServiceList[0]?.product.productGroup?.id || ''; data.productServiceList[0]?.product.productGroup?.id || '';
(previousValue = { ((previousValue = {
id: data.id || undefined, id: data.id || undefined,
debitNoteQuotationId: data.debitNoteQuotationId || undefined, debitNoteQuotationId: data.debitNoteQuotationId || undefined,
productServiceList: structuredClone( productServiceList: structuredClone(
@ -404,7 +412,7 @@ async function assignFormData(id: string) {
quotationId: data.debitNoteQuotationId, quotationId: data.debitNoteQuotationId,
remark: data.remark || undefined, remark: data.remark || undefined,
}), }),
(currentFormData.value = structuredClone(previousValue)); (currentFormData.value = structuredClone(previousValue)));
assignProductServiceList(); assignProductServiceList();
assignSelectedWorker(); assignSelectedWorker();
@ -426,7 +434,6 @@ async function getQuotation(id?: string) {
const data = await quotationStore.getQuotation(quotationId); const data = await quotationStore.getQuotation(quotationId);
if (!!data) { if (!!data) {
quotationData.value = data; quotationData.value = data;
agentPrice.value = quotationData.value.agentPrice;
} }
} }
} }
@ -542,28 +549,25 @@ function getPrice(
return a; return a;
} }
const calcVat = const price = precisionRound(c.pricePerUnit * c.amount);
c.product[agentPrice.value ? 'agentPriceCalcVat' : 'calcVat']; const vat =
precisionRound(
(c.pricePerUnit * (c.discount ? c.amount : 1) - c.discount) *
(config.value?.vat || 0.07),
) * (!c.discount ? c.amount : 1);
const vatFactor = calcVat ? (config.value?.vat ?? 0.07) : 0; a.totalPrice = precisionRound(a.totalPrice + price);
a.totalDiscount = precisionRound(a.totalDiscount + Number(c.discount));
const pricePerUnit = precisionRound( a.vat = c.product.calcVat ? precisionRound(a.vat + vat) : a.vat;
(c.pricePerUnit * (1 + vatFactor)) / (1 + vatFactor),
);
const price = precisionRound(
(pricePerUnit * c.amount * (1 + vatFactor) - c.discount) /
(1 + vatFactor),
);
const vat = price * vatFactor;
a.totalPrice = precisionRound(a.totalPrice + price + c.discount);
a.totalDiscount = precisionRound(a.totalDiscount + c.discount);
a.vat = precisionRound(a.vat + vat);
a.vatExcluded = c.product.calcVat a.vatExcluded = c.product.calcVat
? a.vatExcluded ? a.vatExcluded
: precisionRound(a.vatExcluded + price); : precisionRound(a.vat + vat);
a.finalPrice = precisionRound(a.totalPrice - a.totalDiscount + a.vat); a.finalPrice = precisionRound(
a.totalPrice -
a.totalDiscount +
a.vat -
Number(currentFormData.value.discount || 0),
);
return a; return a;
}, },
@ -571,7 +575,6 @@ function getPrice(
totalPrice: 0, totalPrice: 0,
totalDiscount: 0, totalDiscount: 0,
vat: 0, vat: 0,
vatIncluded: 0,
vatExcluded: 0, vatExcluded: 0,
finalPrice: 0, finalPrice: 0,
}, },
@ -805,11 +808,15 @@ async function submit() {
worker: JSON.parse( worker: JSON.parse(
JSON.stringify([ JSON.stringify([
...selectedWorkerItem.value.map((v) => { ...selectedWorker.value.map((v) => {
{ {
return v.id; return v.id;
} }
}), }),
...newWorkerList.value.map((v) => {
const { attachment, ...payload } = v;
return pageState.mode === 'edit' ? payload.id : payload;
}),
]), ]),
), ),
dueDate: currentFormData.value.dueDate, dueDate: currentFormData.value.dueDate,
@ -862,7 +869,6 @@ async function exampleReceipt(id: string) {
function storeDataLocal() { function storeDataLocal() {
// quotationFormData.value.productServiceList = productServiceList.value; // quotationFormData.value.productServiceList = productServiceList.value;
//
localStorage.setItem( localStorage.setItem(
'debit-note-preview', 'debit-note-preview',
@ -871,7 +877,6 @@ function storeDataLocal() {
...currentFormData.value, ...currentFormData.value,
customerBranchId: quotationData.value?.customerBranchId, customerBranchId: quotationData.value?.customerBranchId,
registeredBranchId: quotationData.value?.registeredBranchId, registeredBranchId: quotationData.value?.registeredBranchId,
agentPrice: quotationData.value.agentPrice,
}, },
meta: { meta: {
source: { source: {
@ -888,7 +893,7 @@ function storeDataLocal() {
dueDate: currentFormData.value.dueDate, dueDate: currentFormData.value.dueDate,
}, },
productServicelist: productService.value, productServicelist: productService.value,
selectedWorker: selectedWorkerItem.value, selectedWorker: selectedWorker.value,
createdBy: getName(), createdBy: getName(),
}, },
}), }),
@ -913,69 +918,6 @@ function closeAble() {
return window.opener !== null; return window.opener !== null;
} }
function combineWorker(newWorker: any, oldWorker: any) {
selectedWorkerItem.value = [
...oldWorker.map((e) => ({
id: e.id,
foreignRefNo: e.employeePassport
? e.employeePassport[0]?.number || '-'
: '-',
employeeName:
locale.value === Lang.English
? `${e.firstNameEN} ${e.lastNameEN}`
: `${e.firstName || e.firstNameEN} ${e.lastName || e.lastNameEN}`,
birthDate: dateFormatJS({ date: e.dateOfBirth }),
gender: e.gender,
age: calculateAge(e.dateOfBirth),
nationality: optionStore.mapOption(e.nationality),
documentExpireDate:
e.employeePassport !== undefined &&
e.employeePassport[0]?.expireDate !== undefined
? dateFormatJS({ date: e.employeePassport[0]?.expireDate })
: '-',
imgUrl: e.selectedImage
? `${API_BASE_URL}/employee/${e.id}/image/${e.selectedImage}`
: '',
status: e.status,
workerNew: false,
lastNameEN: e.lastNameEN,
lastName: e.lastName,
middleNameEN: e.middleNameEN,
middleName: e.middleName,
firstNameEN: e.firstNameEN,
firstName: e.firstName,
namePrefix: e.namePrefix,
})),
...newWorker.map((v: any) => ({
id: v.id,
foreignRefNo: v.passportNo || '-',
employeeName:
locale.value === Lang.English
? `${v.firstNameEN} ${v.lastNameEN}`
: `${v.firstName || v.firstNameEN} ${v.lastName || v.lastNameEN}`,
birthDate: dateFormatJS({ date: v.dateOfBirth }),
gender: v.gender,
age: calculateAge(v.dateOfBirth),
nationality: optionStore.mapOption(v.nationality),
documentExpireDate: '-',
imgUrl: '',
status: 'CREATED',
lastNameEN: v.lastNameEN,
lastName: v.lastName,
middleNameEN: v.middleNameEN,
middleName: v.middleName,
firstNameEN: v.firstNameEN,
firstName: v.firstName,
namePrefix: v.namePrefix,
dateOfBirth: v.dateOfBirth,
workerNew: true,
})),
];
}
watch( watch(
() => pageState.mode, () => pageState.mode,
() => toggleMode(pageState.mode), () => toggleMode(pageState.mode),
@ -1104,11 +1046,7 @@ async function submitAccepted() {
:status-active="i.active?.()" :status-active="i.active?.()"
:status-done="i.status === 'done'" :status-done="i.status === 'done'"
:status-waiting="i.status === 'waiting'" :status-waiting="i.status === 'waiting'"
@click=" @click="i.handler()"
() => {
if (pageState.mode !== 'create') i.handler();
}
"
/> />
</nav> </nav>
<!-- #TODO add goToQuotation as @goto-quotation--> <!-- #TODO add goToQuotation as @goto-quotation-->
@ -1139,7 +1077,6 @@ async function submitAccepted() {
<PaymentForm <PaymentForm
v-if="view === QuotationStatus.PaymentPending" v-if="view === QuotationStatus.PaymentPending"
is-debit-note is-debit-note
:branch-id="quotationData?.registeredBranchId"
:readonly="isRoleInclude(['sale', 'head_of_sale'])" :readonly="isRoleInclude(['sale', 'head_of_sale'])"
:data="debitNoteData" :data="debitNoteData"
@fetch-status=" @fetch-status="
@ -1161,7 +1098,7 @@ async function submitAccepted() {
" "
:row-worker="selectedWorkerItem" :row-worker="selectedWorkerItem"
@add-worker="() => (pageState.employeeModal = true)" @add-worker="() => (pageState.employeeModal = true)"
@delete="(i) => deleteItem(selectedWorkerItem, i)" @delete="(i) => deleteItem(selectedWorker, i)"
/> />
<!-- #TODO add openProductDialog at @add-product--> <!-- #TODO add openProductDialog at @add-product-->
@ -1381,7 +1318,7 @@ async function submitAccepted() {
<SaveButton <SaveButton
v-if="pageState.mode !== 'info'" v-if="pageState.mode !== 'info'"
:disabled=" :disabled="
selectedWorkerItem.length === 0 || productService.length === 0 selectedWorkerItem.length === 0 && productService.length === 0
" "
@click="submit" @click="submit"
solid solid
@ -1426,12 +1363,13 @@ async function submitAccepted() {
<!-- add employee quotation --> <!-- add employee quotation -->
<QuotationFormWorkerSelect <QuotationFormWorkerSelect
:preselect-worker="selectedWorkerItem" :preselect-worker="selectedWorker"
:customerBranchId="quotationData?.customerBranchId" :customerBranchId="quotationData?.customerBranchId"
v-model:open="pageState.employeeModal" v-model:open="pageState.employeeModal"
v-model:new-worker-list="newWorkerList"
@success=" @success="
(v) => { (v) => {
combineWorker(v.newWorker, v.worker); selectedWorker = v.worker;
} }
" "
/> />

View file

@ -143,15 +143,6 @@ onMounted(async () => {
}); });
getList(); getList();
window.addEventListener('focus', () => {
debitNote.getDebitNoteStats().then((res) => {
if (res) {
stats.value = res;
}
});
getList();
});
}); });
watch( watch(
@ -420,7 +411,7 @@ watch(
@delete="() => triggerDelete(item.row.id)" @delete="() => triggerDelete(item.row.id)"
:title="item.row.debitNoteQuotation?.workName" :title="item.row.debitNoteQuotation?.workName"
:code="item.row.code" :code="item.row.code"
:status="$t(`debitNote.stats.${item.row.quotationStatus}`)" :status="$t(`quotation.status.${item.row.quotationStatus}`)"
:badge-color="hslaColors[item.row.quotationStatus] || ''" :badge-color="hslaColors[item.row.quotationStatus] || ''"
:custom-data="[ :custom-data="[
{ {
@ -434,12 +425,10 @@ watch(
label: $t('quotation.customer'), label: $t('quotation.customer'),
value: value:
item.row.customerBranch.customer.customerType === 'CORP' item.row.customerBranch.customer.customerType === 'CORP'
? $i18n.locale === 'tha'
? item.row.customerBranch.registerName ? item.row.customerBranch.registerName
: item.row.customerBranch.registerNameEN
: $i18n.locale === 'tha' : $i18n.locale === 'tha'
? `${item.row.customerBranch.firstName || ''} ${item.row.customerBranch.lastName || ''}` ? `${item.row.customerBranch.firstName} ${item.row.customerBranch.lastName}`
: `${item.row.customerBranch.firstNameEN || ''} ${item.row.customerBranch.lastNameEN || ''}`, : `${item.row.customerBranch.firstNameEN} ${item.row.customerBranch.lastNameEN}`,
}, },
{ {
label: $t('requestList.quotationCode'), label: $t('requestList.quotationCode'),

View file

@ -82,8 +82,6 @@ const data = ref<
>(); >();
const productServiceList = ref<ProductServiceList[]>([]); const productServiceList = ref<ProductServiceList[]>([]);
const selectedInstallmentNo = ref<number[]>([]);
const agentPrice = ref<boolean>(false);
const summaryPrice = ref<SummaryPrice>({ const summaryPrice = ref<SummaryPrice>({
totalPrice: 0, totalPrice: 0,
@ -219,8 +217,6 @@ onMounted(async () => {
} }
productServiceList.value = parsed.meta.productServicelist; productServiceList.value = parsed.meta.productServicelist;
selectedInstallmentNo.value = parsed.meta.selectedInstallmentNo;
agentPrice.value = parsed.meta.agentPrice;
productList.value = productList.value =
productServiceList.value?.map((v) => ({ productServiceList.value?.map((v) => ({
@ -228,56 +224,40 @@ 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: priceUnit: v.pricePerUnit || 0,
v.pricePerUnit +
(v.product[agentPrice.value ? 'agentPriceCalcVat' : 'calcVat']
? v.pricePerUnit * (config.value?.vat || 0.07)
: 0),
discount: v.discount || 0, discount: v.discount || 0,
vat: v.vat || 0, vat: v.vat || 0,
value: precisionRound( value: precisionRound(
(v.pricePerUnit + (v.pricePerUnit || 0) * v.amount -
(v.product[agentPrice.value ? 'agentPriceCalcVat' : 'calcVat'] (v.discount || 0) +
? v.pricePerUnit * (config.value?.vat || 0.07) (v.product.calcVat
: 0)) * ? ((v.pricePerUnit || 0) * v.amount - (v.discount || 0)) *
v.amount - (config.value?.vat || 0.07)
v.discount, : 0),
), ),
})) || []; })) || [];
} }
summaryPrice.value = (productServiceList.value || []).reduce( summaryPrice.value = (productServiceList.value || []).reduce(
(a, c) => { (a, c) => {
if ( const price = precisionRound((c.pricePerUnit || 0) * c.amount);
selectedInstallmentNo.value?.length > 0 && const vat = precisionRound(
c.installmentNo && ((c.pricePerUnit || 0) * c.amount - (c.discount || 0)) *
!selectedInstallmentNo.value?.includes(c.installmentNo) (config.value?.vat || 0.07),
) {
return a;
}
const calcVat =
c.product[agentPrice.value ? 'agentPriceCalcVat' : 'calcVat'];
const vatFactor = calcVat ? (config.value?.vat ?? 0.07) : 0;
const pricePerUnit = precisionRound(
(c.pricePerUnit * (1 + vatFactor)) / (1 + vatFactor),
); );
const price = precisionRound( a.totalPrice = precisionRound(a.totalPrice + price);
(pricePerUnit * c.amount * (1 + vatFactor) - c.discount) / a.totalDiscount = precisionRound(a.totalDiscount + Number(c.discount));
(1 + vatFactor), a.vat = c.product.calcVat ? precisionRound(a.vat + vat) : a.vat;
);
const vat = price * vatFactor;
a.totalPrice = precisionRound(a.totalPrice + price + c.discount);
a.totalDiscount = precisionRound(a.totalDiscount + c.discount);
a.vat = precisionRound(a.vat + vat);
a.vatExcluded = c.product.calcVat a.vatExcluded = c.product.calcVat
? a.vatExcluded ? a.vatExcluded
: precisionRound(a.vatExcluded + price); : precisionRound(a.vat + vat);
a.finalPrice = precisionRound(a.totalPrice - a.totalDiscount + a.vat); a.finalPrice = precisionRound(
a.totalPrice -
a.totalDiscount +
a.vat -
Number(data.value?.discount || 0),
);
return a; return a;
}, },
@ -369,9 +349,7 @@ function print() {
{{ formatNumberDecimal(v.priceUnit, 2) }} {{ formatNumberDecimal(v.priceUnit, 2) }}
</td> </td>
<td style="text-align: right"> <td style="text-align: right">
<template v-if="v.discount !== 0"> {{ formatNumberDecimal(v.discount, 2) }}
{{ formatNumberDecimal(v.discount, 2) }} ฿
</template>
</td> </td>
<td style="text-align: right"> <td style="text-align: right">
{{ formatNumberDecimal(v.vat, 2) }} {{ formatNumberDecimal(v.vat, 2) }}
@ -435,8 +413,8 @@ function print() {
{{ {{
formatNumberDecimal( formatNumberDecimal(
summaryPrice.totalPrice - summaryPrice.totalPrice -
summaryPrice.totalDiscount - summaryPrice.totalDiscount +
summaryPrice.vatExcluded, summaryPrice.vat,
2, 2,
) )
}} }}

View file

@ -79,8 +79,8 @@ function titleMode(mode: View): string {
}) })
}} }}
</span> </span>
<span>{{ $t('branch.form.taxNo') }} {{ branch.taxNo }}</span> <span>เลขประจำตวผเสยภาษ {{ branch.taxNo }}</span>
<span>{{ $t('taskOrder.telephone') }} {{ branch.telephoneNo }}</span> <span>เบอรโทร {{ branch.telephoneNo }}</span>
<span>{{ branch.webUrl }}</span> <span>{{ branch.webUrl }}</span>
</article> </article>
<article> <article>
@ -105,18 +105,8 @@ function titleMode(mode: View): string {
}) })
}} }}
</span> </span>
<span> <span>เลขประจำตวผเสยภาษ {{ customer.citizenId }}</span>
{{ <span>เบอรโทร {{ customer.telephoneNo }}</span>
customer.customer.customerType === 'CORP'
? `${$t('customer.form.legalPersonNo')} `
: `${$t('customer.form.taxpayyerNo')} `
}}{{
customer.customer.customerType === 'CORP'
? customer.codeCustomer
: customer.citizenId
}}
</span>
<span>{{ $t('taskOrder.telephone') }} {{ customer.telephoneNo }}</span>
</article> </article>
</section> </section>
<section class="detail-quotation-info"> <section class="detail-quotation-info">

View file

@ -343,16 +343,9 @@ watch(
{ {
label: $t('general.customer'), label: $t('general.customer'),
value: value:
item.row.invoice.quotation.customerBranch.customer item.row.invoice.quotation.customerBranch
.customerType === 'CORP' .registerName ||
? $i18n.locale === 'tha' `${item.row.invoice.quotation.customerBranch?.firstName || '-'} ${item.row.invoice.quotation.customerBranch?.lastName || ''}`,
? item.row.invoice.quotation.customerBranch
.registerName
: item.row.invoice.quotation.customerBranch
.registerNameEN
: $i18n.locale === 'tha'
? `${item.row.invoice.quotation.customerBranch?.firstName || '-'} ${item.row.invoice.quotation.customerBranch?.lastName || ''}`
: `${item.row.invoice.quotation.customerBranch?.firstNameEN || '-'} ${item.row.invoice.quotation.customerBranch?.lastNameEN || ''}`,
}, },
{ {
label: $t('taskOrder.issueDate'), label: $t('taskOrder.issueDate'),

View file

@ -45,7 +45,7 @@ const fieldSelected = ref<('no' | 'name' | 'nameEN')[]>([
const fieldSelectedOption = ref<{ label: string; value: string }[]>([ const fieldSelectedOption = ref<{ label: string; value: string }[]>([
{ {
label: 'general.order', label: 'general.order',
value: 'no', value: 'orderNumber',
}, },
{ {
@ -312,6 +312,25 @@ watch(
</q-input> </q-input>
<div class="row col-md-5 justify-end" style="white-space: nowrap"> <div class="row col-md-5 justify-end" style="white-space: nowrap">
<q-select
v-if="$q.screen.gt.sm"
v-model="statusFilter"
outlined
dense
option-value="value"
option-label="label"
class="col"
:class="{ 'offset-md-5': pageState.gridView }"
map-options
emit-value
:for="'field-select-status'"
:hide-dropdown-icon="$q.screen.lt.sm"
:options="[
{ label: $t('general.all'), value: 'all' },
{ label: $t('general.active'), value: 'statusACTIVE' },
{ label: $t('general.inactive'), value: 'statusINACTIVE' },
]"
/>
<q-select <q-select
v-if="!pageState.gridView" v-if="!pageState.gridView"
id="select-field" id="select-field"
@ -576,18 +595,10 @@ watch(
></q-badge> ></q-badge>
</q-avatar> </q-avatar>
<span class="text-weight-bold column q-pl-md"> <span class="text-weight-bold column q-pl-md">
{{ {{ props.row.name }}
$i18n.locale === 'tha'
? props.row.name
: props.row.nameEN
}}
<span class="text-caption app-text-muted-2"> <span class="text-caption app-text-muted-2">
{{ {{ props.row.nameEN }}
$i18n.locale === 'tha'
? props.row.nameEN
: props.row.name
}}
</span> </span>
</span> </span>
<nav <nav

Some files were not shown because too many files have changed in this diff Show more