Merge branch 'feat/handle-role' into develop
Some checks failed
Spell Check / Spell Check with Typos (push) Failing after 6s
Some checks failed
Spell Check / Spell Check with Typos (push) Failing after 6s
This commit is contained in:
commit
f08c83c98b
44 changed files with 430 additions and 237 deletions
|
|
@ -89,15 +89,7 @@ defineProps<{
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<q-separator />
|
||||||
style="
|
|
||||||
display: block;
|
|
||||||
width: 100%;
|
|
||||||
height: 1px;
|
|
||||||
background: hsla(0 0% 0% / 0.1);
|
|
||||||
margin-bottom: var(--size-2);
|
|
||||||
"
|
|
||||||
/>
|
|
||||||
<slot name="data"></slot>
|
<slot name="data"></slot>
|
||||||
<template v-if="!$slots.data">
|
<template v-if="!$slots.data">
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ const prop = withDefaults(
|
||||||
inTable?: boolean;
|
inTable?: boolean;
|
||||||
addButton?: boolean;
|
addButton?: boolean;
|
||||||
prefixId?: string;
|
prefixId?: string;
|
||||||
|
hideAction?: boolean;
|
||||||
|
hideDelete?: boolean;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
gridView: false,
|
gridView: false,
|
||||||
|
|
@ -265,9 +267,10 @@ defineEmits<{
|
||||||
@click.stop="$emit('view', props.row)"
|
@click.stop="$emit('view', props.row)"
|
||||||
/>
|
/>
|
||||||
<KebabAction
|
<KebabAction
|
||||||
v-if="!inTable"
|
v-if="!inTable && !hideAction"
|
||||||
:id-name="props.row.firstName"
|
:id-name="props.row.firstName"
|
||||||
:status="props.row.status"
|
:status="props.row.status"
|
||||||
|
:hide-delete="hideDelete"
|
||||||
@view="$emit('view', props.row)"
|
@view="$emit('view', props.row)"
|
||||||
@edit="$emit('edit', props.row)"
|
@edit="$emit('edit', props.row)"
|
||||||
@delete="$emit('delete', props.row)"
|
@delete="$emit('delete', props.row)"
|
||||||
|
|
@ -280,9 +283,11 @@ defineEmits<{
|
||||||
<template v-slot:item="props">
|
<template v-slot:item="props">
|
||||||
<div class="col-12 col-md-3 col-sm-6">
|
<div class="col-12 col-md-3 col-sm-6">
|
||||||
<PersonCard
|
<PersonCard
|
||||||
|
history
|
||||||
|
:hide-delete="hideDelete"
|
||||||
|
:hide-action="hideAction"
|
||||||
:id="`card-${props.row.firstNameEN}`"
|
:id="`card-${props.row.firstNameEN}`"
|
||||||
:field-selected="fieldSelected"
|
:field-selected="fieldSelected"
|
||||||
history
|
|
||||||
:prefix-id="props.row.firstNameEN ?? props.rowIndex"
|
:prefix-id="props.row.firstNameEN ?? props.rowIndex"
|
||||||
:data="{
|
:data="{
|
||||||
code: props.row.code,
|
code: props.row.code,
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ import { QField } from 'quasar';
|
||||||
defineProps<{
|
defineProps<{
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
onDrawer?: boolean;
|
onDrawer?: boolean;
|
||||||
|
hideAction?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
@ -201,6 +202,7 @@ onMounted(async () => {
|
||||||
:class="{ 'q-ml-lg': $q.screen.gt.xs, 'q-mt-sm': $q.screen.lt.sm }"
|
:class="{ 'q-ml-lg': $q.screen.gt.xs, 'q-mt-sm': $q.screen.lt.sm }"
|
||||||
>
|
>
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
|
:disable="hideAction"
|
||||||
class="q-mr-sm"
|
class="q-mr-sm"
|
||||||
two-way
|
two-way
|
||||||
:model-value="flowData.status !== 'INACTIVE'"
|
:model-value="flowData.status !== 'INACTIVE'"
|
||||||
|
|
|
||||||
|
|
@ -48,6 +48,7 @@ defineProps<{
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
onDrawer?: boolean;
|
onDrawer?: boolean;
|
||||||
inputOnly?: boolean;
|
inputOnly?: boolean;
|
||||||
|
disableToggle?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
defineEmits<{
|
defineEmits<{
|
||||||
|
|
@ -76,6 +77,7 @@ defineEmits<{
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
class="q-mr-sm"
|
class="q-mr-sm"
|
||||||
two-way
|
two-way
|
||||||
|
:disable="disableToggle"
|
||||||
:model-value="status !== 'INACTIVE'"
|
:model-value="status !== 'INACTIVE'"
|
||||||
@click="
|
@click="
|
||||||
() => {
|
() => {
|
||||||
|
|
@ -195,8 +197,8 @@ defineEmits<{
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(
|
:deep(
|
||||||
.q-item__section.column.q-item__section--side.justify-center.q-item__section--avatar.q-focusable.relative-position.cursor-pointer
|
.q-item__section.column.q-item__section--side.justify-center.q-item__section--avatar.q-focusable.relative-position.cursor-pointer
|
||||||
) {
|
) {
|
||||||
justify-content: start !important;
|
justify-content: start !important;
|
||||||
padding-right: 8px !important;
|
padding-right: 8px !important;
|
||||||
padding-top: 16px;
|
padding-top: 16px;
|
||||||
|
|
@ -208,15 +210,15 @@ defineEmits<{
|
||||||
}
|
}
|
||||||
|
|
||||||
: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.expansion-rounded.surface-2
|
.q-item.q-item-type.row.no-wrap.q-item--dense.q-item--clickable.q-link.cursor-pointer.q-focusable.q-hoverable.expansion-rounded.surface-2
|
||||||
.q-focus-helper
|
.q-focus-helper
|
||||||
) {
|
) {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ const props = withDefaults(
|
||||||
page?: number;
|
page?: number;
|
||||||
pageSize?: number;
|
pageSize?: number;
|
||||||
hideBtnPreview?: boolean;
|
hideBtnPreview?: boolean;
|
||||||
|
hideAction?: boolean;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
row: () => [],
|
row: () => [],
|
||||||
|
|
@ -149,6 +150,7 @@ defineEmits<{
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<KebabAction
|
<KebabAction
|
||||||
|
v-if="!hideAction"
|
||||||
:idName="`btn-kebab-${props.row.workName}`"
|
:idName="`btn-kebab-${props.row.workName}`"
|
||||||
status="'ACTIVE'"
|
status="'ACTIVE'"
|
||||||
hide-toggle
|
hide-toggle
|
||||||
|
|
|
||||||
|
|
@ -229,6 +229,7 @@ const smallBanner = ref(false);
|
||||||
|
|
||||||
<ToggleButton
|
<ToggleButton
|
||||||
v-if="useToggle"
|
v-if="useToggle"
|
||||||
|
:disable="readonly"
|
||||||
two-way
|
two-way
|
||||||
:model-value="toggleStatus !== 'INACTIVE'"
|
:model-value="toggleStatus !== 'INACTIVE'"
|
||||||
@click="$emit('update:toggleStatus', toggleStatus)"
|
@click="$emit('update:toggleStatus', toggleStatus)"
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { BranchWithChildren } from 'stores/branch/types';
|
import { BranchWithChildren } from 'stores/branch/types';
|
||||||
import KebabAction from './shared/KebabAction.vue';
|
import KebabAction from './shared/KebabAction.vue';
|
||||||
import { isRoleInclude } from 'stores/utils';
|
|
||||||
|
|
||||||
const nodes = defineModel<(any | BranchWithChildren)[]>('nodes', {
|
const nodes = defineModel<(any | BranchWithChildren)[]>('nodes', {
|
||||||
default: [],
|
default: [],
|
||||||
|
|
@ -120,11 +119,7 @@ defineEmits<{
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<q-btn
|
<q-btn
|
||||||
v-if="
|
v-if="node.isHeadOffice && typeTree === 'branch'"
|
||||||
node.isHeadOffice &&
|
|
||||||
typeTree === 'branch' &&
|
|
||||||
isRoleInclude(['head_of_admin', 'admin', 'system'])
|
|
||||||
"
|
|
||||||
:id="`create-sub-branch-btn-${node.name}`"
|
:id="`create-sub-branch-btn-${node.name}`"
|
||||||
@click.stop="$emit('create', node)"
|
@click.stop="$emit('create', node)"
|
||||||
icon="mdi-file-plus-outline"
|
icon="mdi-file-plus-outline"
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,8 @@ defineProps<{
|
||||||
history?: boolean;
|
history?: boolean;
|
||||||
prefixId?: string;
|
prefixId?: string;
|
||||||
separateEnter?: boolean;
|
separateEnter?: boolean;
|
||||||
|
hideAction?: boolean;
|
||||||
|
hideDelete?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
defineEmits<{
|
defineEmits<{
|
||||||
|
|
@ -76,8 +78,10 @@ defineEmits<{
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<KebabAction
|
<KebabAction
|
||||||
|
v-if="!hideAction"
|
||||||
:id-name="prefixId"
|
:id-name="prefixId"
|
||||||
:status="disabled ? 'INACTIVE' : 'ACTIVE'"
|
:status="disabled ? 'INACTIVE' : 'ACTIVE'"
|
||||||
|
:hide-delete="hideDelete"
|
||||||
@view="
|
@view="
|
||||||
separateEnter
|
separateEnter
|
||||||
? $emit('viewCard', 'INFO')
|
? $emit('viewCard', 'INFO')
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import useMyBranch from 'stores/my-branch';
|
||||||
import { getUserId, getRole } from 'src/services/keycloak';
|
import { getUserId, getRole } from 'src/services/keycloak';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { isRoleInclude } from 'src/stores/utils';
|
import { canAccess } from 'src/stores/utils';
|
||||||
|
|
||||||
type Menu = {
|
type Menu = {
|
||||||
label: string;
|
label: string;
|
||||||
|
|
@ -71,82 +71,41 @@ function initMenu() {
|
||||||
{
|
{
|
||||||
label: 'branch',
|
label: 'branch',
|
||||||
route: '/branch-management',
|
route: '/branch-management',
|
||||||
hidden: !isRoleInclude([
|
hidden: !canAccess('branch'),
|
||||||
'system',
|
|
||||||
'head_of_admin',
|
|
||||||
'admin',
|
|
||||||
'branch_manager',
|
|
||||||
'head_of_accountant',
|
|
||||||
]),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'personnel',
|
label: 'personnel',
|
||||||
route: '/personnel-management',
|
route: '/personnel-management',
|
||||||
hidden: !isRoleInclude([
|
hidden: !canAccess('personnel'),
|
||||||
'owner',
|
|
||||||
'system',
|
|
||||||
'head_of_admin',
|
|
||||||
'admin',
|
|
||||||
'branch_manager',
|
|
||||||
]),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'workflow',
|
label: 'workflow',
|
||||||
route: '/workflow',
|
route: '/workflow',
|
||||||
hidden: !isRoleInclude(['system', 'head_of_admin', 'admin']),
|
hidden: !canAccess('workflow'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'property',
|
label: 'property',
|
||||||
route: '/property',
|
route: '/property',
|
||||||
hidden: !isRoleInclude(['system', 'head_of_admin', 'admin']),
|
hidden: !canAccess('workflow'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'productService',
|
label: 'productService',
|
||||||
route: '/product-service',
|
route: '/product-service',
|
||||||
hidden: !isRoleInclude([
|
|
||||||
'system',
|
|
||||||
'head_of_admin',
|
|
||||||
'admin',
|
|
||||||
'branch_manager',
|
|
||||||
'head_of_accountant',
|
|
||||||
'head_of_sale',
|
|
||||||
'sale',
|
|
||||||
]),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'customer',
|
label: 'customer',
|
||||||
route: '/customer-management',
|
route: '/customer-management',
|
||||||
hidden: !isRoleInclude([
|
hidden: !canAccess('customer'),
|
||||||
'system',
|
|
||||||
'head_of_admin',
|
|
||||||
'admin',
|
|
||||||
'branch_manager',
|
|
||||||
'head_of_accountant',
|
|
||||||
'accountant',
|
|
||||||
'head_of_sale',
|
|
||||||
'sale',
|
|
||||||
]),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'agencies',
|
label: 'agencies',
|
||||||
route: '/agencies-management',
|
route: '/agencies-management',
|
||||||
hidden: !isRoleInclude(['system', 'head_of_admin', 'admin']),
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: 'menu.sales',
|
label: 'menu.sales',
|
||||||
icon: 'mdi-store-settings-outline',
|
icon: 'mdi-store-settings-outline',
|
||||||
hidden: !isRoleInclude([
|
|
||||||
'system',
|
|
||||||
'head_of_admin',
|
|
||||||
'admin',
|
|
||||||
'branch_manager',
|
|
||||||
'head_of_accountant',
|
|
||||||
'accountant',
|
|
||||||
'head_of_sale',
|
|
||||||
'sale',
|
|
||||||
]),
|
|
||||||
children: [
|
children: [
|
||||||
{ label: 'quotation', route: '/quotation' },
|
{ label: 'quotation', route: '/quotation' },
|
||||||
{ label: 'invoice', route: '/invoice' },
|
{ label: 'invoice', route: '/invoice' },
|
||||||
|
|
@ -169,16 +128,6 @@ function initMenu() {
|
||||||
label: 'menu.account',
|
label: 'menu.account',
|
||||||
icon: 'mdi-bank-outline',
|
icon: 'mdi-bank-outline',
|
||||||
disabled: false,
|
disabled: false,
|
||||||
hidden: !isRoleInclude([
|
|
||||||
'system',
|
|
||||||
'head_of_admin',
|
|
||||||
'admin',
|
|
||||||
'branch_manager',
|
|
||||||
'head_of_accountant',
|
|
||||||
'accountant',
|
|
||||||
'head_of_sale',
|
|
||||||
'sale',
|
|
||||||
]),
|
|
||||||
children: [
|
children: [
|
||||||
{ label: 'receipt', route: '/receipt' },
|
{ label: 'receipt', route: '/receipt' },
|
||||||
{ label: 'creditNote', route: '/credit-note' },
|
{ label: 'creditNote', route: '/credit-note' },
|
||||||
|
|
@ -200,10 +149,13 @@ function initMenu() {
|
||||||
{
|
{
|
||||||
label: 'menu.overall',
|
label: 'menu.overall',
|
||||||
icon: 'mdi-monitor-dashboard',
|
icon: 'mdi-monitor-dashboard',
|
||||||
hidden: !isRoleInclude(['system', 'head_of_admin', 'admin', 'executive']),
|
|
||||||
children: [
|
children: [
|
||||||
{ label: 'report', route: '/report' },
|
{ label: 'report', route: '/report' },
|
||||||
{ label: 'dashboard', route: '/dash-board' },
|
{
|
||||||
|
label: 'dashboard',
|
||||||
|
route: '/dash-board',
|
||||||
|
hidden: !canAccess('dashBoard'),
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import type { QTableProps, QTableSlots } from 'quasar';
|
||||||
import { resetScrollBar } from 'src/stores/utils';
|
import { resetScrollBar } from 'src/stores/utils';
|
||||||
import useBranchStore from 'stores/branch';
|
import useBranchStore from 'stores/branch';
|
||||||
import useFlowStore from 'stores/flow';
|
import useFlowStore from 'stores/flow';
|
||||||
import { isRoleInclude } from 'stores/utils';
|
import { isRoleInclude, canAccess } from 'stores/utils';
|
||||||
import {
|
import {
|
||||||
BranchWithChildren,
|
BranchWithChildren,
|
||||||
BranchCreate,
|
BranchCreate,
|
||||||
|
|
@ -1050,7 +1050,7 @@ watch(currentHq, () => {
|
||||||
{{ $t('branch.allBranch') }}
|
{{ $t('branch.allBranch') }}
|
||||||
</div>
|
</div>
|
||||||
<q-btn
|
<q-btn
|
||||||
v-if="isRoleInclude(['head_of_admin', 'admin', 'system'])"
|
v-if="isRoleInclude(['system'])"
|
||||||
round
|
round
|
||||||
flat
|
flat
|
||||||
size="md"
|
size="md"
|
||||||
|
|
@ -1561,6 +1561,16 @@ watch(currentHq, () => {
|
||||||
</q-td>
|
</q-td>
|
||||||
<q-td>
|
<q-td>
|
||||||
<KebabAction
|
<KebabAction
|
||||||
|
v-if="
|
||||||
|
isRoleInclude([
|
||||||
|
'system',
|
||||||
|
'head_of_admin',
|
||||||
|
'admin',
|
||||||
|
'executive',
|
||||||
|
'accountant',
|
||||||
|
]) ||
|
||||||
|
(canAccess('branch') && currentHq.id)
|
||||||
|
"
|
||||||
:status="props.row.status"
|
:status="props.row.status"
|
||||||
:idName="props.row.name"
|
:idName="props.row.name"
|
||||||
@view="
|
@view="
|
||||||
|
|
@ -1702,6 +1712,16 @@ watch(currentHq, () => {
|
||||||
>
|
>
|
||||||
<template v-slot:action>
|
<template v-slot:action>
|
||||||
<KebabAction
|
<KebabAction
|
||||||
|
v-if="
|
||||||
|
isRoleInclude([
|
||||||
|
'system',
|
||||||
|
'head_of_admin',
|
||||||
|
'admin',
|
||||||
|
'executive',
|
||||||
|
'accountant',
|
||||||
|
]) ||
|
||||||
|
(canAccess('branch') && currentHq.id)
|
||||||
|
"
|
||||||
:status="props.row.status"
|
:status="props.row.status"
|
||||||
:idName="props.row.name"
|
:idName="props.row.name"
|
||||||
@view="
|
@view="
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import { baseUrl } from 'src/stores/utils';
|
||||||
import useCustomerStore from 'stores/customer';
|
import useCustomerStore from 'stores/customer';
|
||||||
import useFlowStore from 'stores/flow';
|
import useFlowStore from 'stores/flow';
|
||||||
import useOptionStore from 'stores/options';
|
import useOptionStore from 'stores/options';
|
||||||
import { dialog } from 'stores/utils';
|
import { dialog, canAccess } from 'stores/utils';
|
||||||
|
|
||||||
import { Status } from 'stores/types';
|
import { Status } from 'stores/types';
|
||||||
import { Employee } from 'stores/employee/types';
|
import { Employee } from 'stores/employee/types';
|
||||||
|
|
@ -285,7 +285,7 @@ watch(
|
||||||
<template>
|
<template>
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
style="z-index: 999"
|
style="z-index: 999"
|
||||||
v-if="$route.name !== 'CustomerManagement'"
|
v-if="$route.name !== 'CustomerManagement' && canAccess('customer', 'edit')"
|
||||||
@click="openEmployerBranchForm('create')"
|
@click="openEmployerBranchForm('create')"
|
||||||
hide-icon
|
hide-icon
|
||||||
></FloatingActionButton>
|
></FloatingActionButton>
|
||||||
|
|
@ -615,7 +615,7 @@ watch(
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<TableEmpoloyee
|
<TableEmpoloyee
|
||||||
:prefix-id="props.row.registerName || props.row.firstName"
|
:prefix-id="props.row.registerName || props.row.firstName"
|
||||||
add-button
|
:add-button="canAccess('customer', 'edit')"
|
||||||
in-table
|
in-table
|
||||||
:list-employee="listEmployee"
|
:list-employee="listEmployee"
|
||||||
:columns-employee="columnsEmployee"
|
:columns-employee="columnsEmployee"
|
||||||
|
|
@ -759,7 +759,10 @@ watch(
|
||||||
/>
|
/>
|
||||||
<DeleteButton
|
<DeleteButton
|
||||||
icon-only
|
icon-only
|
||||||
v-if="customerBranchFormState.dialogType === 'info'"
|
v-if="
|
||||||
|
customerBranchFormState.dialogType === 'info' &&
|
||||||
|
canAccess('customer', 'edit')
|
||||||
|
"
|
||||||
@click="
|
@click="
|
||||||
() => {
|
() => {
|
||||||
deleteBranchById(customerBranchFormData.id || '');
|
deleteBranchById(customerBranchFormData.id || '');
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@ import { useRoute, useRouter } from 'vue-router';
|
||||||
import { getUserId, getRole } from 'src/services/keycloak';
|
import { getUserId, getRole } from 'src/services/keycloak';
|
||||||
import { baseUrl, setPrefixName, waitAll } from 'src/stores/utils';
|
import { baseUrl, setPrefixName, waitAll } from 'src/stores/utils';
|
||||||
import { dateFormat } from 'src/utils/datetime';
|
import { dateFormat } from 'src/utils/datetime';
|
||||||
import { dialogCheckData } from 'stores/utils';
|
import { dialogCheckData, canAccess } from 'stores/utils';
|
||||||
|
|
||||||
import useOcrStore from 'stores/ocr';
|
import useOcrStore from 'stores/ocr';
|
||||||
import useCustomerStore from 'stores/customer';
|
import useCustomerStore from 'stores/customer';
|
||||||
|
|
@ -397,7 +397,11 @@ async function fetchListEmployee(opt?: {
|
||||||
employeeStats.value = await employeeStore.getStatsEmployee();
|
employeeStats.value = await employeeStore.getStatsEmployee();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function triggerChangeStatus(id: string, status: string) {
|
async function triggerChangeStatus(
|
||||||
|
id: string,
|
||||||
|
status: string,
|
||||||
|
employeeName?: string,
|
||||||
|
) {
|
||||||
return await new Promise((resolve, reject) => {
|
return await new Promise((resolve, reject) => {
|
||||||
dialog({
|
dialog({
|
||||||
color: status !== 'INACTIVE' ? 'warning' : 'info',
|
color: status !== 'INACTIVE' ? 'warning' : 'info',
|
||||||
|
|
@ -412,7 +416,11 @@ async function triggerChangeStatus(id: string, status: string) {
|
||||||
: t('dialog.message.confirmChangeStatusOn'),
|
: t('dialog.message.confirmChangeStatusOn'),
|
||||||
action: async () => {
|
action: async () => {
|
||||||
if (currentTab.value === 'employee') {
|
if (currentTab.value === 'employee') {
|
||||||
await toggleStatusEmployee(id, status === 'INACTIVE' ? false : true)
|
await toggleStatusEmployee(
|
||||||
|
id,
|
||||||
|
status === 'INACTIVE' ? false : true,
|
||||||
|
employeeName,
|
||||||
|
)
|
||||||
.then(resolve)
|
.then(resolve)
|
||||||
.catch(reject);
|
.catch(reject);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -426,9 +434,14 @@ async function triggerChangeStatus(id: string, status: string) {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function toggleStatusEmployee(id: string, status: boolean) {
|
async function toggleStatusEmployee(
|
||||||
|
id: string,
|
||||||
|
status: boolean,
|
||||||
|
employeeName: string,
|
||||||
|
) {
|
||||||
const res = await employeeStore.editById(id, {
|
const res = await employeeStore.editById(id, {
|
||||||
status: !status ? 'ACTIVE' : 'INACTIVE',
|
status: !status ? 'ACTIVE' : 'INACTIVE',
|
||||||
|
firstNameEN: employeeName,
|
||||||
});
|
});
|
||||||
if (res && employeeFormState.value.drawerModal)
|
if (res && employeeFormState.value.drawerModal)
|
||||||
currentFromDataEmployee.value.status = res.status;
|
currentFromDataEmployee.value.status = res.status;
|
||||||
|
|
@ -658,7 +671,7 @@ const emptyCreateDialog = ref(false);
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
style="z-index: 999"
|
style="z-index: 999"
|
||||||
:hide-icon="currentTab === 'employee'"
|
:hide-icon="currentTab === 'employee'"
|
||||||
v-if="$route.name === 'CustomerManagement'"
|
v-if="$route.name === 'CustomerManagement' && canAccess('customer', 'edit')"
|
||||||
@click="
|
@click="
|
||||||
() => {
|
() => {
|
||||||
if (currentTab === 'employee') {
|
if (currentTab === 'employee') {
|
||||||
|
|
@ -1391,6 +1404,7 @@ const emptyCreateDialog = ref(false);
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<KebabAction
|
<KebabAction
|
||||||
|
:hide-delete="!canAccess('customer', 'edit')"
|
||||||
:id-name="
|
:id-name="
|
||||||
props.row.branch[0].customerName ||
|
props.row.branch[0].customerName ||
|
||||||
props.row.branch[0].firstName
|
props.row.branch[0].firstName
|
||||||
|
|
@ -1638,7 +1652,17 @@ const emptyCreateDialog = ref(false);
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-slot:action>
|
<template v-slot:action>
|
||||||
|
<q-btn
|
||||||
|
icon="mdi-eye-outline"
|
||||||
|
:id="`btn-eye-${props.row.branch[0].customerName || props.row.branch[0].firstName}`"
|
||||||
|
size="sm"
|
||||||
|
dense
|
||||||
|
round
|
||||||
|
flat
|
||||||
|
@click.stop="editCustomerForm(props.row.id)"
|
||||||
|
/>
|
||||||
<KebabAction
|
<KebabAction
|
||||||
|
:hide-delete="!canAccess('customer', 'edit')"
|
||||||
:status="props.row.status"
|
:status="props.row.status"
|
||||||
:id-name="props.row.name"
|
:id-name="props.row.name"
|
||||||
@view="
|
@view="
|
||||||
|
|
@ -1772,6 +1796,7 @@ const emptyCreateDialog = ref(false);
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<TableEmpoloyee
|
<TableEmpoloyee
|
||||||
|
:hide-delete="!canAccess('customer', 'edit')"
|
||||||
v-model:page-size="pageSize"
|
v-model:page-size="pageSize"
|
||||||
v-model:current-page="currentPageEmployee"
|
v-model:current-page="currentPageEmployee"
|
||||||
:grid-view="gridView"
|
:grid-view="gridView"
|
||||||
|
|
@ -1819,7 +1844,11 @@ const emptyCreateDialog = ref(false);
|
||||||
"
|
"
|
||||||
@toggle-status="
|
@toggle-status="
|
||||||
async (item: any) => {
|
async (item: any) => {
|
||||||
triggerChangeStatus(item.id, item.status);
|
triggerChangeStatus(
|
||||||
|
item.id,
|
||||||
|
item.status,
|
||||||
|
item.firstNameEN,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
|
@ -1889,6 +1918,7 @@ const emptyCreateDialog = ref(false);
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<TooltipComponent
|
<TooltipComponent
|
||||||
|
v-if="canAccess('customer', 'edit')"
|
||||||
class="self-end q-ma-md"
|
class="self-end q-ma-md"
|
||||||
:title="'general.noData'"
|
:title="'general.noData'"
|
||||||
:caption="'general.clickToCreate'"
|
:caption="'general.clickToCreate'"
|
||||||
|
|
@ -1900,6 +1930,7 @@ const emptyCreateDialog = ref(false);
|
||||||
style="flex-grow: 1"
|
style="flex-grow: 1"
|
||||||
>
|
>
|
||||||
<EmptyAddButton
|
<EmptyAddButton
|
||||||
|
v-if="canAccess('customer', 'edit')"
|
||||||
:label="'general.add'"
|
:label="'general.add'"
|
||||||
:i18n-args="{
|
:i18n-args="{
|
||||||
text: $t(`customer.${currentTab}`),
|
text: $t(`customer.${currentTab}`),
|
||||||
|
|
@ -1914,6 +1945,7 @@ const emptyCreateDialog = ref(false);
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
|
<NoData v-else />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -4423,6 +4455,7 @@ const emptyCreateDialog = ref(false);
|
||||||
(customerFormState.branchIndex !== -1 &&
|
(customerFormState.branchIndex !== -1 &&
|
||||||
customerFormState.branchIndex !== idx)
|
customerFormState.branchIndex !== idx)
|
||||||
"
|
"
|
||||||
|
:hide-delete="!canAccess('customer', 'edit')"
|
||||||
:readonly="customerFormState.branchIndex !== idx"
|
:readonly="customerFormState.branchIndex !== idx"
|
||||||
@edit="() => (customerFormState.branchIndex = idx)"
|
@edit="() => (customerFormState.branchIndex = idx)"
|
||||||
@cancel="() => customerFormUndo(false)"
|
@cancel="() => customerFormUndo(false)"
|
||||||
|
|
@ -4463,11 +4496,18 @@ const emptyCreateDialog = ref(false);
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
:title="
|
:title="
|
||||||
employeeFormState.currentEmployee
|
setPrefixName(
|
||||||
? $i18n.locale === 'eng'
|
{
|
||||||
? `${employeeFormState.currentEmployee.firstNameEN} ${employeeFormState.currentEmployee.lastNameEN}`
|
namePrefix: employeeFormState.currentEmployee.namePrefix,
|
||||||
: `${employeeFormState.currentEmployee.firstName} ${employeeFormState.currentEmployee.lastName}`
|
firstName:
|
||||||
: '-'
|
employeeFormState.currentEmployee.firstName ||
|
||||||
|
employeeFormState.currentEmployee.firstNameEN,
|
||||||
|
lastName: employeeFormState.currentEmployee.lastName,
|
||||||
|
firstNameEN: employeeFormState.currentEmployee.firstNameEN,
|
||||||
|
lastNameEN: employeeFormState.currentEmployee.lastNameEN,
|
||||||
|
},
|
||||||
|
{ locale },
|
||||||
|
)
|
||||||
"
|
"
|
||||||
:badge-class="
|
:badge-class="
|
||||||
currentFromDataEmployee.gender === 'male'
|
currentFromDataEmployee.gender === 'male'
|
||||||
|
|
@ -4562,7 +4602,11 @@ const emptyCreateDialog = ref(false);
|
||||||
@update:toggle-status="
|
@update:toggle-status="
|
||||||
(v) => {
|
(v) => {
|
||||||
if (currentFromDataEmployee.id !== undefined)
|
if (currentFromDataEmployee.id !== undefined)
|
||||||
triggerChangeStatus(currentFromDataEmployee.id, v);
|
triggerChangeStatus(
|
||||||
|
currentFromDataEmployee.id,
|
||||||
|
v,
|
||||||
|
currentFromDataEmployee.firstNameEN,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
:active="currentFromDataEmployee.status !== 'INACTIVE'"
|
:active="currentFromDataEmployee.status !== 'INACTIVE'"
|
||||||
|
|
@ -4582,7 +4626,9 @@ const emptyCreateDialog = ref(false);
|
||||||
? setPrefixName(
|
? setPrefixName(
|
||||||
{
|
{
|
||||||
namePrefix: employeeFormState.currentEmployee.namePrefix,
|
namePrefix: employeeFormState.currentEmployee.namePrefix,
|
||||||
firstName: employeeFormState.currentEmployee.firstName,
|
firstName:
|
||||||
|
employeeFormState.currentEmployee.firstName ||
|
||||||
|
employeeFormState.currentEmployee.firstNameEN,
|
||||||
lastName: employeeFormState.currentEmployee.lastName,
|
lastName: employeeFormState.currentEmployee.lastName,
|
||||||
firstNameEN: employeeFormState.currentEmployee.firstNameEN,
|
firstNameEN: employeeFormState.currentEmployee.firstNameEN,
|
||||||
lastNameEN: employeeFormState.currentEmployee.lastNameEN,
|
lastNameEN: employeeFormState.currentEmployee.lastNameEN,
|
||||||
|
|
@ -4878,7 +4924,10 @@ const emptyCreateDialog = ref(false);
|
||||||
type="button"
|
type="button"
|
||||||
/>
|
/>
|
||||||
<DeleteButton
|
<DeleteButton
|
||||||
v-if="!employeeFormState.isEmployeeEdit"
|
v-if="
|
||||||
|
!employeeFormState.isEmployeeEdit &&
|
||||||
|
canAccess('customer', 'edit')
|
||||||
|
"
|
||||||
id="btn-info-basic-delete"
|
id="btn-info-basic-delete"
|
||||||
icon-only
|
icon-only
|
||||||
@click="
|
@click="
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,13 @@ const telephoneNo = defineModel<string>('telephoneNo', { default: '' });
|
||||||
class="col-md-6"
|
class="col-md-6"
|
||||||
:readonly
|
:readonly
|
||||||
:disabled="
|
:disabled="
|
||||||
!isRoleInclude(['admin', 'system', 'head_of_admin']) && !readonly
|
!isRoleInclude([
|
||||||
|
'admin',
|
||||||
|
'system',
|
||||||
|
'head_of_admin',
|
||||||
|
'executive',
|
||||||
|
'accountant',
|
||||||
|
]) && !readonly
|
||||||
"
|
"
|
||||||
:label="$t('customer.form.registeredBranch')"
|
:label="$t('customer.form.registeredBranch')"
|
||||||
select-first-value
|
select-first-value
|
||||||
|
|
|
||||||
|
|
@ -51,6 +51,7 @@ withDefaults(
|
||||||
actionDisabled?: boolean;
|
actionDisabled?: boolean;
|
||||||
customerType?: 'CORP' | 'PERS';
|
customerType?: 'CORP' | 'PERS';
|
||||||
hideAction?: boolean;
|
hideAction?: boolean;
|
||||||
|
hideDelete?: boolean;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
hideAction: false,
|
hideAction: false,
|
||||||
|
|
@ -81,7 +82,7 @@ withDefaults(
|
||||||
/>
|
/>
|
||||||
<DeleteButton
|
<DeleteButton
|
||||||
icon-only
|
icon-only
|
||||||
v-if="readonly"
|
v-if="readonly && !hideDelete"
|
||||||
@click="$emit('delete')"
|
@click="$emit('delete')"
|
||||||
type="button"
|
type="button"
|
||||||
:disabled="actionDisabled"
|
:disabled="actionDisabled"
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
isEdit?: boolean;
|
isEdit?: boolean;
|
||||||
|
hideAction?: boolean;
|
||||||
}>(),
|
}>(),
|
||||||
{ readonly: false, isEdit: false },
|
{ readonly: false, isEdit: false },
|
||||||
);
|
);
|
||||||
|
|
@ -207,7 +208,7 @@ function triggerPropertiesDialog(step: WorkFlowPayloadStep) {
|
||||||
style="position: absolute; z-index: 999; top: 0; right: 0"
|
style="position: absolute; z-index: 999; top: 0; right: 0"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="flowData.status !== 'INACTIVE'"
|
v-if="flowData.status !== 'INACTIVE' && !hideAction"
|
||||||
class="surface-1 row rounded"
|
class="surface-1 row rounded"
|
||||||
>
|
>
|
||||||
<UndoButton
|
<UndoButton
|
||||||
|
|
@ -287,6 +288,7 @@ function triggerPropertiesDialog(step: WorkFlowPayloadStep) {
|
||||||
>
|
>
|
||||||
<template v-slot:btn-form-flow-step-drawer>
|
<template v-slot:btn-form-flow-step-drawer>
|
||||||
<q-btn
|
<q-btn
|
||||||
|
v-if="!hideAction"
|
||||||
dense
|
dense
|
||||||
flat
|
flat
|
||||||
icon="mdi-plus"
|
icon="mdi-plus"
|
||||||
|
|
@ -315,6 +317,7 @@ function triggerPropertiesDialog(step: WorkFlowPayloadStep) {
|
||||||
<FormFlow
|
<FormFlow
|
||||||
:readonly
|
:readonly
|
||||||
onDrawer
|
onDrawer
|
||||||
|
:hide-action="hideAction"
|
||||||
v-model:user-in-table="userInTable"
|
v-model:user-in-table="userInTable"
|
||||||
v-model:flow-data="flowData"
|
v-model:flow-data="flowData"
|
||||||
v-model:register-branch-id="registerBranchId"
|
v-model:register-branch-id="registerBranchId"
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ import {
|
||||||
} from 'src/stores/workflow-template/types';
|
} from 'src/stores/workflow-template/types';
|
||||||
import { useWorkflowTemplate } from 'src/stores/workflow-template';
|
import { useWorkflowTemplate } from 'src/stores/workflow-template';
|
||||||
import { useNavigator } from 'src/stores/navigator';
|
import { useNavigator } from 'src/stores/navigator';
|
||||||
import { dialog } from 'src/stores/utils';
|
import { dialog, canAccess } from 'src/stores/utils';
|
||||||
|
|
||||||
import FloatingActionButton from 'components/FloatingActionButton.vue';
|
import FloatingActionButton from 'components/FloatingActionButton.vue';
|
||||||
import StatCardComponent from 'src/components/StatCardComponent.vue';
|
import StatCardComponent from 'src/components/StatCardComponent.vue';
|
||||||
|
|
@ -334,6 +334,7 @@ watch(
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
|
v-if="canAccess('workflow', 'edit')"
|
||||||
style="z-index: 999"
|
style="z-index: 999"
|
||||||
hide-icon
|
hide-icon
|
||||||
@click="triggerDialog('add')"
|
@click="triggerDialog('add')"
|
||||||
|
|
@ -682,6 +683,7 @@ watch(
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<KebabAction
|
<KebabAction
|
||||||
|
v-if="canAccess('workflow', 'edit')"
|
||||||
:id-name="props.row.name"
|
:id-name="props.row.name"
|
||||||
:status="props.row.status"
|
:status="props.row.status"
|
||||||
@view="
|
@view="
|
||||||
|
|
@ -763,6 +765,7 @@ watch(
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<KebabAction
|
<KebabAction
|
||||||
|
v-if="canAccess('workflow', 'edit')"
|
||||||
:id-name="props.row.name"
|
:id-name="props.row.name"
|
||||||
:status="props.row.status"
|
:status="props.row.status"
|
||||||
@view="
|
@view="
|
||||||
|
|
@ -846,6 +849,7 @@ watch(
|
||||||
@drawer-undo="undo"
|
@drawer-undo="undo"
|
||||||
@close="resetForm"
|
@close="resetForm"
|
||||||
@submit="submit"
|
@submit="submit"
|
||||||
|
:hide-action="!canAccess('workflow', 'edit')"
|
||||||
:readonly="!pageState.isDrawerEdit"
|
:readonly="!pageState.isDrawerEdit"
|
||||||
:isEdit="pageState.isDrawerEdit"
|
:isEdit="pageState.isDrawerEdit"
|
||||||
v-model="pageState.addModal"
|
v-model="pageState.addModal"
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,7 @@ import PaginationPageSize from 'src/components/PaginationPageSize.vue';
|
||||||
import useFlowStore from 'stores/flow';
|
import useFlowStore from 'stores/flow';
|
||||||
|
|
||||||
import { dateFormat } from 'src/utils/datetime';
|
import { dateFormat } from 'src/utils/datetime';
|
||||||
import { formatNumberDecimal, isRoleInclude, notify } from 'stores/utils';
|
import { formatNumberDecimal, isRoleInclude, canAccess } from 'stores/utils';
|
||||||
const { getWorkflowTemplate } = useWorkflowTemplate();
|
const { getWorkflowTemplate } = useWorkflowTemplate();
|
||||||
|
|
||||||
import { Status } from 'stores/types';
|
import { Status } from 'stores/types';
|
||||||
|
|
@ -143,27 +143,25 @@ const { t } = useI18n();
|
||||||
const baseUrl = ref<string>(import.meta.env.VITE_API_BASE_URL);
|
const baseUrl = ref<string>(import.meta.env.VITE_API_BASE_URL);
|
||||||
|
|
||||||
const priceDisplay = computed(() => ({
|
const priceDisplay = computed(() => ({
|
||||||
price: !isRoleInclude(['sale_agent']),
|
// price: !isRoleInclude(['sale_agent']),
|
||||||
|
price: true,
|
||||||
agentPrice: isRoleInclude([
|
agentPrice: isRoleInclude([
|
||||||
'admin',
|
|
||||||
'head_of_admin',
|
|
||||||
'head_of_sale',
|
|
||||||
'system',
|
'system',
|
||||||
'owner',
|
'head_of_admin',
|
||||||
|
'admin',
|
||||||
|
'executive',
|
||||||
'accountant',
|
'accountant',
|
||||||
'sale_agent',
|
'head_of_sale',
|
||||||
]),
|
]),
|
||||||
serviceCharge: isRoleInclude([
|
serviceCharge: isRoleInclude([
|
||||||
'admin',
|
|
||||||
'head_of_admin',
|
|
||||||
'system',
|
'system',
|
||||||
'owner',
|
'head_of_admin',
|
||||||
|
'admin',
|
||||||
|
'executive',
|
||||||
'accountant',
|
'accountant',
|
||||||
]),
|
]),
|
||||||
}));
|
}));
|
||||||
const actionDisplay = computed(() =>
|
const actionDisplay = computed(() => canAccess('product', 'edit'));
|
||||||
isRoleInclude(['admin', 'head_of_admin', 'system', 'owner', 'accountant']),
|
|
||||||
);
|
|
||||||
const splitterModel = computed(() =>
|
const splitterModel = computed(() =>
|
||||||
$q.screen.lt.md ? (productMode.value !== 'group' ? 0 : 100) : 25,
|
$q.screen.lt.md ? (productMode.value !== 'group' ? 0 : 100) : 25,
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import { FloatingActionButton, PaginationComponent } from 'src/components';
|
||||||
import PaginationPageSize from 'src/components/PaginationPageSize.vue';
|
import PaginationPageSize from 'src/components/PaginationPageSize.vue';
|
||||||
import PropertyDialog from './PropertyDialog.vue';
|
import PropertyDialog from './PropertyDialog.vue';
|
||||||
import { Property } from 'src/stores/property/types';
|
import { Property } from 'src/stores/property/types';
|
||||||
import { dialog, toCamelCase } from 'src/stores/utils';
|
import { dialog, toCamelCase, canAccess } from 'src/stores/utils';
|
||||||
import CreateButton from 'src/components/AddButton.vue';
|
import CreateButton from 'src/components/AddButton.vue';
|
||||||
import useOptionStore from 'stores/options';
|
import useOptionStore from 'stores/options';
|
||||||
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
|
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
|
||||||
|
|
@ -331,6 +331,7 @@ watch(
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
|
v-if="canAccess('workflow', 'edit')"
|
||||||
style="z-index: 999"
|
style="z-index: 999"
|
||||||
hide-icon
|
hide-icon
|
||||||
@click="triggerDialog('add')"
|
@click="triggerDialog('add')"
|
||||||
|
|
@ -536,11 +537,19 @@ watch(
|
||||||
class="col surface-2 flex items-center justify-center"
|
class="col surface-2 flex items-center justify-center"
|
||||||
>
|
>
|
||||||
<NoData
|
<NoData
|
||||||
v-if="pageState.total !== 0 || pageState.searchDate.length > 0"
|
v-if="
|
||||||
|
pageState.total !== 0 ||
|
||||||
|
pageState.searchDate.length > 0 ||
|
||||||
|
!canAccess('workflow', 'edit')
|
||||||
|
"
|
||||||
:not-found="!!pageState.inputSearch"
|
:not-found="!!pageState.inputSearch"
|
||||||
/>
|
/>
|
||||||
<CreateButton
|
<CreateButton
|
||||||
v-if="pageState.total === 0 && pageState.searchDate.length === 0"
|
v-if="
|
||||||
|
pageState.total === 0 &&
|
||||||
|
pageState.searchDate.length === 0 &&
|
||||||
|
canAccess('workflow', 'edit')
|
||||||
|
"
|
||||||
@click="triggerDialog('add')"
|
@click="triggerDialog('add')"
|
||||||
label="general.add"
|
label="general.add"
|
||||||
:i18n-args="{ text: $t('flow.title') }"
|
:i18n-args="{ text: $t('flow.title') }"
|
||||||
|
|
@ -698,6 +707,7 @@ watch(
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<KebabAction
|
<KebabAction
|
||||||
|
v-if="canAccess('workflow', 'edit')"
|
||||||
:id-name="props.row.name"
|
:id-name="props.row.name"
|
||||||
:status="props.row.status"
|
:status="props.row.status"
|
||||||
@view="
|
@view="
|
||||||
|
|
@ -815,6 +825,7 @@ watch(
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<KebabAction
|
<KebabAction
|
||||||
|
v-if="canAccess('workflow', 'edit')"
|
||||||
:id-name="props.row.id"
|
:id-name="props.row.id"
|
||||||
:status="props.row.status"
|
:status="props.row.status"
|
||||||
@view="
|
@view="
|
||||||
|
|
@ -906,6 +917,7 @@ watch(
|
||||||
@drawer-undo="() => undo()"
|
@drawer-undo="() => undo()"
|
||||||
@close="() => resetForm()"
|
@close="() => resetForm()"
|
||||||
@submit="() => submit()"
|
@submit="() => submit()"
|
||||||
|
:hide-action="!canAccess('workflow', 'edit')"
|
||||||
:readonly="!pageState.isDrawerEdit"
|
:readonly="!pageState.isDrawerEdit"
|
||||||
:isEdit="pageState.isDrawerEdit"
|
:isEdit="pageState.isDrawerEdit"
|
||||||
v-model="pageState.addModal"
|
v-model="pageState.addModal"
|
||||||
|
|
|
||||||
|
|
@ -30,6 +30,7 @@ withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
isEdit?: boolean;
|
isEdit?: boolean;
|
||||||
|
hideAction?: boolean;
|
||||||
}>(),
|
}>(),
|
||||||
{ readonly: false, isEdit: false },
|
{ readonly: false, isEdit: false },
|
||||||
);
|
);
|
||||||
|
|
@ -151,7 +152,7 @@ defineEmits<{
|
||||||
style="position: absolute; z-index: 999; top: 0; right: 0"
|
style="position: absolute; z-index: 999; top: 0; right: 0"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="propertyData.status !== 'INACTIVE'"
|
v-if="propertyData.status !== 'INACTIVE' && !hideAction"
|
||||||
class="surface-1 row rounded"
|
class="surface-1 row rounded"
|
||||||
>
|
>
|
||||||
<UndoButton
|
<UndoButton
|
||||||
|
|
@ -236,6 +237,7 @@ defineEmits<{
|
||||||
<FormProperty
|
<FormProperty
|
||||||
onDrawer
|
onDrawer
|
||||||
:readonly="!isEdit"
|
:readonly="!isEdit"
|
||||||
|
:disable-toggle="hideAction"
|
||||||
v-model:name="formProperty.name"
|
v-model:name="formProperty.name"
|
||||||
v-model:name-en="formProperty.nameEN"
|
v-model:name-en="formProperty.nameEN"
|
||||||
v-model:type="formProperty.type"
|
v-model:type="formProperty.type"
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import useMyBranch from 'stores/my-branch';
|
||||||
import { useQuotationForm } from './form';
|
import { useQuotationForm } from './form';
|
||||||
import { hslaColors } from './constants';
|
import { hslaColors } from './constants';
|
||||||
import { pageTabs, columnQuotation } from './constants';
|
import { pageTabs, columnQuotation } from './constants';
|
||||||
import { toCamelCase } from 'stores/utils';
|
import { toCamelCase, canAccess } from 'stores/utils';
|
||||||
|
|
||||||
// NOTE Import Types
|
// NOTE Import Types
|
||||||
import { CustomerBranchCreate, CustomerType } from 'stores/customer/types';
|
import { CustomerBranchCreate, CustomerType } from 'stores/customer/types';
|
||||||
|
|
@ -413,6 +413,7 @@ async function storeDataLocal(id: string) {
|
||||||
hide-icon
|
hide-icon
|
||||||
style="z-index: 999"
|
style="z-index: 999"
|
||||||
@click.stop="triggerAddQuotationDialog"
|
@click.stop="triggerAddQuotationDialog"
|
||||||
|
v-if="canAccess('quotation', 'edit')"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="column full-height no-wrap">
|
<div class="column full-height no-wrap">
|
||||||
|
|
@ -647,12 +648,20 @@ async function storeDataLocal(id: string) {
|
||||||
class="col surface-2 flex items-center justify-center"
|
class="col surface-2 flex items-center justify-center"
|
||||||
>
|
>
|
||||||
<NoData
|
<NoData
|
||||||
v-if="pageState.inputSearch || pageState.currentTab !== 'Issued'"
|
v-if="
|
||||||
|
pageState.inputSearch ||
|
||||||
|
!canAccess('quotation', 'edit') ||
|
||||||
|
pageState.currentTab !== 'Issued'
|
||||||
|
"
|
||||||
:not-found="!!pageState.inputSearch"
|
:not-found="!!pageState.inputSearch"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<CreateButton
|
<CreateButton
|
||||||
v-if="!pageState.inputSearch && pageState.currentTab === 'Issued'"
|
v-if="
|
||||||
|
!pageState.inputSearch &&
|
||||||
|
pageState.currentTab === 'Issued' &&
|
||||||
|
canAccess('quotation', 'edit')
|
||||||
|
"
|
||||||
@click="triggerAddQuotationDialog"
|
@click="triggerAddQuotationDialog"
|
||||||
label="general.add"
|
label="general.add"
|
||||||
:i18n-args="{ text: $t('quotation.title') }"
|
:i18n-args="{ text: $t('quotation.title') }"
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,7 @@ const { data: config } = storeToRefs(configStore);
|
||||||
|
|
||||||
const prop = defineProps<{
|
const prop = defineProps<{
|
||||||
data?: Quotation | QuotationFull | DebitNote;
|
data?: Quotation | QuotationFull | DebitNote;
|
||||||
|
readonly?: boolean;
|
||||||
isDebitNote?: boolean;
|
isDebitNote?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
|
|
@ -479,7 +480,7 @@ onMounted(async () => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- bill -->
|
<!-- bill -->
|
||||||
<span class="app-text-muted-2 q-pt-md">
|
<span class="app-text-muted-2 q-pt-md" v-if="paymentData.length > 0">
|
||||||
{{ $t('quotation.receiptDialog.billOfPayment') }}
|
{{ $t('quotation.receiptDialog.billOfPayment') }}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
|
@ -521,6 +522,7 @@ onMounted(async () => {
|
||||||
|
|
||||||
<div class="q-ml-auto row" style="gap: 10px">
|
<div class="q-ml-auto row" style="gap: 10px">
|
||||||
<q-btn
|
<q-btn
|
||||||
|
:disable="readonly"
|
||||||
id="btn-payment"
|
id="btn-payment"
|
||||||
@click.stop
|
@click.stop
|
||||||
unelevated
|
unelevated
|
||||||
|
|
@ -547,7 +549,11 @@ onMounted(async () => {
|
||||||
<span>
|
<span>
|
||||||
{{ $t(`quotation.receiptDialog.${p.paymentStatus}`) }}
|
{{ $t(`quotation.receiptDialog.${p.paymentStatus}`) }}
|
||||||
</span>
|
</span>
|
||||||
<q-icon name="mdi-chevron-down" class="q-pl-xs" />
|
<q-icon
|
||||||
|
v-if="!readonly"
|
||||||
|
name="mdi-chevron-down"
|
||||||
|
class="q-pl-xs"
|
||||||
|
/>
|
||||||
<q-menu
|
<q-menu
|
||||||
ref="refQMenu"
|
ref="refQMenu"
|
||||||
fit
|
fit
|
||||||
|
|
@ -634,6 +640,7 @@ onMounted(async () => {
|
||||||
})
|
})
|
||||||
}}
|
}}
|
||||||
<q-btn
|
<q-btn
|
||||||
|
v-if="!readonly"
|
||||||
unelevated
|
unelevated
|
||||||
id="btn-upload-file"
|
id="btn-upload-file"
|
||||||
:label="$t('general.upload')"
|
:label="$t('general.upload')"
|
||||||
|
|
@ -762,10 +769,10 @@ onMounted(async () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(
|
:deep(
|
||||||
.q-expansion-item
|
.q-expansion-item
|
||||||
.q-item.q-item-type.row.no-wrap.q-item--clickable.q-link.cursor-pointer.q-focusable.q-hoverable
|
.q-item.q-item-type.row.no-wrap.q-item--clickable.q-link.cursor-pointer.q-focusable.q-hoverable
|
||||||
.q-focus-helper
|
.q-focus-helper
|
||||||
) {
|
) {
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ import {
|
||||||
dialogCheckData,
|
dialogCheckData,
|
||||||
dialogWarningClose,
|
dialogWarningClose,
|
||||||
formatNumberDecimal,
|
formatNumberDecimal,
|
||||||
|
canAccess,
|
||||||
|
isRoleInclude,
|
||||||
} from 'stores/utils';
|
} from 'stores/utils';
|
||||||
import { ProductTree, quotationProductTree } from './utils';
|
import { ProductTree, quotationProductTree } from './utils';
|
||||||
|
|
||||||
|
|
@ -213,17 +215,6 @@ const attachmentData = ref<
|
||||||
url?: string;
|
url?: string;
|
||||||
}[]
|
}[]
|
||||||
>([]);
|
>([]);
|
||||||
const hideBtnApproveInvoice = computed(() => {
|
|
||||||
const role = getRole();
|
|
||||||
const allowedRoles = [
|
|
||||||
'system',
|
|
||||||
'head_of_admin',
|
|
||||||
'admin',
|
|
||||||
'head_of_accountant',
|
|
||||||
'accountant',
|
|
||||||
];
|
|
||||||
return !role || !role.some((r) => allowedRoles.includes(r));
|
|
||||||
});
|
|
||||||
|
|
||||||
const getToolbarConfig = computed(() => {
|
const getToolbarConfig = computed(() => {
|
||||||
const toolbar = [['left', 'center', 'justify'], ['toggle'], ['clip']];
|
const toolbar = [['left', 'center', 'justify'], ['toggle'], ['clip']];
|
||||||
|
|
@ -1746,7 +1737,7 @@ function covertToNode() {
|
||||||
:readonly="
|
:readonly="
|
||||||
{
|
{
|
||||||
quotation: quotationFormState.mode !== 'edit',
|
quotation: quotationFormState.mode !== 'edit',
|
||||||
invoice: false,
|
invoice: isRoleInclude(['sale', 'head_of_sale']),
|
||||||
accepted: true,
|
accepted: true,
|
||||||
}[view]
|
}[view]
|
||||||
"
|
"
|
||||||
|
|
@ -1950,6 +1941,7 @@ function covertToNode() {
|
||||||
view !== View.Receipt &&
|
view !== View.Receipt &&
|
||||||
view !== View.Complete
|
view !== View.Complete
|
||||||
"
|
"
|
||||||
|
:readonly="isRoleInclude(['sale', 'head_of_sale'])"
|
||||||
:data="quotationFormState.source"
|
:data="quotationFormState.source"
|
||||||
v-model:first-code-payment="firstCodePayment"
|
v-model:first-code-payment="firstCodePayment"
|
||||||
@fetch-status="
|
@fetch-status="
|
||||||
|
|
@ -2327,7 +2319,6 @@ function covertToNode() {
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<MainButton
|
<MainButton
|
||||||
v-if="!hideBtnApproveInvoice"
|
|
||||||
solid
|
solid
|
||||||
icon="mdi-account-multiple-check-outline"
|
icon="mdi-account-multiple-check-outline"
|
||||||
color="207 96% 32%"
|
color="207 96% 32%"
|
||||||
|
|
|
||||||
|
|
@ -66,21 +66,21 @@ const serviceList = defineModel<Partial<Record<ProductGroupId, Service[]>>>(
|
||||||
);
|
);
|
||||||
|
|
||||||
const priceDisplay = computed(() => ({
|
const priceDisplay = computed(() => ({
|
||||||
price: !isRoleInclude(['sale_agent']),
|
// price: !isRoleInclude(['sale_agent']),
|
||||||
|
price: true,
|
||||||
agentPrice: isRoleInclude([
|
agentPrice: isRoleInclude([
|
||||||
'admin',
|
|
||||||
'head_of_admin',
|
|
||||||
'head_of_sale',
|
|
||||||
'system',
|
'system',
|
||||||
'owner',
|
'head_of_admin',
|
||||||
|
'admin',
|
||||||
|
'executive',
|
||||||
'accountant',
|
'accountant',
|
||||||
'sale_agent',
|
'head_of_sale',
|
||||||
]),
|
]),
|
||||||
serviceCharge: isRoleInclude([
|
serviceCharge: isRoleInclude([
|
||||||
'admin',
|
|
||||||
'head_of_admin',
|
|
||||||
'system',
|
'system',
|
||||||
'owner',
|
'head_of_admin',
|
||||||
|
'admin',
|
||||||
|
'executive',
|
||||||
'accountant',
|
'accountant',
|
||||||
]),
|
]),
|
||||||
}));
|
}));
|
||||||
|
|
|
||||||
|
|
@ -62,6 +62,8 @@ const props = withDefaults(
|
||||||
defineProps<{
|
defineProps<{
|
||||||
readonly?: boolean;
|
readonly?: boolean;
|
||||||
isEdit?: boolean;
|
isEdit?: boolean;
|
||||||
|
hideAction?: boolean;
|
||||||
|
hideDelete?: boolean;
|
||||||
|
|
||||||
dataId?: string;
|
dataId?: string;
|
||||||
}>(),
|
}>(),
|
||||||
|
|
@ -411,6 +413,7 @@ watch(
|
||||||
:prefix="data.name"
|
:prefix="data.name"
|
||||||
hide-fade
|
hide-fade
|
||||||
use-toggle
|
use-toggle
|
||||||
|
:readonly="hideAction"
|
||||||
:active="data.status !== 'INACTIVE'"
|
:active="data.status !== 'INACTIVE'"
|
||||||
:toggle-title="$t('status.title')"
|
:toggle-title="$t('status.title')"
|
||||||
:icon="'ph-building-office'"
|
:icon="'ph-building-office'"
|
||||||
|
|
@ -450,7 +453,7 @@ watch(
|
||||||
style="position: absolute; z-index: 999; top: 0; right: 0"
|
style="position: absolute; z-index: 999; top: 0; right: 0"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
v-if="data.status !== 'INACTIVE'"
|
v-if="data.status !== 'INACTIVE' && !hideAction"
|
||||||
class="surface-1 row rounded"
|
class="surface-1 row rounded"
|
||||||
>
|
>
|
||||||
<UndoButton
|
<UndoButton
|
||||||
|
|
@ -484,7 +487,7 @@ watch(
|
||||||
type="button"
|
type="button"
|
||||||
/>
|
/>
|
||||||
<DeleteButton
|
<DeleteButton
|
||||||
v-if="!isEdit"
|
v-if="!isEdit && !hideDelete"
|
||||||
id="btn-info-basic-delete"
|
id="btn-info-basic-delete"
|
||||||
icon-only
|
icon-only
|
||||||
@click="
|
@click="
|
||||||
|
|
@ -597,6 +600,7 @@ watch(
|
||||||
v-model:on-create-data-list="imageListOnCreate"
|
v-model:on-create-data-list="imageListOnCreate"
|
||||||
v-model:image-url="imageState.imageUrl"
|
v-model:image-url="imageState.imageUrl"
|
||||||
v-model:data-list="imageList"
|
v-model:data-list="imageList"
|
||||||
|
:changeDisabled="hideAction"
|
||||||
:on-create="model"
|
:on-create="model"
|
||||||
:hiddenFooter="!imageState.isImageEdit"
|
:hiddenFooter="!imageState.isImageEdit"
|
||||||
@add-image="addImage"
|
@add-image="addImage"
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,7 @@ import { Icon } from '@iconify/vue/dist/iconify.js';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
|
|
||||||
import { baseUrl } from 'src/stores/utils';
|
import { baseUrl, canAccess } from 'src/stores/utils';
|
||||||
import { useNavigator } from 'src/stores/navigator';
|
import { useNavigator } from 'src/stores/navigator';
|
||||||
import { useInstitution } from 'src/stores/institution';
|
import { useInstitution } from 'src/stores/institution';
|
||||||
import { Institution, InstitutionPayload } from 'src/stores/institution/types';
|
import { Institution, InstitutionPayload } from 'src/stores/institution/types';
|
||||||
|
|
@ -366,6 +366,7 @@ watch(
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
|
v-if="canAccess('agencies', 'edit')"
|
||||||
style="z-index: 999"
|
style="z-index: 999"
|
||||||
hide-icon
|
hide-icon
|
||||||
@click="triggerDialog('add')"
|
@click="triggerDialog('add')"
|
||||||
|
|
@ -750,6 +751,7 @@ watch(
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<KebabAction
|
<KebabAction
|
||||||
|
:hide-delete="!canAccess('agencies', 'edit')"
|
||||||
:id-name="props.row.name"
|
:id-name="props.row.name"
|
||||||
:status="props.row.status"
|
:status="props.row.status"
|
||||||
@view="
|
@view="
|
||||||
|
|
@ -833,6 +835,7 @@ watch(
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<KebabAction
|
<KebabAction
|
||||||
|
:hide-delete="!canAccess('agencies', 'edit')"
|
||||||
:id-name="props.row.id"
|
:id-name="props.row.id"
|
||||||
:status="props.row.status"
|
:status="props.row.status"
|
||||||
@view="
|
@view="
|
||||||
|
|
@ -973,6 +976,7 @@ watch(
|
||||||
@change-status="triggerChangeStatus"
|
@change-status="triggerChangeStatus"
|
||||||
:readonly="!pageState.isDrawerEdit"
|
:readonly="!pageState.isDrawerEdit"
|
||||||
:isEdit="pageState.isDrawerEdit"
|
:isEdit="pageState.isDrawerEdit"
|
||||||
|
:hide-delete="!canAccess('agencies', 'edit')"
|
||||||
v-model="pageState.addModal"
|
v-model="pageState.addModal"
|
||||||
v-model:drawer-model="pageState.viewDrawer"
|
v-model:drawer-model="pageState.viewDrawer"
|
||||||
v-model:data="formData"
|
v-model:data="formData"
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,7 @@ import { column } from './constants';
|
||||||
import useFlowStore from 'src/stores/flow';
|
import useFlowStore from 'src/stores/flow';
|
||||||
import { useRequestList } from 'src/stores/request-list';
|
import { useRequestList } from 'src/stores/request-list';
|
||||||
import { RequestData, RequestDataStatus } from 'src/stores/request-list/types';
|
import { RequestData, RequestDataStatus } from 'src/stores/request-list/types';
|
||||||
import { dialogWarningClose } from 'src/stores/utils';
|
import { dialogWarningClose, canAccess } from 'src/stores/utils';
|
||||||
import { CancelButton, SaveButton } from 'src/components/button';
|
import { CancelButton, SaveButton } from 'src/components/button';
|
||||||
import { getRole } from 'src/services/keycloak';
|
import { getRole } from 'src/services/keycloak';
|
||||||
import FloatingActionButton from 'src/components/FloatingActionButton.vue';
|
import FloatingActionButton from 'src/components/FloatingActionButton.vue';
|
||||||
|
|
@ -473,6 +473,7 @@ watch(
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<TableRequestList
|
<TableRequestList
|
||||||
|
:no-link="!canAccess('customer', 'view')"
|
||||||
:columns="column"
|
:columns="column"
|
||||||
:rows="data"
|
:rows="data"
|
||||||
:grid="pageState.gridView"
|
:grid="pageState.gridView"
|
||||||
|
|
@ -574,6 +575,7 @@ watch(
|
||||||
v-if="requestListActionData"
|
v-if="requestListActionData"
|
||||||
v-model="pageState.requestListActionDialog"
|
v-model="pageState.requestListActionDialog"
|
||||||
:request-list="requestListActionData"
|
:request-list="requestListActionData"
|
||||||
|
:no-link="!canAccess('customer', 'view')"
|
||||||
@submit="submitRequestListAction"
|
@submit="submitRequestListAction"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
|
|
@ -86,7 +86,11 @@ function assignToForm() {
|
||||||
customerDutyCost: attributesForm.value.customerDutyCost ?? 30,
|
customerDutyCost: attributesForm.value.customerDutyCost ?? 30,
|
||||||
companyDuty: attributesForm.value.companyDuty ?? false,
|
companyDuty: attributesForm.value.companyDuty ?? false,
|
||||||
companyDutyCost: attributesForm.value.companyDutyCost ?? 30,
|
companyDutyCost: attributesForm.value.companyDutyCost ?? 30,
|
||||||
responsibleUserLocal: attributesForm.value.responsibleUserLocal ?? true,
|
responsibleUserLocal: attributesForm.value.responsibleUserLocal
|
||||||
|
? attributesForm.value.responsibleUserLocal
|
||||||
|
: props.responsibleAreaDistrictId
|
||||||
|
? false
|
||||||
|
: true,
|
||||||
responsibleUserId:
|
responsibleUserId:
|
||||||
attributesForm.value.responsibleUserId || props.defaultMessenger,
|
attributesForm.value.responsibleUserId || props.defaultMessenger,
|
||||||
individualDuty: attributesForm.value.individualDuty ?? false,
|
individualDuty: attributesForm.value.individualDuty ?? false,
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ import useAddressStore from 'src/stores/address';
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
requestList: RequestData[];
|
requestList: RequestData[];
|
||||||
|
noLink?: boolean;
|
||||||
}>();
|
}>();
|
||||||
|
|
||||||
defineEmits<{
|
defineEmits<{
|
||||||
|
|
@ -99,6 +100,7 @@ watch(
|
||||||
hide-action
|
hide-action
|
||||||
hide-view
|
hide-view
|
||||||
checkable
|
checkable
|
||||||
|
:no-link="noLink"
|
||||||
:list-same-area="listSameArea"
|
:list-same-area="listSameArea"
|
||||||
:columns="column"
|
:columns="column"
|
||||||
:rows="requestList"
|
:rows="requestList"
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import {
|
||||||
getEmployeeName,
|
getEmployeeName,
|
||||||
getCustomerName,
|
getCustomerName,
|
||||||
dialogWarningClose,
|
dialogWarningClose,
|
||||||
|
canAccess,
|
||||||
} from 'src/stores/utils';
|
} from 'src/stores/utils';
|
||||||
import { dateFormatJS } from 'src/utils/datetime';
|
import { dateFormatJS } from 'src/utils/datetime';
|
||||||
import { useRequestList } from 'src/stores/request-list';
|
import { useRequestList } from 'src/stores/request-list';
|
||||||
|
|
@ -459,6 +460,7 @@ async function submitRejectCancel() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function toCustomer(customer: RequestData['quotation']['customerBranch']) {
|
function toCustomer(customer: RequestData['quotation']['customerBranch']) {
|
||||||
|
if (!canAccess('customer', 'view')) return;
|
||||||
const url = new URL(
|
const url = new URL(
|
||||||
`/customer-management?tab=customer&id=${customer.customerId}`,
|
`/customer-management?tab=customer&id=${customer.customerId}`,
|
||||||
window.location.origin,
|
window.location.origin,
|
||||||
|
|
@ -468,6 +470,7 @@ function toCustomer(customer: RequestData['quotation']['customerBranch']) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function toEmployee(employee: RequestData['employee']) {
|
function toEmployee(employee: RequestData['employee']) {
|
||||||
|
if (!canAccess('customer', 'view')) return;
|
||||||
const url = new URL(
|
const url = new URL(
|
||||||
`/customer-management?tab=employee&id=${employee.id}`,
|
`/customer-management?tab=employee&id=${employee.id}`,
|
||||||
window.location.origin,
|
window.location.origin,
|
||||||
|
|
@ -742,7 +745,7 @@ function toEmployee(employee: RequestData['employee']) {
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
<DataDisplay
|
<DataDisplay
|
||||||
clickable
|
:clickable="canAccess('customer', 'view')"
|
||||||
class="col"
|
class="col"
|
||||||
icon="mdi-account-settings-outline"
|
icon="mdi-account-settings-outline"
|
||||||
:label="$t('customer.employer')"
|
:label="$t('customer.employer')"
|
||||||
|
|
@ -755,7 +758,7 @@ function toEmployee(employee: RequestData['employee']) {
|
||||||
@label-click="toCustomer(data.quotation.customerBranch)"
|
@label-click="toCustomer(data.quotation.customerBranch)"
|
||||||
/>
|
/>
|
||||||
<DataDisplay
|
<DataDisplay
|
||||||
clickable
|
:clickable="canAccess('customer', 'view')"
|
||||||
class="col"
|
class="col"
|
||||||
icon="mdi-account-settings-outline"
|
icon="mdi-account-settings-outline"
|
||||||
:label="$t('customer.employee')"
|
:label="$t('customer.employee')"
|
||||||
|
|
@ -864,7 +867,7 @@ function toEmployee(employee: RequestData['employee']) {
|
||||||
requestWorkId: value.id || '',
|
requestWorkId: value.id || '',
|
||||||
},
|
},
|
||||||
value.stepStatus?.[pageState.currentStep - 1]
|
value.stepStatus?.[pageState.currentStep - 1]
|
||||||
?.responsibleUserId,
|
?.responsibleUserId ?? data.defaultMessengerId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
"
|
"
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ const props = withDefaults(
|
||||||
hideView?: boolean;
|
hideView?: boolean;
|
||||||
checkable?: boolean;
|
checkable?: boolean;
|
||||||
listSameArea?: string[];
|
listSameArea?: string[];
|
||||||
|
noLink?: boolean;
|
||||||
}>(),
|
}>(),
|
||||||
{
|
{
|
||||||
row: () => [],
|
row: () => [],
|
||||||
|
|
@ -119,6 +120,7 @@ function getEmployeeName(
|
||||||
}
|
}
|
||||||
|
|
||||||
function toCustomer(customer: RequestData['quotation']['customerBranch']) {
|
function toCustomer(customer: RequestData['quotation']['customerBranch']) {
|
||||||
|
if (props.noLink) return;
|
||||||
const url = new URL(
|
const url = new URL(
|
||||||
`/customer-management?tab=customer&id=${customer.customerId}`,
|
`/customer-management?tab=customer&id=${customer.customerId}`,
|
||||||
window.location.origin,
|
window.location.origin,
|
||||||
|
|
@ -128,6 +130,7 @@ function toCustomer(customer: RequestData['quotation']['customerBranch']) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function toEmployee(employee: RequestData['employee']) {
|
function toEmployee(employee: RequestData['employee']) {
|
||||||
|
if (props.noLink) return;
|
||||||
const url = new URL(
|
const url = new URL(
|
||||||
`/customer-management?tab=employee&id=${employee.id}`,
|
`/customer-management?tab=employee&id=${employee.id}`,
|
||||||
window.location.origin,
|
window.location.origin,
|
||||||
|
|
@ -234,7 +237,7 @@ function handleCheckAll() {
|
||||||
</q-td>
|
</q-td>
|
||||||
<q-td v-if="visibleColumns.includes('employer')" class="text-left">
|
<q-td v-if="visibleColumns.includes('employer')" class="text-left">
|
||||||
<span
|
<span
|
||||||
class="link"
|
:class="{ link: !noLink }"
|
||||||
@click="toCustomer(props.row.quotation.customerBranch)"
|
@click="toCustomer(props.row.quotation.customerBranch)"
|
||||||
>
|
>
|
||||||
{{
|
{{
|
||||||
|
|
@ -246,7 +249,10 @@ function handleCheckAll() {
|
||||||
</span>
|
</span>
|
||||||
</q-td>
|
</q-td>
|
||||||
<q-td v-if="visibleColumns.includes('employee')" class="text-left">
|
<q-td v-if="visibleColumns.includes('employee')" class="text-left">
|
||||||
<span class="link" @click="toEmployee(props.row.employee)">
|
<span
|
||||||
|
:class="{ link: !noLink }"
|
||||||
|
@click="toEmployee(props.row.employee)"
|
||||||
|
>
|
||||||
{{ getEmployeeName(props.row, { locale: $i18n.locale }) || '-' }}
|
{{ getEmployeeName(props.row, { locale: $i18n.locale }) || '-' }}
|
||||||
</span>
|
</span>
|
||||||
</q-td>
|
</q-td>
|
||||||
|
|
@ -403,6 +409,7 @@ function handleCheckAll() {
|
||||||
hide-kebab-delete
|
hide-kebab-delete
|
||||||
:use-cancel="!hideAction"
|
:use-cancel="!hideAction"
|
||||||
class="full-height"
|
class="full-height"
|
||||||
|
:hide-action="hideAction"
|
||||||
:use-reject-cancel="
|
:use-reject-cancel="
|
||||||
props.row.customerRequestCancel && !props.row.rejectRequestCancel
|
props.row.customerRequestCancel && !props.row.rejectRequestCancel
|
||||||
"
|
"
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import { storeToRefs } from 'pinia';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRoute } from 'vue-router';
|
import { useRoute } from 'vue-router';
|
||||||
|
import { canAccess } from 'src/stores/utils';
|
||||||
|
|
||||||
// NOTE: Components
|
// NOTE: Components
|
||||||
import StatCardComponent from 'src/components/StatCardComponent.vue';
|
import StatCardComponent from 'src/components/StatCardComponent.vue';
|
||||||
|
|
@ -172,6 +173,7 @@ watch(
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
|
v-if="canAccess('taskOrder', 'edit') || pageState.isMessenger"
|
||||||
style="z-index: 999"
|
style="z-index: 999"
|
||||||
:hide-icon="!pageState.isMessenger"
|
:hide-icon="!pageState.isMessenger"
|
||||||
@click.stop="
|
@click.stop="
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import {
|
||||||
useRequestList,
|
useRequestList,
|
||||||
RequestWork,
|
RequestWork,
|
||||||
RequestWorkStatus,
|
RequestWorkStatus,
|
||||||
|
RequestDataStatus,
|
||||||
} from 'src/stores/request-list';
|
} from 'src/stores/request-list';
|
||||||
import DialogHeader from 'src/components/dialog/DialogHeader.vue';
|
import DialogHeader from 'src/components/dialog/DialogHeader.vue';
|
||||||
import CancelButton from 'src/components/button/CancelButton.vue';
|
import CancelButton from 'src/components/button/CancelButton.vue';
|
||||||
|
|
@ -192,7 +193,8 @@ function submit() {
|
||||||
s.workStatus ===
|
s.workStatus ===
|
||||||
(props.creditNote
|
(props.creditNote
|
||||||
? RequestWorkStatus.Canceled
|
? RequestWorkStatus.Canceled
|
||||||
: RequestWorkStatus.InProgress),
|
: RequestWorkStatus.InProgress) ||
|
||||||
|
v.request.requestDataStatus === RequestDataStatus.Canceled,
|
||||||
);
|
);
|
||||||
if (curr) {
|
if (curr) {
|
||||||
const task: Task = {
|
const task: Task = {
|
||||||
|
|
@ -387,8 +389,8 @@ function assignTempGroup() {
|
||||||
}
|
}
|
||||||
|
|
||||||
: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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,7 @@ function getEmployeeName(
|
||||||
return (
|
return (
|
||||||
{
|
{
|
||||||
['eng']: `${useOptionStore().mapOption(employee?.namePrefix || '')} ${employee?.firstNameEN} ${employee?.lastNameEN}`,
|
['eng']: `${useOptionStore().mapOption(employee?.namePrefix || '')} ${employee?.firstNameEN} ${employee?.lastNameEN}`,
|
||||||
['tha']: `${useOptionStore().mapOption(employee?.namePrefix || '')} ${employee?.firstName} ${employee?.lastName}`,
|
['tha']: `${useOptionStore().mapOption(employee?.namePrefix || '')} ${employee?.firstName || employee?.firstNameEN} ${employee?.lastName}`,
|
||||||
}[opts?.locale || 'eng'] || '-'
|
}[opts?.locale || 'eng'] || '-'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
import { QTableProps, QTableSlots } from 'quasar';
|
import { QTableProps, QTableSlots } from 'quasar';
|
||||||
|
|
||||||
|
import { canAccess } from 'src/stores/utils';
|
||||||
|
|
||||||
import QuotationCard from 'src/components/05_quotation/QuotationCard.vue';
|
import QuotationCard from 'src/components/05_quotation/QuotationCard.vue';
|
||||||
import BadgeComponent from 'src/components/BadgeComponent.vue';
|
import BadgeComponent from 'src/components/BadgeComponent.vue';
|
||||||
import KebabAction from 'src/components/shared/KebabAction.vue';
|
import KebabAction from 'src/components/shared/KebabAction.vue';
|
||||||
|
|
@ -216,6 +218,7 @@ const emit = defineEmits<{
|
||||||
v-if="
|
v-if="
|
||||||
!receive && props.row.taskOrderStatus === TaskOrderStatus.Pending
|
!receive && props.row.taskOrderStatus === TaskOrderStatus.Pending
|
||||||
"
|
"
|
||||||
|
:hide-delete="!canAccess('related', 'edit')"
|
||||||
:idName="`btn-kebab-${props.row.taskName}`"
|
:idName="`btn-kebab-${props.row.taskName}`"
|
||||||
status="'ACTIVE'"
|
status="'ACTIVE'"
|
||||||
hide-toggle
|
hide-toggle
|
||||||
|
|
@ -264,6 +267,7 @@ const emit = defineEmits<{
|
||||||
:status="$t(taskOrderStatus(props.row.taskOrderStatus, 'status'))"
|
:status="$t(taskOrderStatus(props.row.taskOrderStatus, 'status'))"
|
||||||
:badge-color="taskOrderStatus(props.row.taskOrderStatus, 'color')"
|
:badge-color="taskOrderStatus(props.row.taskOrderStatus, 'color')"
|
||||||
hide-preview
|
hide-preview
|
||||||
|
:hideKebabDelete="!canAccess('related', 'edit')"
|
||||||
:hide-action="
|
:hide-action="
|
||||||
receive || props.row.taskOrderStatus !== TaskOrderStatus.Pending
|
receive || props.row.taskOrderStatus !== TaskOrderStatus.Pending
|
||||||
"
|
"
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { useRoute, useRouter } from 'vue-router';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { api } from 'src/boot/axios';
|
import { api } from 'src/boot/axios';
|
||||||
import { Lang } from 'src/utils/ui';
|
import { Lang } from 'src/utils/ui';
|
||||||
import { baseUrl } from 'stores/utils';
|
import { baseUrl, canAccess } from 'stores/utils';
|
||||||
|
|
||||||
import TaskStatusComponent from '../TaskStatusComponent.vue';
|
import TaskStatusComponent from '../TaskStatusComponent.vue';
|
||||||
import StateButton from 'src/components/button/StateButton.vue';
|
import StateButton from 'src/components/button/StateButton.vue';
|
||||||
|
|
@ -1175,7 +1175,10 @@ watch(
|
||||||
<template #append="{ props: subProps }">
|
<template #append="{ props: subProps }">
|
||||||
<TaskStatusComponent
|
<TaskStatusComponent
|
||||||
:key="subProps.row.id"
|
:key="subProps.row.id"
|
||||||
:no-action="view !== TaskOrderStatus.Validate"
|
:no-action="
|
||||||
|
view !== TaskOrderStatus.Validate &&
|
||||||
|
!canAccess('taskOrder', 'edit')
|
||||||
|
"
|
||||||
type="order"
|
type="order"
|
||||||
:readonly="
|
:readonly="
|
||||||
(() => {
|
(() => {
|
||||||
|
|
|
||||||
|
|
@ -34,7 +34,7 @@ import {
|
||||||
import { RequestWork } from 'src/stores/request-list/types';
|
import { RequestWork } from 'src/stores/request-list/types';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import useOptionStore from 'src/stores/options';
|
import useOptionStore from 'src/stores/options';
|
||||||
import { dialogWarningClose } from 'src/stores/utils';
|
import { dialogWarningClose, canAccess, isRoleInclude } from 'src/stores/utils';
|
||||||
import { useI18n } from 'vue-i18n';
|
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';
|
||||||
|
|
@ -684,6 +684,7 @@ onMounted(async () => {
|
||||||
|
|
||||||
<RefundInformation
|
<RefundInformation
|
||||||
v-if="view === CreditNoteStatus.Pending"
|
v-if="view === CreditNoteStatus.Pending"
|
||||||
|
:readonly="!canAccess('related', 'edit')"
|
||||||
:total="creditNoteData?.value"
|
:total="creditNoteData?.value"
|
||||||
:paid="
|
:paid="
|
||||||
creditNoteData?.paybackStatus === CreditNotePaybackStatus.Done
|
creditNoteData?.paybackStatus === CreditNotePaybackStatus.Done
|
||||||
|
|
@ -727,7 +728,7 @@ onMounted(async () => {
|
||||||
|
|
||||||
<AdditionalFileExpansion
|
<AdditionalFileExpansion
|
||||||
v-if="view !== CreditNoteStatus.Success"
|
v-if="view !== CreditNoteStatus.Success"
|
||||||
:readonly="false"
|
:readonly="isRoleInclude(['sale', 'head_of_sale'])"
|
||||||
v-model:file-data="attachmentData"
|
v-model:file-data="attachmentData"
|
||||||
:transform-url="
|
:transform-url="
|
||||||
async (url: string) => {
|
async (url: string) => {
|
||||||
|
|
@ -871,7 +872,8 @@ onMounted(async () => {
|
||||||
<SaveButton
|
<SaveButton
|
||||||
v-if="
|
v-if="
|
||||||
!creditNoteData ||
|
!creditNoteData ||
|
||||||
creditNoteData?.creditNoteStatus === CreditNoteStatus.Waiting
|
(creditNoteData?.creditNoteStatus === CreditNoteStatus.Waiting &&
|
||||||
|
canAccess('related', 'edit'))
|
||||||
"
|
"
|
||||||
:disabled="taskListGroup.length === 0 || pageState.mode === 'edit'"
|
:disabled="taskListGroup.length === 0 || pageState.mode === 'edit'"
|
||||||
type="submit"
|
type="submit"
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ import useFlowStore from 'src/stores/flow';
|
||||||
import { pageTabs, columns, hslaColors } from './constants';
|
import { pageTabs, columns, hslaColors } from './constants';
|
||||||
import { CreditNoteStatus, useCreditNote } from 'src/stores/credit-note';
|
import { CreditNoteStatus, useCreditNote } from 'src/stores/credit-note';
|
||||||
import TableCreditNote from './TableCreditNote.vue';
|
import TableCreditNote from './TableCreditNote.vue';
|
||||||
import { dialogWarningClose } from 'src/stores/utils';
|
import { dialogWarningClose, canAccess } from 'src/stores/utils';
|
||||||
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
|
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
|
||||||
|
|
||||||
const $q = useQuasar();
|
const $q = useQuasar();
|
||||||
|
|
@ -146,6 +146,7 @@ watch(
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
|
v-if="canAccess('related', 'edit')"
|
||||||
style="z-index: 999"
|
style="z-index: 999"
|
||||||
hide-icon
|
hide-icon
|
||||||
@click.stop="triggerCreateCreditNote()"
|
@click.stop="triggerCreateCreditNote()"
|
||||||
|
|
@ -360,7 +361,10 @@ watch(
|
||||||
<TableCreditNote
|
<TableCreditNote
|
||||||
:grid="pageState.gridView"
|
:grid="pageState.gridView"
|
||||||
:visible-columns="pageState.fieldSelected"
|
:visible-columns="pageState.fieldSelected"
|
||||||
:hide-delete="pageState.currentTab !== CreditNoteStatus.Waiting"
|
:hide-delete="
|
||||||
|
pageState.currentTab !== CreditNoteStatus.Waiting ||
|
||||||
|
!canAccess('related', 'edit')
|
||||||
|
"
|
||||||
@view="(v) => navigateTo({ statusDialog: 'info', creditId: v.id })"
|
@view="(v) => navigateTo({ statusDialog: 'info', creditId: v.id })"
|
||||||
@delete="(v) => triggerDelete(v.id)"
|
@delete="(v) => triggerDelete(v.id)"
|
||||||
>
|
>
|
||||||
|
|
@ -376,6 +380,10 @@ watch(
|
||||||
})
|
})
|
||||||
"
|
"
|
||||||
@delete="() => triggerDelete(item.row.id)"
|
@delete="() => triggerDelete(item.row.id)"
|
||||||
|
:hide-kebab-delete="
|
||||||
|
pageState.currentTab !== CreditNoteStatus.Waiting ||
|
||||||
|
!canAccess('related', 'edit')
|
||||||
|
"
|
||||||
:title="item.row.quotation.workName"
|
:title="item.row.quotation.workName"
|
||||||
:code="item.row.code"
|
:code="item.row.code"
|
||||||
:status="$t(`creditNote.status.${item.row.creditNoteStatus}`)"
|
:status="$t(`creditNote.status.${item.row.creditNoteStatus}`)"
|
||||||
|
|
|
||||||
|
|
@ -172,11 +172,13 @@ const refundOpts = ref<
|
||||||
>
|
>
|
||||||
{{ $t('creditNote.label.refund') }}
|
{{ $t('creditNote.label.refund') }}
|
||||||
<q-btn-dropdown
|
<q-btn-dropdown
|
||||||
|
:disable="readonly"
|
||||||
dense
|
dense
|
||||||
unelevated
|
unelevated
|
||||||
:label="$t(`creditNote.status.payback.${paybackStatus}`)"
|
:label="$t(`creditNote.status.payback.${paybackStatus}`)"
|
||||||
class="text-capitalize text-weight-regular product-status rounded"
|
class="text-capitalize text-weight-regular product-status rounded"
|
||||||
:class="{
|
:class="{
|
||||||
|
'hide-dropdown q-pr-md': readonly,
|
||||||
warning: paybackStatus === CreditNotePaybackStatus.Pending,
|
warning: paybackStatus === CreditNotePaybackStatus.Pending,
|
||||||
danger: paybackStatus === CreditNotePaybackStatus.Verify,
|
danger: paybackStatus === CreditNotePaybackStatus.Verify,
|
||||||
'positive hide-dropdown q-pr-md':
|
'positive hide-dropdown q-pr-md':
|
||||||
|
|
@ -219,7 +221,6 @@ const refundOpts = ref<
|
||||||
<UploadFileSection
|
<UploadFileSection
|
||||||
multiple
|
multiple
|
||||||
:layout="$q.screen.gt.sm ? 'column' : 'row'"
|
:layout="$q.screen.gt.sm ? 'column' : 'row'"
|
||||||
:readonly
|
|
||||||
:label="`${$t('general.upload', { msg: ' E-slip' })} ${$t(
|
:label="`${$t('general.upload', { msg: ' E-slip' })} ${$t(
|
||||||
'general.or',
|
'general.or',
|
||||||
{
|
{
|
||||||
|
|
@ -281,9 +282,9 @@ const refundOpts = ref<
|
||||||
}
|
}
|
||||||
|
|
||||||
:deep(
|
:deep(
|
||||||
.hide-dropdown
|
.hide-dropdown
|
||||||
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>
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,13 @@ import {
|
||||||
import { RequestWork } from 'src/stores/request-list/types';
|
import { RequestWork } from 'src/stores/request-list/types';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import useOptionStore from 'src/stores/options';
|
import useOptionStore from 'src/stores/options';
|
||||||
import { deleteItem, dialog, dialogWarningClose } from 'src/stores/utils';
|
import {
|
||||||
|
canAccess,
|
||||||
|
deleteItem,
|
||||||
|
dialog,
|
||||||
|
dialogWarningClose,
|
||||||
|
isRoleInclude,
|
||||||
|
} from 'src/stores/utils';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { Employee } from 'src/stores/employee/types';
|
import { Employee } from 'src/stores/employee/types';
|
||||||
import QuotationFormWorkerSelect from '../05_quotation/QuotationFormWorkerSelect.vue';
|
import QuotationFormWorkerSelect from '../05_quotation/QuotationFormWorkerSelect.vue';
|
||||||
|
|
@ -1071,6 +1077,7 @@ async function submitAccepted() {
|
||||||
<PaymentForm
|
<PaymentForm
|
||||||
v-if="view === QuotationStatus.PaymentPending"
|
v-if="view === QuotationStatus.PaymentPending"
|
||||||
is-debit-note
|
is-debit-note
|
||||||
|
:readonly="isRoleInclude(['sale', 'head_of_sale'])"
|
||||||
:data="debitNoteData"
|
:data="debitNoteData"
|
||||||
@fetch-status="
|
@fetch-status="
|
||||||
() => {
|
() => {
|
||||||
|
|
@ -1126,7 +1133,6 @@ async function submitAccepted() {
|
||||||
view === QuotationStatus.Issued ||
|
view === QuotationStatus.Issued ||
|
||||||
view === QuotationStatus.Accepted
|
view === QuotationStatus.Accepted
|
||||||
"
|
"
|
||||||
readonly
|
|
||||||
:total-price="summaryPrice.finalPrice"
|
:total-price="summaryPrice.finalPrice"
|
||||||
class="q-mb-md"
|
class="q-mb-md"
|
||||||
v-model:pay-type="currentFormData.payCondition"
|
v-model:pay-type="currentFormData.payCondition"
|
||||||
|
|
@ -1144,7 +1150,7 @@ async function submitAccepted() {
|
||||||
view === QuotationStatus.Accepted ||
|
view === QuotationStatus.Accepted ||
|
||||||
view === QuotationStatus.PaymentPending
|
view === QuotationStatus.PaymentPending
|
||||||
"
|
"
|
||||||
:readonly
|
:readonly="isRoleInclude(['sale', 'head_of_sale'])"
|
||||||
v-model:file-data="attachmentData"
|
v-model:file-data="attachmentData"
|
||||||
:transform-url="
|
:transform-url="
|
||||||
async (url: string) => {
|
async (url: string) => {
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@ import { useNavigator } from 'src/stores/navigator';
|
||||||
import useFlowStore from 'src/stores/flow';
|
import useFlowStore from 'src/stores/flow';
|
||||||
import { pageTabs, columns, hslaColors } from './constants';
|
import { pageTabs, columns, hslaColors } from './constants';
|
||||||
import { DebitNoteStatus, useDebitNote } from 'src/stores/debit-note';
|
import { DebitNoteStatus, useDebitNote } from 'src/stores/debit-note';
|
||||||
import { dialogWarningClose } from 'src/stores/utils';
|
import { canAccess, dialogWarningClose } from 'src/stores/utils';
|
||||||
import { useQuasar } from 'quasar';
|
import { useQuasar } from 'quasar';
|
||||||
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
|
import AdvanceSearch from 'src/components/shared/AdvanceSearch.vue';
|
||||||
|
|
||||||
|
|
@ -163,6 +163,7 @@ watch(
|
||||||
<FloatingActionButton
|
<FloatingActionButton
|
||||||
style="z-index: 999"
|
style="z-index: 999"
|
||||||
hide-icon
|
hide-icon
|
||||||
|
v-if="canAccess('related', 'edit')"
|
||||||
@click.stop="() => triggerCreateDebitNote()"
|
@click.stop="() => triggerCreateDebitNote()"
|
||||||
></FloatingActionButton>
|
></FloatingActionButton>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { DebitNote, useDebitNote } from 'src/stores/debit-note';
|
||||||
|
|
||||||
import { columns } from './constants';
|
import { columns } from './constants';
|
||||||
import KebabAction from 'src/components/shared/KebabAction.vue';
|
import KebabAction from 'src/components/shared/KebabAction.vue';
|
||||||
|
import { canAccess } from 'src/stores/utils';
|
||||||
|
|
||||||
const debitNote = useDebitNote();
|
const debitNote = useDebitNote();
|
||||||
const { data, page, pageSize } = storeToRefs(debitNote);
|
const { data, page, pageSize } = storeToRefs(debitNote);
|
||||||
|
|
@ -80,6 +81,7 @@ const visible = computed(() =>
|
||||||
:id-name="`btn-kebab-${props.row.workName}`"
|
:id-name="`btn-kebab-${props.row.workName}`"
|
||||||
hide-toggle
|
hide-toggle
|
||||||
hide-edit
|
hide-edit
|
||||||
|
:hide-delete="!canAccess('related', 'edit')"
|
||||||
@edit="$emit('edit', props.row)"
|
@edit="$emit('edit', props.row)"
|
||||||
@delete="$emit('delete', props.row)"
|
@delete="$emit('delete', props.row)"
|
||||||
@view="$emit('view', props.row)"
|
@view="$emit('view', props.row)"
|
||||||
|
|
|
||||||
|
|
@ -2,17 +2,15 @@
|
||||||
import MenuItem from 'components/00_home/MenuItem.vue';
|
import MenuItem from 'components/00_home/MenuItem.vue';
|
||||||
import { useNavigator } from 'src/stores/navigator';
|
import { useNavigator } from 'src/stores/navigator';
|
||||||
import { onMounted, ref } from 'vue';
|
import { onMounted, ref } from 'vue';
|
||||||
import { getRole } from 'src/services/keycloak';
|
import { canAccess } from 'src/stores/utils';
|
||||||
|
|
||||||
const navigatorStore = useNavigator();
|
const navigatorStore = useNavigator();
|
||||||
const menu = ref<InstanceType<typeof MenuItem>['$props']['list']>([]);
|
const menu = ref<InstanceType<typeof MenuItem>['$props']['list']>([]);
|
||||||
const role = ref();
|
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
navigatorStore.current.title = '';
|
navigatorStore.current.title = '';
|
||||||
navigatorStore.current.path = [{ text: '' }];
|
navigatorStore.current.path = [{ text: '' }];
|
||||||
|
|
||||||
role.value = getRole();
|
|
||||||
menu.value = [
|
menu.value = [
|
||||||
{
|
{
|
||||||
value: 'branch-management',
|
value: 'branch-management',
|
||||||
|
|
@ -20,14 +18,7 @@ onMounted(() => {
|
||||||
color: 'green',
|
color: 'green',
|
||||||
title: 'menu.branch',
|
title: 'menu.branch',
|
||||||
caption: 'menu.branchCaption',
|
caption: 'menu.branchCaption',
|
||||||
hidden:
|
hidden: !canAccess('branch'),
|
||||||
role.value.includes('admin') ||
|
|
||||||
role.value.includes('branch_admin') ||
|
|
||||||
role.value.includes('head_of_admin') ||
|
|
||||||
role.value.includes('system') ||
|
|
||||||
role.value.includes('owner')
|
|
||||||
? false
|
|
||||||
: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 'personnel-management',
|
value: 'personnel-management',
|
||||||
|
|
@ -35,15 +26,7 @@ onMounted(() => {
|
||||||
color: 'cyan',
|
color: 'cyan',
|
||||||
title: 'menu.user',
|
title: 'menu.user',
|
||||||
caption: 'menu.userCaption',
|
caption: 'menu.userCaption',
|
||||||
hidden:
|
hidden: !canAccess('personnel'),
|
||||||
role.value.includes('admin') ||
|
|
||||||
role.value.includes('branch_admin') ||
|
|
||||||
role.value.includes('head_of_admin') ||
|
|
||||||
role.value.includes('system') ||
|
|
||||||
role.value.includes('owner') ||
|
|
||||||
role.value.includes('branch_manager')
|
|
||||||
? false
|
|
||||||
: true,
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 'customer-management',
|
value: 'customer-management',
|
||||||
|
|
@ -52,6 +35,7 @@ onMounted(() => {
|
||||||
title: 'menu.customer',
|
title: 'menu.customer',
|
||||||
caption: 'menu.customerCaption',
|
caption: 'menu.customerCaption',
|
||||||
isax: true,
|
isax: true,
|
||||||
|
hidden: !canAccess('customer'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: 'product-service',
|
value: 'product-service',
|
||||||
|
|
@ -115,6 +99,7 @@ onMounted(() => {
|
||||||
title: 'menu.dashboard',
|
title: 'menu.dashboard',
|
||||||
caption: 'menu.dashboardCaption',
|
caption: 'menu.dashboardCaption',
|
||||||
isax: true,
|
isax: true,
|
||||||
|
hidden: !canAccess('dashBoard'),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { RouteRecordRaw } from 'vue-router';
|
import { RouteRecordRaw } from 'vue-router';
|
||||||
import { isRoleInclude } from 'stores/utils';
|
import { isRoleInclude, canAccess } from 'stores/utils';
|
||||||
|
|
||||||
const routes: RouteRecordRaw[] = [
|
const routes: RouteRecordRaw[] = [
|
||||||
{
|
{
|
||||||
|
|
@ -16,21 +16,8 @@ const routes: RouteRecordRaw[] = [
|
||||||
path: '/branch-management',
|
path: '/branch-management',
|
||||||
name: 'BranchManagement',
|
name: 'BranchManagement',
|
||||||
beforeEnter: (to, from, next) => {
|
beforeEnter: (to, from, next) => {
|
||||||
if (
|
if (canAccess('branch')) next();
|
||||||
isRoleInclude([
|
else next('/');
|
||||||
'admin',
|
|
||||||
'branch_admin',
|
|
||||||
'head_of_admin',
|
|
||||||
'head_of_account',
|
|
||||||
'system',
|
|
||||||
'owner',
|
|
||||||
'branch_manager',
|
|
||||||
])
|
|
||||||
) {
|
|
||||||
next();
|
|
||||||
} else {
|
|
||||||
next('/');
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
component: () => import('pages/01_branch-management/MainPage.vue'),
|
component: () => import('pages/01_branch-management/MainPage.vue'),
|
||||||
},
|
},
|
||||||
|
|
@ -38,36 +25,36 @@ const routes: RouteRecordRaw[] = [
|
||||||
path: '/personnel-management',
|
path: '/personnel-management',
|
||||||
name: 'PersonnelManagement',
|
name: 'PersonnelManagement',
|
||||||
beforeEnter: (to, from, next) => {
|
beforeEnter: (to, from, next) => {
|
||||||
if (
|
if (canAccess('personnel')) next();
|
||||||
isRoleInclude([
|
else next('/');
|
||||||
'admin',
|
|
||||||
'branch_admin',
|
|
||||||
'head_of_admin',
|
|
||||||
'system',
|
|
||||||
'owner',
|
|
||||||
'branch_manager',
|
|
||||||
])
|
|
||||||
) {
|
|
||||||
next();
|
|
||||||
} else {
|
|
||||||
next('/');
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
component: () => import('pages/02_personnel-management/MainPage.vue'),
|
component: () => import('pages/02_personnel-management/MainPage.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/customer-management',
|
path: '/customer-management',
|
||||||
name: 'CustomerManagement',
|
name: 'CustomerManagement',
|
||||||
|
beforeEnter: (to, from, next) => {
|
||||||
|
if (canAccess('customer')) next();
|
||||||
|
else next('/');
|
||||||
|
},
|
||||||
component: () => import('pages/03_customer-management/MainPage.vue'),
|
component: () => import('pages/03_customer-management/MainPage.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/customer-management/:customerId',
|
path: '/customer-management/:customerId',
|
||||||
name: 'CustomerSpecificManagement',
|
name: 'CustomerSpecificManagement',
|
||||||
|
beforeEnter: (to, from, next) => {
|
||||||
|
if (canAccess('customer')) next();
|
||||||
|
else next('/');
|
||||||
|
},
|
||||||
component: () => import('pages/03_customer-management/MainPage.vue'),
|
component: () => import('pages/03_customer-management/MainPage.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/customer-management/:customerId/branch',
|
path: '/customer-management/:customerId/branch',
|
||||||
name: 'CustomerBranchManagement',
|
name: 'CustomerBranchManagement',
|
||||||
|
beforeEnter: (to, from, next) => {
|
||||||
|
if (canAccess('customer')) next();
|
||||||
|
else next('/');
|
||||||
|
},
|
||||||
component: () => import('pages/03_customer-management/MainPage.vue'),
|
component: () => import('pages/03_customer-management/MainPage.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -78,11 +65,19 @@ const routes: RouteRecordRaw[] = [
|
||||||
{
|
{
|
||||||
path: '/workflow',
|
path: '/workflow',
|
||||||
name: 'Workflow',
|
name: 'Workflow',
|
||||||
|
beforeEnter: (to, from, next) => {
|
||||||
|
if (canAccess('workflow')) next();
|
||||||
|
else next('/');
|
||||||
|
},
|
||||||
component: () => import('pages/04_flow-managment/MainPage.vue'),
|
component: () => import('pages/04_flow-managment/MainPage.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/property',
|
path: '/property',
|
||||||
name: 'Property',
|
name: 'Property',
|
||||||
|
beforeEnter: (to, from, next) => {
|
||||||
|
if (canAccess('workflow')) next();
|
||||||
|
else next('/');
|
||||||
|
},
|
||||||
component: () => import('pages/04_property-managment/MainPage.vue'),
|
component: () => import('pages/04_property-managment/MainPage.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -98,6 +93,10 @@ const routes: RouteRecordRaw[] = [
|
||||||
{
|
{
|
||||||
path: '/agencies-management',
|
path: '/agencies-management',
|
||||||
name: 'agencies-management',
|
name: 'agencies-management',
|
||||||
|
beforeEnter: (to, from, next) => {
|
||||||
|
if (canAccess('agencies')) next();
|
||||||
|
else next('/');
|
||||||
|
},
|
||||||
component: () => import('pages/07_agencies-management/MainPage.vue'),
|
component: () => import('pages/07_agencies-management/MainPage.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -138,6 +137,10 @@ const routes: RouteRecordRaw[] = [
|
||||||
{
|
{
|
||||||
path: '/dash-board',
|
path: '/dash-board',
|
||||||
name: 'dashBoard',
|
name: 'dashBoard',
|
||||||
|
beforeEnter: (to, from, next) => {
|
||||||
|
if (canAccess('dashBoard')) next();
|
||||||
|
else next('/');
|
||||||
|
},
|
||||||
component: () => import('pages/15_dash-board/MainPage.vue'),
|
component: () => import('pages/15_dash-board/MainPage.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -225,7 +228,7 @@ const routes: RouteRecordRaw[] = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/receipt/:id',
|
path: '/receipt/:id',
|
||||||
name: 'receiptform',
|
name: 'receiptForm',
|
||||||
component: () => import('pages/13_receipt/MainPage.vue'),
|
component: () => import('pages/13_receipt/MainPage.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -221,11 +221,99 @@ export function checkTabBeforeAdd(data: unknown[], except?: string[]) {
|
||||||
|
|
||||||
export function isRoleInclude(role2check: string[]): boolean {
|
export function isRoleInclude(role2check: string[]): boolean {
|
||||||
const roles = getRole() ?? [];
|
const roles = getRole() ?? [];
|
||||||
const isIncluded = role2check.some((r) => roles.includes(r));
|
const filterRole = roles.filter(
|
||||||
|
(role: string) =>
|
||||||
|
role !== 'offline_access' &&
|
||||||
|
role !== 'uma_authorization' &&
|
||||||
|
!role.startsWith('default-roles'),
|
||||||
|
);
|
||||||
|
const isIncluded = role2check.some((r) => filterRole.includes(r));
|
||||||
|
|
||||||
return isIncluded;
|
return isIncluded;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const allRoles = [
|
||||||
|
'head_of_admin',
|
||||||
|
'admin',
|
||||||
|
'executive',
|
||||||
|
'accountant',
|
||||||
|
'branch_admin',
|
||||||
|
'branch_manager',
|
||||||
|
'branch_accountant',
|
||||||
|
'head_of_sale',
|
||||||
|
'sale',
|
||||||
|
'data_entry',
|
||||||
|
'document_checker',
|
||||||
|
'messenger',
|
||||||
|
'corporate_customer',
|
||||||
|
'agency',
|
||||||
|
];
|
||||||
|
|
||||||
|
const permissions = {
|
||||||
|
branch: {
|
||||||
|
edit: allRoles.slice(0, 7),
|
||||||
|
view: allRoles.slice(0, 7),
|
||||||
|
},
|
||||||
|
personnel: {
|
||||||
|
edit: allRoles.slice(0, 6).filter((r) => r !== 'accountant'),
|
||||||
|
view: allRoles.slice(0, 6).filter((r) => r !== 'accountant'),
|
||||||
|
},
|
||||||
|
product: {
|
||||||
|
edit: allRoles.slice(0, 7),
|
||||||
|
view: allRoles,
|
||||||
|
},
|
||||||
|
workflow: {
|
||||||
|
edit: allRoles.slice(0, 6),
|
||||||
|
view: allRoles.filter((r) => r !== 'branch_accountant'),
|
||||||
|
},
|
||||||
|
customer: {
|
||||||
|
edit: allRoles.slice(0, 9).filter((r) => r !== 'branch_accountant'),
|
||||||
|
view: allRoles.slice(0, 9),
|
||||||
|
},
|
||||||
|
agencies: {
|
||||||
|
edit: allRoles.slice(0, 7),
|
||||||
|
view: allRoles,
|
||||||
|
},
|
||||||
|
quotation: {
|
||||||
|
edit: allRoles.slice(0, 9).filter((r) => r !== 'branch_accountant'),
|
||||||
|
view: allRoles.slice(0, 9),
|
||||||
|
},
|
||||||
|
taskOrder: {
|
||||||
|
edit: [...allRoles.slice(0, 6), 'data_entry'],
|
||||||
|
view: allRoles,
|
||||||
|
},
|
||||||
|
related: {
|
||||||
|
// ใช้กับหลายเมนู
|
||||||
|
edit: allRoles.slice(0, 6),
|
||||||
|
view: allRoles,
|
||||||
|
},
|
||||||
|
// account: {
|
||||||
|
// edit: allRoles.slice(0, 6),
|
||||||
|
// view: allRoles.slice(0, 7),
|
||||||
|
// },
|
||||||
|
uploadSlip: {
|
||||||
|
edit: allRoles.slice(0, 6),
|
||||||
|
view: allRoles.filter((r) => r !== 'head_of_sale' && r !== 'sale'),
|
||||||
|
},
|
||||||
|
dashBoard: {
|
||||||
|
edit: allRoles.slice(0, 6).filter((r) => r !== 'admin'),
|
||||||
|
view: allRoles.filter((r) => r !== 'admin'),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export function canAccess(
|
||||||
|
menu: keyof typeof permissions,
|
||||||
|
action: 'edit' | 'view' = 'view',
|
||||||
|
): boolean {
|
||||||
|
// uma_authorization = all roles
|
||||||
|
const roles = getRole() ?? [];
|
||||||
|
if (roles.includes('system')) return true;
|
||||||
|
|
||||||
|
const allowedRoles = permissions[menu]?.[action] || [];
|
||||||
|
|
||||||
|
return allowedRoles.some((role: string) => roles.includes(role));
|
||||||
|
}
|
||||||
|
|
||||||
export function resetScrollBar(elementId: string) {
|
export function resetScrollBar(elementId: string) {
|
||||||
const element = document.getElementById(elementId);
|
const element = document.getElementById(elementId);
|
||||||
|
|
||||||
|
|
@ -606,7 +694,7 @@ export function getEmployeeName(
|
||||||
|
|
||||||
return {
|
return {
|
||||||
['eng']: `${typeof employee.namePrefix === 'string' ? useOptionStore().mapOption(employee.namePrefix) : ''} ${employee.firstNameEN} ${employee.lastNameEN}`,
|
['eng']: `${typeof employee.namePrefix === 'string' ? useOptionStore().mapOption(employee.namePrefix) : ''} ${employee.firstNameEN} ${employee.lastNameEN}`,
|
||||||
['tha']: `${typeof employee.namePrefix === 'string' ? useOptionStore().mapOption(employee.namePrefix) : ''} ${employee.firstName} ${employee.lastName}`,
|
['tha']: `${typeof employee.namePrefix === 'string' ? useOptionStore().mapOption(employee.namePrefix) : ''} ${employee.firstName || employee.firstNameEN} ${employee.lastName}`,
|
||||||
}[opts?.locale || 'eng'];
|
}[opts?.locale || 'eng'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue