feat #7311: subsections
This commit is contained in:
parent
0947dcc17e
commit
99ef7fad76
|
@ -1231,13 +1231,13 @@ item/itemType:
|
||||||
zone:
|
zone:
|
||||||
pageTitles:
|
pageTitles:
|
||||||
zones: Zonas
|
zones: Zonas
|
||||||
zonesList: Zonas
|
list: Zonas
|
||||||
deliveryList: Días de entrega
|
deliveryList: Días de entrega
|
||||||
upcomingList: Próximos repartos
|
upcomingList: Próximos repartos
|
||||||
account:
|
account:
|
||||||
pageTitles:
|
pageTitles:
|
||||||
users: Usuarios
|
users: Usuarios
|
||||||
usersList: Usuarios
|
list: Usuarios
|
||||||
roles: Roles
|
roles: Roles
|
||||||
alias: Alias de correo
|
alias: Alias de correo
|
||||||
accounts: Cuentas
|
accounts: Cuentas
|
||||||
|
|
|
@ -0,0 +1,181 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import VnSelect from 'src/components/common/VnSelect.vue';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import FormModel from 'components/FormModel.vue';
|
||||||
|
import VnRow from 'components/ui/VnRow.vue';
|
||||||
|
import VnInput from 'src/components/common/VnInput.vue';
|
||||||
|
import VnInputDate from 'components/common/VnInputDate.vue';
|
||||||
|
|
||||||
|
import axios from 'axios';
|
||||||
|
import { useSession } from 'src/composables/useSession';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { getTokenMultimedia } = useSession();
|
||||||
|
const token = getTokenMultimedia();
|
||||||
|
|
||||||
|
const accountFilter = {
|
||||||
|
fields: [
|
||||||
|
'id',
|
||||||
|
'clientFk',
|
||||||
|
'created',
|
||||||
|
'workerFk',
|
||||||
|
'accountStateFk',
|
||||||
|
'packages',
|
||||||
|
'pickup',
|
||||||
|
],
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
relation: 'client',
|
||||||
|
scope: {
|
||||||
|
fields: ['name'],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const accountStates = ref([]);
|
||||||
|
const accountStatesCopy = ref([]);
|
||||||
|
const optionsList = ref([]);
|
||||||
|
|
||||||
|
const workersOptions = ref([]);
|
||||||
|
|
||||||
|
function setAccountStates(data) {
|
||||||
|
accountStates.value = data;
|
||||||
|
accountStatesCopy.value = data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getEnumValues() {
|
||||||
|
optionsList.value = [{ id: null, description: t('account.basicData.null') }];
|
||||||
|
const { data } = await axios.get(`Applications/get-enum-values`, {
|
||||||
|
params: {
|
||||||
|
schema: 'vn',
|
||||||
|
table: 'account',
|
||||||
|
column: 'pickup',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
for (let value of data)
|
||||||
|
optionsList.value.push({
|
||||||
|
id: value,
|
||||||
|
description: t(`account.basicData.${value}`),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getEnumValues();
|
||||||
|
|
||||||
|
const statesFilter = {
|
||||||
|
options: accountStates,
|
||||||
|
filterFn: (options, value) => {
|
||||||
|
const search = value.toLowerCase();
|
||||||
|
|
||||||
|
if (value === '') return accountStatesCopy.value;
|
||||||
|
|
||||||
|
return options.value.filter((row) => {
|
||||||
|
const description = row.description.toLowerCase();
|
||||||
|
|
||||||
|
return description.indexOf(search) > -1;
|
||||||
|
});
|
||||||
|
},
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="Workers/activeWithInheritedRole"
|
||||||
|
:filter="{ where: { role: 'salesPerson' } }"
|
||||||
|
@on-fetch="(data) => (workersOptions = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData url="AccountStates" @on-fetch="setAccountStates" auto-load />
|
||||||
|
<FormModel
|
||||||
|
:url="`Accounts/${route.params.id}`"
|
||||||
|
:url-update="`Accounts/updateAccount/${route.params.id}`"
|
||||||
|
:filter="accountFilter"
|
||||||
|
model="account"
|
||||||
|
auto-load
|
||||||
|
>
|
||||||
|
<template #form="{ data, validate, filter }">
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnInput
|
||||||
|
v-model="data.client.name"
|
||||||
|
:label="t('account.basicData.customer')"
|
||||||
|
disable
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<VnInputDate
|
||||||
|
v-model="data.created"
|
||||||
|
:label="t('account.basicData.created')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<VnSelect
|
||||||
|
:label="t('account.basicData.assignedTo')"
|
||||||
|
v-model="data.workerFk"
|
||||||
|
:options="workersOptions"
|
||||||
|
option-value="id"
|
||||||
|
option-label="name"
|
||||||
|
emit-value
|
||||||
|
auto-load
|
||||||
|
:rules="validate('account.accountStateFk')"
|
||||||
|
>
|
||||||
|
<template #before>
|
||||||
|
<QAvatar color="orange">
|
||||||
|
<QImg
|
||||||
|
v-if="data.workerFk"
|
||||||
|
:src="`/api/Images/user/160x160/${data.workerFk}/download?access_token=${token}`"
|
||||||
|
spinner-color="white"
|
||||||
|
/>
|
||||||
|
</QAvatar>
|
||||||
|
</template>
|
||||||
|
</VnSelect>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QSelect
|
||||||
|
v-model="data.accountStateFk"
|
||||||
|
:options="accountStates"
|
||||||
|
option-value="id"
|
||||||
|
option-label="description"
|
||||||
|
emit-value
|
||||||
|
:label="t('account.basicData.state')"
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
@filter="(value, update) => filter(value, update, statesFilter)"
|
||||||
|
:rules="validate('account.accountStateFk')"
|
||||||
|
:input-debounce="0"
|
||||||
|
>
|
||||||
|
</QSelect>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
<VnRow class="row q-gutter-md q-mb-md">
|
||||||
|
<div class="col">
|
||||||
|
<QInput
|
||||||
|
v-model.number="data.packages"
|
||||||
|
:label="t('globals.packages')"
|
||||||
|
:rules="validate('account.packages')"
|
||||||
|
type="number"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col">
|
||||||
|
<QSelect
|
||||||
|
v-model="data.pickup"
|
||||||
|
:options="optionsList"
|
||||||
|
option-value="id"
|
||||||
|
option-label="description"
|
||||||
|
emit-value
|
||||||
|
:label="t('account.basicData.pickup')"
|
||||||
|
map-options
|
||||||
|
use-input
|
||||||
|
:input-debounce="0"
|
||||||
|
>
|
||||||
|
</QSelect>
|
||||||
|
</div>
|
||||||
|
</VnRow>
|
||||||
|
</template>
|
||||||
|
</FormModel>
|
||||||
|
</template>
|
|
@ -0,0 +1,15 @@
|
||||||
|
<script setup>
|
||||||
|
import VnCard from 'components/common/VnCard.vue';
|
||||||
|
import AccountDescriptor from './AccountDescriptor.vue';
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<VnCard
|
||||||
|
data-key="Account"
|
||||||
|
base-url="Accounts"
|
||||||
|
:descriptor="AccountDescriptor"
|
||||||
|
searchbar-data-key="AccountList"
|
||||||
|
searchbar-url="Accounts/filter"
|
||||||
|
searchbar-label="Search account"
|
||||||
|
searchbar-info="You can search by account id or customer name"
|
||||||
|
/>
|
||||||
|
</template>
|
|
@ -0,0 +1,216 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed, onMounted } from 'vue';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { toDate, toPercentage } from 'src/filters';
|
||||||
|
import { useState } from 'src/composables/useState';
|
||||||
|
import TicketDescriptorProxy from 'pages/Ticket/Card/TicketDescriptorProxy.vue';
|
||||||
|
import AccountDescriptorMenu from 'pages/Account/Card/AccountDescriptorMenu.vue';
|
||||||
|
import CardDescriptor from 'components/ui/CardDescriptor.vue';
|
||||||
|
import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
|
import useCardDescription from 'src/composables/useCardDescription';
|
||||||
|
import VnUserLink from 'src/components/ui/VnUserLink.vue';
|
||||||
|
import { getUrl } from 'src/composables/getUrl';
|
||||||
|
import ZoneDescriptorProxy from 'src/pages/Zone/Card/ZoneDescriptorProxy.vue';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
required: false,
|
||||||
|
default: null,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const state = useState();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const salixUrl = ref();
|
||||||
|
const entityId = computed(() => {
|
||||||
|
return $props.id || route.params.id;
|
||||||
|
});
|
||||||
|
|
||||||
|
const filter = {
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
relation: 'client',
|
||||||
|
scope: {
|
||||||
|
include: [
|
||||||
|
{ relation: 'salesPersonUser' },
|
||||||
|
{
|
||||||
|
relation: 'accountsRatio',
|
||||||
|
scope: {
|
||||||
|
fields: ['accountingRate'],
|
||||||
|
limit: 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relation: 'accountState',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relation: 'ticket',
|
||||||
|
scope: {
|
||||||
|
include: [
|
||||||
|
{ relation: 'zone' },
|
||||||
|
{
|
||||||
|
relation: 'address',
|
||||||
|
scope: {
|
||||||
|
include: { relation: 'province' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
relation: 'worker',
|
||||||
|
scope: {
|
||||||
|
include: { relation: 'user' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const STATE_COLOR = {
|
||||||
|
pending: 'warning',
|
||||||
|
incomplete: 'info',
|
||||||
|
resolved: 'positive',
|
||||||
|
canceled: 'negative',
|
||||||
|
};
|
||||||
|
function stateColor(code) {
|
||||||
|
return STATE_COLOR[code];
|
||||||
|
}
|
||||||
|
const data = ref(useCardDescription());
|
||||||
|
const setData = (entity) => {
|
||||||
|
if (!entity) return;
|
||||||
|
data.value = useCardDescription(entity.client.name, entity.id);
|
||||||
|
state.set('AccountDescriptor', entity);
|
||||||
|
};
|
||||||
|
onMounted(async () => {
|
||||||
|
salixUrl.value = await getUrl('');
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<CardDescriptor
|
||||||
|
ref="descriptor"
|
||||||
|
:url="`Accounts/${entityId}`"
|
||||||
|
:filter="filter"
|
||||||
|
module="Account"
|
||||||
|
:title="data.title"
|
||||||
|
:subtitle="data.subtitle"
|
||||||
|
@on-fetch="setData"
|
||||||
|
data-key="accountData"
|
||||||
|
>
|
||||||
|
<template #menu="{ entity }">
|
||||||
|
<AccountDescriptorMenu :account="entity" />
|
||||||
|
</template>
|
||||||
|
<template #body="{ entity }">
|
||||||
|
<VnLv v-if="entity.accountState" :label="t('account.card.state')">
|
||||||
|
<template #value>
|
||||||
|
<QBadge
|
||||||
|
:color="stateColor(entity.accountState.code)"
|
||||||
|
text-color="black"
|
||||||
|
dense
|
||||||
|
>
|
||||||
|
{{ entity.accountState.description }}
|
||||||
|
</QBadge>
|
||||||
|
</template>
|
||||||
|
</VnLv>
|
||||||
|
<VnLv :label="t('account.card.created')" :value="toDate(entity.created)" />
|
||||||
|
<VnLv :label="t('account.card.commercial')">
|
||||||
|
<template #value>
|
||||||
|
<VnUserLink
|
||||||
|
:name="entity.client?.salesPersonUser?.name"
|
||||||
|
:worker-id="entity.client?.salesPersonFk"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</VnLv>
|
||||||
|
<VnLv
|
||||||
|
v-if="entity.worker"
|
||||||
|
:label="t('account.card.attendedBy')"
|
||||||
|
:value="entity.worker.user.name"
|
||||||
|
>
|
||||||
|
<template #value>
|
||||||
|
<VnUserLink
|
||||||
|
:name="entity.worker.user.nickname"
|
||||||
|
:worker-id="entity.worker.id"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</VnLv>
|
||||||
|
<VnLv :label="t('account.card.zone')">
|
||||||
|
<template #value>
|
||||||
|
<span class="link">
|
||||||
|
{{ entity.ticket?.zone?.name }}
|
||||||
|
<ZoneDescriptorProxy :id="entity.ticket?.zone?.id" />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</VnLv>
|
||||||
|
<VnLv
|
||||||
|
:label="t('account.card.province')"
|
||||||
|
:value="entity.ticket?.address?.province?.name"
|
||||||
|
/>
|
||||||
|
<VnLv :label="t('account.card.ticketId')">
|
||||||
|
<template #value>
|
||||||
|
<span class="link">
|
||||||
|
{{ entity.ticketFk }}
|
||||||
|
|
||||||
|
<TicketDescriptorProxy :id="entity.ticketFk" />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</VnLv>
|
||||||
|
<VnLv
|
||||||
|
:label="t('accountRate')"
|
||||||
|
:value="toPercentage(entity.client?.accountsRatio?.accountingRate)"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<template #actions="{ entity }">
|
||||||
|
<QCardActions>
|
||||||
|
<QBtn
|
||||||
|
size="md"
|
||||||
|
icon="vn:client"
|
||||||
|
color="primary"
|
||||||
|
:to="{ name: 'CustomerCard', params: { id: entity.clientFk } }"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('account.card.customerSummary') }}</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
<QBtn
|
||||||
|
size="md"
|
||||||
|
icon="vn:ticket"
|
||||||
|
color="primary"
|
||||||
|
:to="{ name: 'TicketCard', params: { id: entity.ticketFk } }"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('account.card.accountedTicket') }}</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
<QBtn
|
||||||
|
size="md"
|
||||||
|
icon="assignment"
|
||||||
|
color="primary"
|
||||||
|
:href="salixUrl + 'ticket/' + entity.ticketFk + '/sale-tracking'"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('account.card.saleTracking') }}</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
<QBtn
|
||||||
|
size="md"
|
||||||
|
icon="visibility"
|
||||||
|
color="primary"
|
||||||
|
:href="salixUrl + 'ticket/' + entity.ticketFk + '/tracking/index'"
|
||||||
|
>
|
||||||
|
<QTooltip>{{ t('account.card.ticketTracking') }}</QTooltip>
|
||||||
|
</QBtn>
|
||||||
|
</QCardActions>
|
||||||
|
</template>
|
||||||
|
</CardDescriptor>
|
||||||
|
</template>
|
||||||
|
<style scoped>
|
||||||
|
.q-item__label {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
accountRate: Claming rate
|
||||||
|
es:
|
||||||
|
accountRate: Ratio de reclamación
|
||||||
|
</i18n>
|
|
@ -0,0 +1,130 @@
|
||||||
|
<script setup>
|
||||||
|
import axios from 'axios';
|
||||||
|
import { ref } from 'vue';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { usePrintService } from 'composables/usePrintService';
|
||||||
|
import SendEmailDialog from 'components/common/SendEmailDialog.vue';
|
||||||
|
import VnConfirm from 'components/ui/VnConfirm.vue';
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
account: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const router = useRouter();
|
||||||
|
const quasar = useQuasar();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { openReport, sendEmail } = usePrintService();
|
||||||
|
|
||||||
|
const account = ref($props.account);
|
||||||
|
|
||||||
|
function openPickupOrder() {
|
||||||
|
const id = account.value.id;
|
||||||
|
openReport(`Accounts/${id}/account-pickup-pdf`, {
|
||||||
|
recipientId: account.value.clientFk,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmPickupOrder() {
|
||||||
|
const customer = account.value.client;
|
||||||
|
quasar.dialog({
|
||||||
|
component: SendEmailDialog,
|
||||||
|
componentProps: {
|
||||||
|
data: {
|
||||||
|
address: customer.email,
|
||||||
|
},
|
||||||
|
promise: sendPickupOrder,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function sendPickupOrder({ address }) {
|
||||||
|
const id = account.value.id;
|
||||||
|
const customer = account.value.client;
|
||||||
|
return sendEmail(`Accounts/${id}/account-pickup-email`, {
|
||||||
|
recipientId: customer.id,
|
||||||
|
recipient: address,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function confirmRemove() {
|
||||||
|
quasar
|
||||||
|
.dialog({
|
||||||
|
component: VnConfirm,
|
||||||
|
componentProps: {
|
||||||
|
title: t('confirmDeletion'),
|
||||||
|
message: t('confirmDeletionMessage'),
|
||||||
|
promise: remove,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.onOk(async () => await router.push({ name: 'AccountList' }));
|
||||||
|
}
|
||||||
|
|
||||||
|
async function remove() {
|
||||||
|
const id = account.value.id;
|
||||||
|
await axios.delete(`Accounts/${id}`);
|
||||||
|
quasar.notify({
|
||||||
|
message: t('globals.dataDeleted'),
|
||||||
|
type: 'positive',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<QItem v-ripple clickable>
|
||||||
|
<QItemSection avatar>
|
||||||
|
<QIcon name="summarize" />
|
||||||
|
</QItemSection>
|
||||||
|
<QItemSection>{{ t('pickupOrder') }}</QItemSection>
|
||||||
|
<QItemSection side>
|
||||||
|
<QIcon name="keyboard_arrow_right" />
|
||||||
|
</QItemSection>
|
||||||
|
<QMenu anchor="top end" self="top start" auto-close>
|
||||||
|
<QList>
|
||||||
|
<QItem @click="openPickupOrder" v-ripple clickable>
|
||||||
|
<QItemSection avatar>
|
||||||
|
<QIcon name="picture_as_pdf" />
|
||||||
|
</QItemSection>
|
||||||
|
<QItemSection>{{ t('openPickupOrder') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
<QItem @click="confirmPickupOrder" v-ripple clickable>
|
||||||
|
<QItemSection avatar>
|
||||||
|
<QIcon name="send" />
|
||||||
|
</QItemSection>
|
||||||
|
<QItemSection>{{ t('sendPickupOrder') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</QList>
|
||||||
|
</QMenu>
|
||||||
|
</QItem>
|
||||||
|
<QSeparator />
|
||||||
|
<QItem @click="confirmRemove()" v-ripple clickable>
|
||||||
|
<QItemSection avatar>
|
||||||
|
<QIcon name="delete" />
|
||||||
|
</QItemSection>
|
||||||
|
<QItemSection>{{ t('deleteAccount') }}</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
{
|
||||||
|
"en": {
|
||||||
|
"pickupOrder": "Pickup order",
|
||||||
|
"openPickupOrder": "Open pickup order",
|
||||||
|
"sendPickupOrder": "Send pickup order",
|
||||||
|
"deleteAccount": "Delete account",
|
||||||
|
"confirmDeletion": "Confirm deletion",
|
||||||
|
"confirmDeletionMessage": "Are you sure you want to delete this account?"
|
||||||
|
},
|
||||||
|
"es": {
|
||||||
|
"pickupOrder": "Orden de recogida",
|
||||||
|
"openPickupOrder": "Abrir orden de recogida",
|
||||||
|
"sendPickupOrder": "Enviar orden de recogida",
|
||||||
|
"deleteAccount": "Eliminar reclamación",
|
||||||
|
"confirmDeletion": "Confirmar eliminación",
|
||||||
|
"confirmDeletionMessage": "Seguro que quieres eliminar esta reclamación?"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</i18n>
|
|
@ -0,0 +1,6 @@
|
||||||
|
<script setup>
|
||||||
|
import VnLog from 'src/components/common/VnLog.vue';
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<VnLog model="Account"></VnLog>
|
||||||
|
</template>
|
|
@ -0,0 +1,256 @@
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import CrudModel from 'components/CrudModel.vue';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnSelect from 'components/common/VnSelect.vue';
|
||||||
|
import { tMobile } from 'composables/tMobile';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const accountDevelopmentForm = ref();
|
||||||
|
const accountReasons = ref([]);
|
||||||
|
const accountResults = ref([]);
|
||||||
|
const accountResponsibles = ref([]);
|
||||||
|
const accountRedeliveries = ref([]);
|
||||||
|
const workers = ref([]);
|
||||||
|
const selected = ref([]);
|
||||||
|
const saveButtonRef = ref();
|
||||||
|
|
||||||
|
const developmentsFilter = {
|
||||||
|
fields: [
|
||||||
|
'id',
|
||||||
|
'accountFk',
|
||||||
|
'accountReasonFk',
|
||||||
|
'accountResultFk',
|
||||||
|
'accountResponsibleFk',
|
||||||
|
'workerFk',
|
||||||
|
'accountRedeliveryFk',
|
||||||
|
],
|
||||||
|
where: {
|
||||||
|
accountFk: route.params.id,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const columns = computed(() => [
|
||||||
|
{
|
||||||
|
name: 'accountReason',
|
||||||
|
label: t('Reason'),
|
||||||
|
field: (row) => row.accountReasonFk,
|
||||||
|
sortable: true,
|
||||||
|
options: accountReasons.value,
|
||||||
|
required: true,
|
||||||
|
model: 'accountReasonFk',
|
||||||
|
optionValue: 'id',
|
||||||
|
optionLabel: 'description',
|
||||||
|
tabIndex: 1,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'accountResult',
|
||||||
|
label: t('Result'),
|
||||||
|
field: (row) => row.accountResultFk,
|
||||||
|
sortable: true,
|
||||||
|
options: accountResults.value,
|
||||||
|
required: true,
|
||||||
|
model: 'accountResultFk',
|
||||||
|
optionValue: 'id',
|
||||||
|
optionLabel: 'description',
|
||||||
|
tabIndex: 2,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'accountResponsible',
|
||||||
|
label: t('Responsible'),
|
||||||
|
field: (row) => row.accountResponsibleFk,
|
||||||
|
sortable: true,
|
||||||
|
options: accountResponsibles.value,
|
||||||
|
required: true,
|
||||||
|
model: 'accountResponsibleFk',
|
||||||
|
optionValue: 'id',
|
||||||
|
optionLabel: 'description',
|
||||||
|
tabIndex: 3,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'worker',
|
||||||
|
label: t('Worker'),
|
||||||
|
field: (row) => row.workerFk,
|
||||||
|
sortable: true,
|
||||||
|
options: workers.value,
|
||||||
|
model: 'workerFk',
|
||||||
|
optionValue: 'id',
|
||||||
|
optionLabel: 'nickname',
|
||||||
|
tabIndex: 4,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'accountRedelivery',
|
||||||
|
label: t('Redelivery'),
|
||||||
|
field: (row) => row.accountRedeliveryFk,
|
||||||
|
sortable: true,
|
||||||
|
options: accountRedeliveries.value,
|
||||||
|
required: true,
|
||||||
|
model: 'accountRedeliveryFk',
|
||||||
|
optionValue: 'id',
|
||||||
|
optionLabel: 'description',
|
||||||
|
tabIndex: 5,
|
||||||
|
align: 'left',
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="AccountReasons"
|
||||||
|
order="description"
|
||||||
|
@on-fetch="(data) => (accountReasons = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="AccountResults"
|
||||||
|
order="description"
|
||||||
|
@on-fetch="(data) => (accountResults = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="AccountResponsibles"
|
||||||
|
order="description"
|
||||||
|
@on-fetch="(data) => (accountResponsibles = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="AccountRedeliveries"
|
||||||
|
order="description"
|
||||||
|
@on-fetch="(data) => (accountRedeliveries = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="Workers/search"
|
||||||
|
:where="{ active: 1 }"
|
||||||
|
order="name ASC"
|
||||||
|
@on-fetch="(data) => (workers = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<CrudModel
|
||||||
|
data-key="AccountDevelopments"
|
||||||
|
url="AccountDevelopments"
|
||||||
|
model="accountDevelopment"
|
||||||
|
:filter="developmentsFilter"
|
||||||
|
ref="accountDevelopmentForm"
|
||||||
|
:data-required="{ accountFk: route.params.id }"
|
||||||
|
v-model:selected="selected"
|
||||||
|
auto-load
|
||||||
|
@save-changes="$router.push(`/account/${route.params.id}/action`)"
|
||||||
|
:default-save="false"
|
||||||
|
>
|
||||||
|
<template #body="{ rows }">
|
||||||
|
<QTable
|
||||||
|
:columns="columns"
|
||||||
|
:rows="rows"
|
||||||
|
row-key="$index"
|
||||||
|
selection="multiple"
|
||||||
|
v-model:selected="selected"
|
||||||
|
:grid="$q.screen.lt.md"
|
||||||
|
table-header-class="text-left"
|
||||||
|
>
|
||||||
|
<template #body-cell="{ row, col }">
|
||||||
|
<QTd
|
||||||
|
auto-width
|
||||||
|
@keyup.ctrl.enter.stop="accountDevelopmentForm.saveChanges()"
|
||||||
|
>
|
||||||
|
<VnSelect
|
||||||
|
v-model="row[col.model]"
|
||||||
|
:options="col.options"
|
||||||
|
:option-value="col.optionValue"
|
||||||
|
:option-label="col.optionLabel"
|
||||||
|
:autofocus="col.tabIndex == 1"
|
||||||
|
input-debounce="0"
|
||||||
|
hide-selected
|
||||||
|
>
|
||||||
|
<template #option="scope" v-if="col.name == 'worker'">
|
||||||
|
<QItem v-bind="scope.itemProps">
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>{{ scope.opt?.name }}</QItemLabel>
|
||||||
|
<QItemLabel caption>
|
||||||
|
{{ scope.opt?.nickname }}
|
||||||
|
{{ scope.opt?.code }}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</template>
|
||||||
|
</VnSelect>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #item="props">
|
||||||
|
<div class="q-pa-xs col-xs-12 col-sm-6 grid-style-transition">
|
||||||
|
<QCard
|
||||||
|
bordered
|
||||||
|
flat
|
||||||
|
@keyup.ctrl.enter.stop="accountDevelopmentForm?.saveChanges()"
|
||||||
|
>
|
||||||
|
<QCardSection>
|
||||||
|
<QCheckbox v-model="props.selected" dense />
|
||||||
|
</QCardSection>
|
||||||
|
<QSeparator />
|
||||||
|
<QList dense>
|
||||||
|
<QItem v-for="col in props.cols" :key="col.name">
|
||||||
|
<QItemSection>
|
||||||
|
<VnSelect
|
||||||
|
:label="col.label"
|
||||||
|
v-model="props.row[col.model]"
|
||||||
|
:options="col.options"
|
||||||
|
:option-value="col.optionValue"
|
||||||
|
:option-label="col.optionLabel"
|
||||||
|
dense
|
||||||
|
input-debounce="0"
|
||||||
|
:autofocus="col.tabIndex == 1"
|
||||||
|
hide-selected
|
||||||
|
/>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</QList>
|
||||||
|
</QCard>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
|
</template>
|
||||||
|
<template #moreAfterActions>
|
||||||
|
<QBtn
|
||||||
|
:label="tMobile('globals.save')"
|
||||||
|
ref="saveButtonRef"
|
||||||
|
color="primary"
|
||||||
|
icon="save"
|
||||||
|
:disable="!accountDevelopmentForm?.hasChanges"
|
||||||
|
@click="accountDevelopmentForm?.onSubmit"
|
||||||
|
:title="t('globals.save')"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</CrudModel>
|
||||||
|
<QPageSticky position="bottom-right" :offset="[25, 25]">
|
||||||
|
<QBtn
|
||||||
|
fab
|
||||||
|
color="primary"
|
||||||
|
icon="add"
|
||||||
|
@keydown.tab.prevent="saveButtonRef.$el.focus()"
|
||||||
|
@click="accountDevelopmentForm.insert()"
|
||||||
|
/>
|
||||||
|
</QPageSticky>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.grid-style-transition {
|
||||||
|
transition: transform 0.28s, background-color 0.28s;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
es:
|
||||||
|
Reason: Motivo
|
||||||
|
Result: Consecuencia
|
||||||
|
Responsible: Responsable
|
||||||
|
Worker: Trabajador
|
||||||
|
Redelivery: Devolución
|
||||||
|
</i18n>
|
|
@ -0,0 +1,354 @@
|
||||||
|
<script setup>
|
||||||
|
import axios from 'axios';
|
||||||
|
import { ref, computed } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useQuasar } from 'quasar';
|
||||||
|
import { useRoute } from 'vue-router';
|
||||||
|
import { useStateStore } from 'stores/useStateStore';
|
||||||
|
import { useArrayData } from 'composables/useArrayData';
|
||||||
|
import { toDate, toCurrency, toPercentage } from 'filters/index';
|
||||||
|
import CrudModel from 'components/CrudModel.vue';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import VnDiscount from 'components/common/vnDiscount.vue';
|
||||||
|
import AccountLinesImport from './AccountLinesImport.vue';
|
||||||
|
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
|
||||||
|
|
||||||
|
const quasar = useQuasar();
|
||||||
|
const route = useRoute();
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const stateStore = useStateStore();
|
||||||
|
const arrayData = useArrayData('AccountLines');
|
||||||
|
const store = arrayData.store;
|
||||||
|
|
||||||
|
const accountFilter = {
|
||||||
|
fields: ['ticketFk'],
|
||||||
|
};
|
||||||
|
const linesFilter = {
|
||||||
|
include: {
|
||||||
|
relation: 'sale',
|
||||||
|
scope: {
|
||||||
|
fields: ['concept', 'ticketFk', 'price', 'quantity', 'discount', 'itemFk'],
|
||||||
|
include: {
|
||||||
|
relation: 'ticket',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const accountLinesForm = ref();
|
||||||
|
const account = ref(null);
|
||||||
|
async function onFetchAccount(data) {
|
||||||
|
account.value = data;
|
||||||
|
fetchMana();
|
||||||
|
}
|
||||||
|
|
||||||
|
const amount = ref();
|
||||||
|
const amountAccounted = ref();
|
||||||
|
async function onFetch(rows, newRows) {
|
||||||
|
if (newRows) rows = newRows;
|
||||||
|
amount.value = 0;
|
||||||
|
amountAccounted.value = 0;
|
||||||
|
if (!rows || !rows.length) return;
|
||||||
|
|
||||||
|
for (const row of rows) {
|
||||||
|
const { sale } = row;
|
||||||
|
amount.value = amount.value + totalRow(sale);
|
||||||
|
const price = row.quantity * sale.price;
|
||||||
|
const discount = (sale.discount * price) / 100;
|
||||||
|
amountAccounted.value = amountAccounted.value + (price - discount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function totalRow({ price, quantity, discount }) {
|
||||||
|
const amount = price * quantity;
|
||||||
|
const appliedDiscount = (discount * amount) / 100;
|
||||||
|
return amount - appliedDiscount;
|
||||||
|
}
|
||||||
|
|
||||||
|
const columns = computed(() => [
|
||||||
|
{
|
||||||
|
name: 'dated',
|
||||||
|
label: t('Delivered'),
|
||||||
|
field: ({ sale: { ticket } }) => toDate(ticket.landed),
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'quantity',
|
||||||
|
label: t('Quantity'),
|
||||||
|
field: ({ sale }) => sale.quantity,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'accounted',
|
||||||
|
label: t('Accounted'),
|
||||||
|
field: (row) => row.quantity,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'description',
|
||||||
|
label: t('Description'),
|
||||||
|
field: ({ sale }) => sale.concept,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'price',
|
||||||
|
label: t('Price'),
|
||||||
|
field: ({ sale }) => sale.price,
|
||||||
|
format: (value) => toCurrency(value),
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'discount',
|
||||||
|
label: t('Discount'),
|
||||||
|
field: ({ sale }) => sale.discount,
|
||||||
|
format: (value) => toPercentage(value / 100),
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'total',
|
||||||
|
label: t('Total'),
|
||||||
|
field: ({ sale }) => totalRow(sale),
|
||||||
|
format: (value) => toCurrency(value),
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const selected = ref([]);
|
||||||
|
const mana = ref(0);
|
||||||
|
async function fetchMana() {
|
||||||
|
const ticketId = account.value.ticketFk;
|
||||||
|
const response = await axios.get(`Tickets/${ticketId}/getSalesPersonMana`);
|
||||||
|
mana.value = response.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function updateDiscount({ saleFk, discount, canceller }) {
|
||||||
|
const body = { salesIds: [saleFk], newDiscount: discount };
|
||||||
|
const accountId = account.value.ticketFk;
|
||||||
|
const query = `Tickets/${accountId}/updateDiscount`;
|
||||||
|
|
||||||
|
await axios.post(query, body, {
|
||||||
|
signal: canceller.signal,
|
||||||
|
});
|
||||||
|
await accountLinesForm.value.reload();
|
||||||
|
}
|
||||||
|
|
||||||
|
function onUpdateDiscount(response) {
|
||||||
|
const row = store.data[response.rowIndex];
|
||||||
|
row.sale.discount = response.discount;
|
||||||
|
quasar.notify({
|
||||||
|
message: t('Discount updated'),
|
||||||
|
type: 'positive',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function showImportDialog() {
|
||||||
|
quasar
|
||||||
|
.dialog({
|
||||||
|
component: AccountLinesImport,
|
||||||
|
componentProps: {
|
||||||
|
ticketId: account.value.ticketFk,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.onOk(() => accountLinesForm.value.reload());
|
||||||
|
}
|
||||||
|
|
||||||
|
async function saveWhenHasChanges() {
|
||||||
|
if (accountLinesForm.value.getChanges().updates) {
|
||||||
|
await accountLinesForm.value.onSubmit();
|
||||||
|
await accountLinesForm.value.reload();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<Teleport to="#st-data" v-if="stateStore.isSubToolbarShown()">
|
||||||
|
<div class="row q-gutter-md">
|
||||||
|
<div>
|
||||||
|
{{ t('Amount') }}
|
||||||
|
<QChip :dense="$q.screen.lt.sm" text-color="white">
|
||||||
|
{{ toCurrency(amount) }}
|
||||||
|
</QChip>
|
||||||
|
</div>
|
||||||
|
<QSeparator dark vertical />
|
||||||
|
<div>
|
||||||
|
{{ t('Amount Accounted') }}
|
||||||
|
<QChip color="positive" :dense="$q.screen.lt.sm">
|
||||||
|
{{ toCurrency(amountAccounted) }}
|
||||||
|
</QChip>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
|
|
||||||
|
<FetchData
|
||||||
|
:url="`Accounts/${route.params.id}`"
|
||||||
|
:filter="accountFilter"
|
||||||
|
@on-fetch="onFetchAccount"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<div class="q-pa-md">
|
||||||
|
<CrudModel
|
||||||
|
data-key="AccountLines"
|
||||||
|
ref="accountLinesForm"
|
||||||
|
:url="`Accounts/${route.params.id}/lines`"
|
||||||
|
save-url="AccountBeginnings/crud"
|
||||||
|
:filter="linesFilter"
|
||||||
|
@on-fetch="onFetch"
|
||||||
|
v-model:selected="selected"
|
||||||
|
:default-save="false"
|
||||||
|
:default-reset="false"
|
||||||
|
auto-load
|
||||||
|
:limit="0"
|
||||||
|
>
|
||||||
|
<template #body="{ rows }">
|
||||||
|
<QTable
|
||||||
|
:columns="columns"
|
||||||
|
:rows="rows"
|
||||||
|
:dense="$q.screen.lt.md"
|
||||||
|
row-key="id"
|
||||||
|
selection="multiple"
|
||||||
|
v-model:selected="selected"
|
||||||
|
:grid="$q.screen.lt.md"
|
||||||
|
>
|
||||||
|
<template #body-cell-accounted="{ row }">
|
||||||
|
<QTd auto-width align="right" class="text-primary">
|
||||||
|
<QInput
|
||||||
|
v-model="row.quantity"
|
||||||
|
type="number"
|
||||||
|
dense
|
||||||
|
@keyup.enter="saveWhenHasChanges()"
|
||||||
|
@blur="saveWhenHasChanges()"
|
||||||
|
/>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-description="{ row, value }">
|
||||||
|
<QTd auto-width align="right" class="text-primary">
|
||||||
|
{{ value }}
|
||||||
|
<ItemDescriptorProxy
|
||||||
|
:id="row.sale.itemFk"
|
||||||
|
></ItemDescriptorProxy>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<template #body-cell-discount="{ row, value, rowIndex }">
|
||||||
|
<QTd auto-width align="right" class="text-primary">
|
||||||
|
{{ value }}
|
||||||
|
<VnDiscount
|
||||||
|
:quantity="row.quantity"
|
||||||
|
:price="row.sale.price"
|
||||||
|
:discount="row.sale.discount"
|
||||||
|
:mana="mana"
|
||||||
|
:promise="updateDiscount"
|
||||||
|
:data="{ saleFk: row.sale.id, rowIndex: rowIndex }"
|
||||||
|
@on-update="onUpdateDiscount"
|
||||||
|
/>
|
||||||
|
</QTd>
|
||||||
|
</template>
|
||||||
|
<!-- View for grid mode -->
|
||||||
|
<template #item="props">
|
||||||
|
<div
|
||||||
|
class="q-mb-md col-12 grid-style-transition"
|
||||||
|
:style="props.selected ? 'transform: scale(0.95);' : ''"
|
||||||
|
>
|
||||||
|
<QCard>
|
||||||
|
<QCardSection>
|
||||||
|
<QCheckbox v-model="props.selected" />
|
||||||
|
</QCardSection>
|
||||||
|
<QSeparator inset />
|
||||||
|
<QList dense>
|
||||||
|
<QItem
|
||||||
|
v-for="column of props.cols"
|
||||||
|
:key="column.name"
|
||||||
|
>
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel caption>
|
||||||
|
{{ column.label }}
|
||||||
|
</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
<QItemSection side>
|
||||||
|
<template v-if="column.name === 'accounted'">
|
||||||
|
<QItemLabel class="text-primary">
|
||||||
|
<QInput
|
||||||
|
v-model="props.row.quantity"
|
||||||
|
type="number"
|
||||||
|
dense
|
||||||
|
autofocus
|
||||||
|
@keyup.enter="
|
||||||
|
saveWhenHasChanges()
|
||||||
|
"
|
||||||
|
@blur="saveWhenHasChanges()"
|
||||||
|
/>
|
||||||
|
</QItemLabel>
|
||||||
|
</template>
|
||||||
|
<template
|
||||||
|
v-else-if="column.name === 'discount'"
|
||||||
|
>
|
||||||
|
<QItemLabel class="text-primary">
|
||||||
|
{{ column.value }}
|
||||||
|
<VnDiscount
|
||||||
|
:quantity="props.row.quantity"
|
||||||
|
:price="props.row.sale.price"
|
||||||
|
:discount="
|
||||||
|
props.row.sale.discount
|
||||||
|
"
|
||||||
|
:mana="mana"
|
||||||
|
:promise="updateDiscount"
|
||||||
|
:data="{
|
||||||
|
saleFk: props.row.sale.id,
|
||||||
|
rowIndex: props.rowIndex,
|
||||||
|
}"
|
||||||
|
@on-update="onUpdateDiscount"
|
||||||
|
/>
|
||||||
|
</QItemLabel>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<QItemLabel>
|
||||||
|
{{ column.value }}
|
||||||
|
</QItemLabel>
|
||||||
|
</template>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</QList>
|
||||||
|
</QCard>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
|
</template>
|
||||||
|
</CrudModel>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<QPageSticky position="bottom-right" :offset="[25, 25]">
|
||||||
|
<QBtn fab color="primary" icon="add" @click="showImportDialog()" />
|
||||||
|
</QPageSticky>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.list {
|
||||||
|
padding-top: 50px;
|
||||||
|
max-width: 900px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
.grid-style-transition {
|
||||||
|
transition: transform 0.28s, background-color 0.28s;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<i18n>
|
||||||
|
en:
|
||||||
|
You are about to remove {count} rows: '
|
||||||
|
You are about to remove <strong>{count}</strong> row |
|
||||||
|
You are about to remove <strong>{count}</strong> rows'
|
||||||
|
es:
|
||||||
|
Delivered: Entregado
|
||||||
|
Quantity: Cantidad
|
||||||
|
Accounted: Reclamada
|
||||||
|
Description: Descripción
|
||||||
|
Price: Precio
|
||||||
|
Discount: Descuento
|
||||||
|
Actions: Acciones
|
||||||
|
Amount: Total
|
||||||
|
Amount Accounted: Cantidad reclamada
|
||||||
|
Delete accounted sales: Eliminar ventas reclamadas
|
||||||
|
Discount updated: Descuento actualizado
|
||||||
|
Accounted quantity: Cantidad reclamada
|
||||||
|
You are about to remove {count} rows: '
|
||||||
|
Vas a eliminar <strong>{count}</strong> línea |
|
||||||
|
Vas a eliminar <strong>{count}</strong> líneas'
|
||||||
|
</i18n>
|
|
@ -0,0 +1,502 @@
|
||||||
|
<script setup>
|
||||||
|
import { onMounted, ref, computed } from 'vue';
|
||||||
|
import { useRoute, useRouter } from 'vue-router';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { toDate, toCurrency } from 'src/filters';
|
||||||
|
import CardSummary from 'components/ui/CardSummary.vue';
|
||||||
|
import FetchData from 'components/FetchData.vue';
|
||||||
|
import { getUrl } from 'src/composables/getUrl';
|
||||||
|
import { useSession } from 'src/composables/useSession';
|
||||||
|
import VnLv from 'src/components/ui/VnLv.vue';
|
||||||
|
import AccountNotes from 'src/pages/Account/Card/AccountNotes.vue';
|
||||||
|
import VnUserLink from 'src/components/ui/VnUserLink.vue';
|
||||||
|
import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
|
||||||
|
import VnTitle from 'src/components/common/VnTitle.vue';
|
||||||
|
import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
|
||||||
|
import axios from 'axios';
|
||||||
|
import dashIfEmpty from 'src/filters/dashIfEmpty';
|
||||||
|
|
||||||
|
const route = useRoute();
|
||||||
|
const router = useRouter();
|
||||||
|
const { t } = useI18n();
|
||||||
|
const { getTokenMultimedia } = useSession();
|
||||||
|
const token = getTokenMultimedia();
|
||||||
|
|
||||||
|
const $props = defineProps({
|
||||||
|
id: {
|
||||||
|
type: Number,
|
||||||
|
default: 0,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const entityId = computed(() => $props.id || route.params.id);
|
||||||
|
const AccountStates = ref([]);
|
||||||
|
const accountUrl = ref();
|
||||||
|
const salixUrl = ref();
|
||||||
|
const accountDmsRef = ref();
|
||||||
|
const accountDmsFilter = ref({
|
||||||
|
include: [
|
||||||
|
{
|
||||||
|
relation: 'dms',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
salixUrl.value = await getUrl('');
|
||||||
|
accountUrl.value = salixUrl.value + `account/${entityId.value}/`;
|
||||||
|
});
|
||||||
|
|
||||||
|
const detailsColumns = ref([
|
||||||
|
{
|
||||||
|
name: 'item',
|
||||||
|
label: 'account.summary.item',
|
||||||
|
field: (row) => row.sale.itemFk,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'landed',
|
||||||
|
label: 'account.summary.landed',
|
||||||
|
field: (row) => row.sale.ticket.landed,
|
||||||
|
format: (value) => toDate(value),
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'quantity',
|
||||||
|
label: 'account.summary.quantity',
|
||||||
|
field: (row) => row.sale.quantity,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'accounted',
|
||||||
|
label: 'account.summary.accounted',
|
||||||
|
field: (row) => row.quantity,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'description',
|
||||||
|
label: 'globals.description',
|
||||||
|
field: (row) => row.sale.concept,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'price',
|
||||||
|
label: 'account.summary.price',
|
||||||
|
field: (row) => row.sale.price,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'discount',
|
||||||
|
label: 'account.summary.discount',
|
||||||
|
field: (row) => row.sale.discount,
|
||||||
|
format: (value) => `${value} %`,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'total',
|
||||||
|
label: 'account.summary.total',
|
||||||
|
field: ({ sale }) =>
|
||||||
|
toCurrency(sale.quantity * sale.price * ((100 - sale.discount) / 100)),
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const STATE_COLOR = {
|
||||||
|
pending: 'warning',
|
||||||
|
incomplete: 'info',
|
||||||
|
resolved: 'positive',
|
||||||
|
canceled: 'negative',
|
||||||
|
};
|
||||||
|
function stateColor(code) {
|
||||||
|
return STATE_COLOR[code];
|
||||||
|
}
|
||||||
|
|
||||||
|
const developmentColumns = ref([
|
||||||
|
{
|
||||||
|
name: 'accountReason',
|
||||||
|
label: 'account.summary.reason',
|
||||||
|
field: (row) => row.accountReason.description,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'accountResult',
|
||||||
|
label: 'account.summary.result',
|
||||||
|
field: (row) => row.accountResult.description,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'accountResponsible',
|
||||||
|
label: 'account.summary.responsible',
|
||||||
|
field: (row) => row.accountResponsible.description,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'worker',
|
||||||
|
label: 'account.summary.worker',
|
||||||
|
field: (row) => row.worker?.user.nickname,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'accountRedelivery',
|
||||||
|
label: 'account.summary.redelivery',
|
||||||
|
field: (row) => row.accountRedelivery.description,
|
||||||
|
sortable: true,
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
const accountDms = ref([]);
|
||||||
|
const multimediaDialog = ref();
|
||||||
|
const multimediaSlide = ref();
|
||||||
|
|
||||||
|
async function getAccountDms() {
|
||||||
|
accountDmsFilter.value.where = { accountFk: entityId.value };
|
||||||
|
await accountDmsRef.value.fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
function setAccountDms(data) {
|
||||||
|
accountDms.value = [];
|
||||||
|
data.forEach((media) => {
|
||||||
|
accountDms.value.push({
|
||||||
|
isVideo: media.dms.contentType == 'video/mp4',
|
||||||
|
url: `/api/Accounts/${media.dmsFk}/downloadFile?access_token=${token}`,
|
||||||
|
dmsFk: media.dmsFk,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function openDialog(dmsId) {
|
||||||
|
multimediaSlide.value = dmsId;
|
||||||
|
multimediaDialog.value = true;
|
||||||
|
}
|
||||||
|
async function changeState(value) {
|
||||||
|
await axios.patch(`Accounts/updateAccount/${entityId.value}`, {
|
||||||
|
accountStateFk: value,
|
||||||
|
});
|
||||||
|
router.go(route.fullPath);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<FetchData
|
||||||
|
url="AccountDms"
|
||||||
|
:filter="accountDmsFilter"
|
||||||
|
@on-fetch="(data) => setAccountDms(data)"
|
||||||
|
ref="accountDmsRef"
|
||||||
|
/>
|
||||||
|
<FetchData
|
||||||
|
url="AccountStates"
|
||||||
|
@on-fetch="(data) => (AccountStates = data)"
|
||||||
|
auto-load
|
||||||
|
/>
|
||||||
|
<CardSummary
|
||||||
|
ref="summary"
|
||||||
|
:url="`Accounts/${entityId}/getSummary`"
|
||||||
|
:entity-id="entityId"
|
||||||
|
@on-fetch="getAccountDms"
|
||||||
|
>
|
||||||
|
<template #header="{ entity: { account } }">
|
||||||
|
{{ account.id }} - {{ account.client.name }} ({{ account.client.id }})
|
||||||
|
</template>
|
||||||
|
<template #header-right>
|
||||||
|
<QBtnDropdown
|
||||||
|
side
|
||||||
|
top
|
||||||
|
color="black"
|
||||||
|
text-color="white"
|
||||||
|
:label="t('ticket.summary.changeState')"
|
||||||
|
>
|
||||||
|
<QList>
|
||||||
|
<QVirtualScroll
|
||||||
|
style="max-height: 300px"
|
||||||
|
:items="AccountStates"
|
||||||
|
separator
|
||||||
|
v-slot="{ item, index }"
|
||||||
|
>
|
||||||
|
<QItem
|
||||||
|
:key="index"
|
||||||
|
dense
|
||||||
|
clickable
|
||||||
|
v-close-popup
|
||||||
|
@click="changeState(item.id)"
|
||||||
|
>
|
||||||
|
<QItemSection>
|
||||||
|
<QItemLabel>{{ item.description }}</QItemLabel>
|
||||||
|
</QItemSection>
|
||||||
|
</QItem>
|
||||||
|
</QVirtualScroll>
|
||||||
|
</QList>
|
||||||
|
</QBtnDropdown>
|
||||||
|
</template>
|
||||||
|
<template #body="{ entity: { account, salesAccounted, developments } }">
|
||||||
|
<QCard class="vn-one">
|
||||||
|
<VnTitle
|
||||||
|
:url="`#/account/${entityId}/basic-data`"
|
||||||
|
:text="t('account.pageTitles.basicData')"
|
||||||
|
/>
|
||||||
|
<VnLv
|
||||||
|
:label="t('account.summary.created')"
|
||||||
|
:value="toDate(account.created)"
|
||||||
|
/>
|
||||||
|
<VnLv :label="t('account.summary.state')">
|
||||||
|
<template #value>
|
||||||
|
<QChip :color="stateColor(account.accountState.code)" dense>
|
||||||
|
{{ account.accountState.description }}
|
||||||
|
</QChip>
|
||||||
|
</template>
|
||||||
|
</VnLv>
|
||||||
|
<VnLv :label="t('globals.salesPerson')">
|
||||||
|
<template #value>
|
||||||
|
<VnUserLink
|
||||||
|
:name="account.client?.salesPersonUser?.name"
|
||||||
|
:worker-id="account.client?.salesPersonFk"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</VnLv>
|
||||||
|
<VnLv :label="t('account.summary.attendedBy')">
|
||||||
|
<template #value>
|
||||||
|
<VnUserLink
|
||||||
|
:name="account.worker?.user?.nickname"
|
||||||
|
:worker-id="account.workerFk"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</VnLv>
|
||||||
|
<VnLv :label="t('account.summary.customer')">
|
||||||
|
<template #value>
|
||||||
|
<span class="link cursor-pointer">
|
||||||
|
{{ account.client?.name }}
|
||||||
|
<CustomerDescriptorProxy :id="account.clientFk" />
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</VnLv>
|
||||||
|
<VnLv
|
||||||
|
:label="t('account.basicData.pickup')"
|
||||||
|
:value="`${dashIfEmpty(account.pickup)}`"
|
||||||
|
/>
|
||||||
|
</QCard>
|
||||||
|
<QCard class="vn-three">
|
||||||
|
<VnTitle
|
||||||
|
:url="`#/account/${entityId}/notes`"
|
||||||
|
:text="t('account.summary.notes')"
|
||||||
|
/>
|
||||||
|
<AccountNotes
|
||||||
|
:id="entityId"
|
||||||
|
:add-note="false"
|
||||||
|
style="max-height: 300px"
|
||||||
|
order="created ASC"
|
||||||
|
/>
|
||||||
|
</QCard>
|
||||||
|
<QCard class="vn-two" v-if="salesAccounted.length > 0">
|
||||||
|
<VnTitle
|
||||||
|
:url="`#/account/${entityId}/lines`"
|
||||||
|
:text="t('account.summary.details')"
|
||||||
|
/>
|
||||||
|
<QTable
|
||||||
|
:columns="detailsColumns"
|
||||||
|
:rows="salesAccounted"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
:rows-per-page-options="[0]"
|
||||||
|
hide-bottom
|
||||||
|
>
|
||||||
|
<template #header="props">
|
||||||
|
<QTr :props="props">
|
||||||
|
<QTh v-for="col in props.cols" :key="col.name" :props="props">
|
||||||
|
{{ t(col.label) }}
|
||||||
|
</QTh>
|
||||||
|
</QTr>
|
||||||
|
</template>
|
||||||
|
<template #body="props">
|
||||||
|
<QTr :props="props">
|
||||||
|
<QTh v-for="col in props.cols" :key="col.name" :props="props">
|
||||||
|
<span v-if="col.name != 'description'">{{
|
||||||
|
t(col.value)
|
||||||
|
}}</span>
|
||||||
|
<QBtn
|
||||||
|
v-if="col.name == 'description'"
|
||||||
|
flat
|
||||||
|
color="blue"
|
||||||
|
>{{ col.value }}</QBtn
|
||||||
|
>
|
||||||
|
<ItemDescriptorProxy
|
||||||
|
v-if="col.name == 'description'"
|
||||||
|
:id="props.row.sale.itemFk"
|
||||||
|
:sale-fk="props.row.saleFk"
|
||||||
|
></ItemDescriptorProxy>
|
||||||
|
</QTh>
|
||||||
|
</QTr>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
|
</QCard>
|
||||||
|
<QCard class="vn-two" v-if="accountDms.length > 0">
|
||||||
|
<VnTitle
|
||||||
|
:url="`#/account/${entityId}/photos`"
|
||||||
|
:text="t('account.summary.photos')"
|
||||||
|
/>
|
||||||
|
<div class="container">
|
||||||
|
<div
|
||||||
|
class="multimedia-container"
|
||||||
|
v-for="(media, index) of accountDms"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
<div class="relative-position">
|
||||||
|
<QIcon
|
||||||
|
name="play_circle"
|
||||||
|
color="primary"
|
||||||
|
size="xl"
|
||||||
|
class="absolute-center zindex"
|
||||||
|
v-if="media.isVideo"
|
||||||
|
@click.stop="openDialog(media.dmsFk)"
|
||||||
|
>
|
||||||
|
<QTooltip>Video</QTooltip>
|
||||||
|
</QIcon>
|
||||||
|
<QCard class="multimedia relative-position">
|
||||||
|
<QImg
|
||||||
|
:src="media.url"
|
||||||
|
class="rounded-borders cursor-pointer fit"
|
||||||
|
@click="openDialog(media.dmsFk)"
|
||||||
|
v-if="!media.isVideo"
|
||||||
|
>
|
||||||
|
</QImg>
|
||||||
|
<video
|
||||||
|
:src="media.url"
|
||||||
|
class="rounded-borders cursor-pointer fit"
|
||||||
|
muted="muted"
|
||||||
|
v-if="media.isVideo"
|
||||||
|
@click="openDialog(media.dmsFk)"
|
||||||
|
/>
|
||||||
|
</QCard>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</QCard>
|
||||||
|
<QCard class="vn-two" v-if="developments.length > 0">
|
||||||
|
<VnTitle
|
||||||
|
:url="accountUrl + 'development'"
|
||||||
|
:text="t('account.summary.development')"
|
||||||
|
/>
|
||||||
|
<QTable
|
||||||
|
:columns="developmentColumns"
|
||||||
|
:rows="developments"
|
||||||
|
flat
|
||||||
|
dense
|
||||||
|
:rows-per-page-options="[0]"
|
||||||
|
hide-bottom
|
||||||
|
>
|
||||||
|
<template #header="props">
|
||||||
|
<QTr :props="props">
|
||||||
|
<QTh v-for="col in props.cols" :key="col.name" :props="props">
|
||||||
|
{{ t(col.label) }}
|
||||||
|
</QTh>
|
||||||
|
</QTr>
|
||||||
|
</template>
|
||||||
|
</QTable>
|
||||||
|
</QCard>
|
||||||
|
<QCard class="vn-max">
|
||||||
|
<VnTitle
|
||||||
|
:url="accountUrl + 'action'"
|
||||||
|
:text="t('account.summary.actions')"
|
||||||
|
/>
|
||||||
|
<div id="slider-container" class="q-px-xl q-py-md">
|
||||||
|
<QSlider
|
||||||
|
v-model="account.responsibility"
|
||||||
|
label
|
||||||
|
:label-value="t('account.summary.responsibility')"
|
||||||
|
label-always
|
||||||
|
color="var()"
|
||||||
|
markers
|
||||||
|
:marker-labels="[
|
||||||
|
{ value: 1, label: t('account.summary.company') },
|
||||||
|
{ value: 5, label: t('account.summary.person') },
|
||||||
|
]"
|
||||||
|
:min="1"
|
||||||
|
:max="5"
|
||||||
|
readonly
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</QCard>
|
||||||
|
<QDialog
|
||||||
|
v-model="multimediaDialog"
|
||||||
|
transition-show="slide-up"
|
||||||
|
transition-hide="slide-down"
|
||||||
|
>
|
||||||
|
<QToolbar class="absolute zindex close-button">
|
||||||
|
<QSpace />
|
||||||
|
<QBtn icon="close" color="primary" round dense v-close-popup />
|
||||||
|
</QToolbar>
|
||||||
|
<QCarousel
|
||||||
|
swipeable
|
||||||
|
animated
|
||||||
|
v-model="multimediaSlide"
|
||||||
|
arrows
|
||||||
|
class="fit"
|
||||||
|
>
|
||||||
|
<QCarouselSlide
|
||||||
|
v-for="media of accountDms"
|
||||||
|
:key="media.dmsFk"
|
||||||
|
:name="media.dmsFk"
|
||||||
|
>
|
||||||
|
<QImg
|
||||||
|
:src="media.url"
|
||||||
|
class="fit"
|
||||||
|
fit="scale-down"
|
||||||
|
v-if="!media.isVideo"
|
||||||
|
/>
|
||||||
|
<video
|
||||||
|
class="q-ma-none fit"
|
||||||
|
v-if="media.isVideo"
|
||||||
|
controls
|
||||||
|
muted
|
||||||
|
autoplay
|
||||||
|
>
|
||||||
|
<source :src="media.url" type="video/mp4" />
|
||||||
|
</video>
|
||||||
|
</QCarouselSlide>
|
||||||
|
</QCarousel>
|
||||||
|
</QDialog>
|
||||||
|
</template>
|
||||||
|
</CardSummary>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.q-dialog__inner--minimized > div {
|
||||||
|
max-width: 80%;
|
||||||
|
}
|
||||||
|
.container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
.multimedia-container {
|
||||||
|
flex: 1 0 21%;
|
||||||
|
}
|
||||||
|
.multimedia {
|
||||||
|
transition: all 0.5s;
|
||||||
|
opacity: 1;
|
||||||
|
height: 250px;
|
||||||
|
|
||||||
|
.q-img {
|
||||||
|
object-fit: cover;
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
video {
|
||||||
|
object-fit: cover;
|
||||||
|
background-color: black;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.multimedia:hover {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.close-button {
|
||||||
|
top: 1%;
|
||||||
|
right: 10%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.zindex {
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.change-state {
|
||||||
|
width: 10%;
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,2 @@
|
||||||
|
Search account: Buscar usuario
|
||||||
|
You can search by account id or customer name: Puedes buscar por id de la reclamación o nombre del cliente
|
|
@ -4,20 +4,28 @@ export default {
|
||||||
path: '/account',
|
path: '/account',
|
||||||
name: 'Account',
|
name: 'Account',
|
||||||
meta: {
|
meta: {
|
||||||
title: 'accounts',
|
title: 'users',
|
||||||
icon: 'vn:account',
|
icon: 'face',
|
||||||
moduleName: 'Account',
|
moduleName: 'Account',
|
||||||
},
|
},
|
||||||
component: RouterView,
|
component: RouterView,
|
||||||
redirect: { name: 'AccountMain' },
|
redirect: { name: 'AccountMain' },
|
||||||
menus: {
|
menus: {
|
||||||
main: ['AccountList', 'AccountDepartment'],
|
main: [
|
||||||
|
'AccountList',
|
||||||
|
'AccountRoles',
|
||||||
|
'AccountAlias',
|
||||||
|
'AccountAccounts',
|
||||||
|
'AccountLdap',
|
||||||
|
'AccountSamba',
|
||||||
|
'AccountConnections',
|
||||||
|
],
|
||||||
card: [
|
card: [
|
||||||
'AccountBasicData',
|
'AccountBasicData',
|
||||||
'AccountNotes',
|
'AccountNotes',
|
||||||
'AccountPda',
|
'AccountMailForwarding',
|
||||||
'AccountNotificationsManager',
|
'AccountPrivileges',
|
||||||
'AccountPBX',
|
'AccountLog',
|
||||||
'AccountLog',
|
'AccountLog',
|
||||||
'AccountCalendar',
|
'AccountCalendar',
|
||||||
'AccountDms',
|
'AccountDms',
|
||||||
|
@ -42,14 +50,69 @@ export default {
|
||||||
component: () => import('src/pages/Account/AccountList.vue'),
|
component: () => import('src/pages/Account/AccountList.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'department',
|
path: 'roles',
|
||||||
name: 'AccountDepartment',
|
name: 'AccountRoles',
|
||||||
meta: {
|
meta: {
|
||||||
title: 'department',
|
title: 'roles',
|
||||||
icon: 'vn:greuge',
|
icon: 'group',
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Account/AccountDepartment.vue'),
|
component: () => import('src/pages/Account/AccountRoles.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: 'alias',
|
||||||
|
name: 'AccountAlias',
|
||||||
|
meta: {
|
||||||
|
title: 'alias',
|
||||||
|
icon: 'email',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Account/AccountAlias.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'accounts',
|
||||||
|
name: 'AccountAccounts',
|
||||||
|
meta: {
|
||||||
|
title: 'accounts',
|
||||||
|
icon: 'accessibility',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Account/AccountAccounts.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'ldap',
|
||||||
|
name: 'AccountLdap',
|
||||||
|
meta: {
|
||||||
|
title: 'ldap',
|
||||||
|
icon: 'account_tree',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Account/AccountLdap.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'samba',
|
||||||
|
name: 'AccountSamba',
|
||||||
|
meta: {
|
||||||
|
title: 'samba',
|
||||||
|
icon: 'preview',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Account/AccountSamba.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'acls',
|
||||||
|
name: 'AccountAcls',
|
||||||
|
meta: {
|
||||||
|
title: 'acls',
|
||||||
|
icon: 'check',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Account/AccountAcls.vue'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'connections',
|
||||||
|
name: 'AccountConnections',
|
||||||
|
meta: {
|
||||||
|
title: 'connections',
|
||||||
|
icon: 'check',
|
||||||
|
},
|
||||||
|
component: () => import('src/pages/Account/AccountConnections.vue'),
|
||||||
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
path: 'create',
|
path: 'create',
|
||||||
name: 'AccountCreate',
|
name: 'AccountCreate',
|
||||||
|
@ -77,8 +140,8 @@ export default {
|
||||||
component: () => import('src/pages/Account/Card/AccountSummary.vue'),
|
component: () => import('src/pages/Account/Card/AccountSummary.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'basic-data',
|
|
||||||
name: 'AccountBasicData',
|
name: 'AccountBasicData',
|
||||||
|
path: 'basic-data',
|
||||||
meta: {
|
meta: {
|
||||||
title: 'basicData',
|
title: 'basicData',
|
||||||
icon: 'vn:settings',
|
icon: 'vn:settings',
|
||||||
|
@ -87,87 +150,34 @@ export default {
|
||||||
import('src/pages/Account/Card/AccountBasicData.vue'),
|
import('src/pages/Account/Card/AccountBasicData.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'notes',
|
name: 'AccountMailForwarding',
|
||||||
name: 'NotesCard',
|
path: 'mailForwarding',
|
||||||
redirect: { name: 'AccountNotes' },
|
|
||||||
children: [
|
|
||||||
{
|
|
||||||
path: '',
|
|
||||||
name: 'AccountNotes',
|
|
||||||
meta: {
|
|
||||||
title: 'notes',
|
|
||||||
icon: 'vn:notes',
|
|
||||||
},
|
|
||||||
component: () =>
|
|
||||||
import('src/pages/Account/Card/AccountNotes.vue'),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'AccountPda',
|
|
||||||
path: 'pda',
|
|
||||||
meta: {
|
meta: {
|
||||||
title: 'pda',
|
title: 'pda',
|
||||||
icon: 'phone_android',
|
icon: 'phone_android',
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Account/Card/AccountPda.vue'),
|
component: () =>
|
||||||
|
import('src/pages/Account/Card/AccountMailForwarding.vue'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'AccountNotificationsManager',
|
name: 'AccountPrivileges',
|
||||||
path: 'notifications',
|
path: 'privileges',
|
||||||
meta: {
|
meta: {
|
||||||
title: 'notifications',
|
title: 'notifications',
|
||||||
icon: 'notifications',
|
icon: 'notifications',
|
||||||
},
|
},
|
||||||
component: () =>
|
component: () =>
|
||||||
import('src/pages/Account/Card/AccountNotificationsManager.vue'),
|
import('src/pages/Account/Card/AccountPrivileges.vue'),
|
||||||
},
|
|
||||||
{
|
|
||||||
path: 'pbx',
|
|
||||||
name: 'AccountPBX',
|
|
||||||
meta: {
|
|
||||||
title: 'pbx',
|
|
||||||
icon: 'vn:pbx',
|
|
||||||
},
|
|
||||||
component: () => import('src/pages/Account/Card/AccountPBX.vue'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'AccountDms',
|
|
||||||
path: 'dms',
|
|
||||||
meta: {
|
|
||||||
title: 'dms',
|
|
||||||
icon: 'cloud_upload',
|
|
||||||
},
|
|
||||||
component: () => import('src/pages/Account/Card/AccountDms.vue'),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'AccountLog',
|
name: 'AccountLog',
|
||||||
path: 'log',
|
path: 'log',
|
||||||
meta: {
|
meta: {
|
||||||
title: 'log',
|
title: 'log',
|
||||||
icon: 'vn:History',
|
icon: 'vn:log',
|
||||||
},
|
},
|
||||||
component: () => import('src/pages/Account/Card/AccountLog.vue'),
|
component: () => import('src/pages/Account/Card/AccountLog.vue'),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: 'AccountCalendar',
|
|
||||||
path: 'calendar',
|
|
||||||
meta: {
|
|
||||||
title: 'calendar',
|
|
||||||
icon: 'calendar_today',
|
|
||||||
},
|
|
||||||
component: () => import('src/pages/Account/Card/AccountCalendar.vue'),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'AccountTimeControl',
|
|
||||||
path: 'time-control',
|
|
||||||
meta: {
|
|
||||||
title: 'timeControl',
|
|
||||||
icon: 'access_time',
|
|
||||||
},
|
|
||||||
component: () =>
|
|
||||||
import('src/pages/Account/Card/AccountTimeControl.vue'),
|
|
||||||
},
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
Loading…
Reference in New Issue