5990-reStyle_summary #82

Merged
alexm merged 7 commits from 5990-reStyle_summary into dev 2023-08-17 05:44:50 +00:00
13 changed files with 947 additions and 1640 deletions

View File

@ -31,7 +31,7 @@ function findMatches(search, item) {
const matches = [];
function findRoute(search, item) {
for (const child of item.children) {
if (search.indexOf(child.name) > -1) {
if (search?.indexOf(child.name) > -1) {
matches.push(child);
} else if (child.children) {
findRoute(search, child);

View File

@ -2,6 +2,8 @@
import { onMounted, ref, watch } from 'vue';
import axios from 'axios';
import SkeletonSummary from 'components/ui/SkeletonSummary.vue';
import VnLv from 'src/components/ui/VnLv.vue';
onMounted(() => fetch());
const entity = ref();
@ -41,15 +43,21 @@ watch(props, async () => {
<template>
<div class="summary container">
<QCard>
<QCard class="cardSummary">
<SkeletonSummary v-if="!entity" />
<template v-if="entity">
<div class="header bg-primary q-pa-sm q-mb-md">
<div class="summaryHeader bg-primary q-pa-md text-weight-bolder">
<slot name="header-left">
<span></span>
</slot>
<slot name="header" :entity="entity">
{{ entity.id }} - {{ entity.name }}
<VnLv :label="`${entity.id} -`" :value="entity.name" />
</slot>
<slot name="header-right">
<span></span>
</slot>
</div>
<div class="body q-pa-md q-mb-md">
<div class="summaryBody row q-mb-md">
<slot name="body" :entity="entity" />
</div>
</template>
@ -63,57 +71,84 @@ watch(props, async () => {
justify-content: center;
}
.summary {
.q-card {
.cardSummary {
width: 100%;
max-width: 1200px;
}
.negative {
color: red;
}
.q-list {
.q-item__label--header {
.summaryHeader {
text-align: center;
font-size: 20px;
display: flex;
justify-content: space-between;
}
.summaryBody {
display: flex;
flex-direction: row;
justify-content: space-evenly;
gap: 15px;
padding: 15px;
a {
color: $primary;
> .q-card.vn-one {
flex: 1;
}
> .q-card.vn-two {
flex: 2;
}
> .q-card.vn-three {
flex: 3;
}
> .q-card.vn-max {
width: 100%;
}
.body > .q-card__section.row {
flex-wrap: wrap;
> .q-card {
width: 100%;
background-color: var(--vn-gray);
padding: 15px;
font-size: 16px;
min-width: 275px;
& > .col {
min-width: 250px;
.vn-label-value {
display: flex;
flex-direction: row;
margin-top: 5px;
.label {
color: var(--vn-label);
width: 10em;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
margin-right: 10px;
}
.value {
color: var(--vn-text);
width: max-content;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
.header {
text-align: center;
font-size: 18px;
color: $primary;
font-weight: bold;
margin-bottom: 25px;
font-size: 20px;
display: inline-block;
}
.header.link:hover {
color: lighten($primary, 20%);
}
}
}
#slider-container {
max-width: 80%;
margin: 0 auto;
.q-slider {
.q-slider__marker-labels:nth-child(1) {
transform: none;
}
.q-slider__marker-labels:nth-child(2) {
transform: none;
left: auto !important;
right: 0%;
@media (max-width: $breakpoint-xs) {
.summaryBody {
padding: 0;
}
}
}
}
.q-dialog .summary {
max-width: 1200px;
}
</style>
<style lang="scss" scoped>
.summaryHeader .vn-label-value {
display: flex;
flex-direction: row;
}
</style>

View File

@ -1,5 +1,7 @@
<script setup>
import { computed } from 'vue';
import { dashIfEmpty } from 'src/filters';
const $props = defineProps({
label: { type: String, default: null },
value: {
@ -7,6 +9,7 @@ const $props = defineProps({
default: null,
},
info: { type: String, default: null },
dash: { type: Boolean, default: true },
});
const isBooleanValue = computed(() => typeof $props.value === 'boolean');
</script>
@ -17,15 +20,18 @@ const isBooleanValue = computed(() => typeof $props.value === 'boolean');
<span>{{ $props.label }}</span>
</slot>
</div>
<div v-if="$props.value || $slots.value" class="value">
<div class="value">
<span v-if="isBooleanValue">
<QIcon
:name="$props.value ? `check` : `close`"
:color="$props.value ? `positive` : `negative`"
size="sm"
/>
</span>
<slot v-else name="value">
<span :title="$props.value">{{ $props.value }}</span>
<span :title="$props.value">
{{ $props.dash ? dashIfEmpty($props.value) : $props.value }}
</span>
</slot>
</div>
<div class="info" v-if="$props.info">

View File

@ -35,4 +35,13 @@ body.body--light {
color: white;
}
}
--vn-text: #000000;
Review

He vist que de esta manera pots difinir variables que canvien segons el modo de color

He vist que de esta manera pots difinir variables que canvien segons el modo de color
--vn-gray: #dddddd;
--vn-label: #5f5f5f;
}
body.body--dark {
--vn-text: #ffffff;
--vn-gray: #313131;
--vn-label: #a8a8a8;
}

View File

@ -47,4 +47,4 @@ $dark: #292929;
$layout-shadow-dark: 0 0 10px 2px rgba(0, 0, 0, 0.2), 0 0px 10px rgba(0, 0, 0, 0.24);
$spacing-md: 16px;
$label-color: rgba(255, 255, 255, 0.6);
$label-color: #ffffff99;

View File

@ -33,6 +33,9 @@ export default {
rowRemoved: 'Row removed',
pleaseWait: 'Please wait...',
noPinnedModules: 'You have dont have any pinned modules',
summary: {
basicData: 'Basic data',
},
},
errors: {
statusUnauthorized: 'Access denied',
@ -154,6 +157,8 @@ export default {
balanceDue: 'Balance due',
balanceDueInfo: 'Deviated invoices minus payments',
recoverySince: 'Recovery since',
businessType: 'Business Type',
city: 'City',
},
basicData: {
socialName: 'Fiscal name',
@ -175,6 +180,7 @@ export default {
basicData: 'Basic Data',
boxing: 'Boxing',
sms: 'Sms',
notes: 'Notes',
},
list: {
nickname: 'Nickname',
@ -244,6 +250,7 @@ export default {
requester: 'Requester',
atender: 'Atender',
request: 'Request',
weight: 'Weight',
goTo: 'Go to',
},
},
@ -411,6 +418,7 @@ export default {
userId: 'User ID',
role: 'Role',
sipExtension: 'Extension',
locker: 'Locker',
},
notificationsManager: {
activeNotifications: 'Active notifications',

View File

@ -33,6 +33,9 @@ export default {
rowRemoved: 'Fila eliminada',
pleaseWait: 'Por favor, espera...',
noPinnedModules: 'No has fijado ningún módulo',
summary: {
basicData: 'Datos básicos',
},
},
errors: {
statusUnauthorized: 'Acceso denegado',
@ -119,7 +122,7 @@ export default {
province: 'Provincia',
country: 'País',
street: 'Calle',
isEqualizated: 'Equalizado',
isEqualizated: 'Recargo de equivalencia',
isActive: 'Activo',
invoiceByAddress: 'Facturar por consignatario',
verifiedData: 'Datos verificados',
@ -153,6 +156,8 @@ export default {
balanceDue: 'Saldo vencido',
balanceDueInfo: 'Facturas fuera de plazo menos recibos',
recoverySince: 'Recobro desde',
businessType: 'Tipo de negocio',
city: 'Población',
},
basicData: {
socialName: 'Nombre fiscal',
@ -174,6 +179,7 @@ export default {
basicData: 'Datos básicos',
boxing: 'Encajado',
sms: 'Sms',
notes: 'Notas',
},
list: {
nickname: 'Alias',
@ -243,6 +249,7 @@ export default {
requester: 'Solicitante',
atender: 'Comprador',
request: 'Petición de compra',
weight: 'Peso',
goTo: 'Ir a',
},
},
@ -411,6 +418,7 @@ export default {
userId: 'ID del usuario',
role: 'Rol',
sipExtension: 'Extensión',
locker: 'Taquilla',
},
notificationsManager: {
activeNotifications: 'Notificaciones activas',

View File

@ -1,12 +1,14 @@
<script setup>
import { ref, computed } from 'vue';
import { onMounted, ref, computed } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { toDate, toCurrency } from 'src/filters';
import CardSummary from 'components/ui/CardSummary.vue';
import WorkerDescriptorProxy from 'pages/Worker/Card/WorkerDescriptorProxy.vue';
import FetchData from 'components/FetchData.vue';
import { getUrl } from 'src/composables/getUrl';
import { useSession } from 'src/composables/useSession';
import WorkerDescriptorProxy from 'pages/Worker/Card/WorkerDescriptorProxy.vue';
import VnLv from 'src/components/ui/VnLv.vue';
const route = useRoute();
const { t } = useI18n();
@ -22,6 +24,14 @@ const $props = defineProps({
const entityId = computed(() => $props.id || route.params.id);
const claimUrl = ref();
const salixUrl = ref();
onMounted(async () => {
salixUrl.value = await getUrl('');
claimUrl.value = salixUrl.value + `claim/${entityId.value}/`;
});
const detailsColumns = ref([
{
name: 'item',
@ -155,78 +165,52 @@ function openDialog(dmsId) {
<template #header="{ entity: { claim } }">
{{ claim.id }} - {{ claim.client.name }}
</template>
<template #body="{ entity: { developments, observations, claim, salesClaimed } }">
<QCardSection class="row q-pa-none q-col-gutter-md">
<div class="col">
<QList>
<QItem>
<QItemSection>
<QItemLabel caption>
{{ t('claim.summary.created') }}
</QItemLabel>
<QItemLabel>{{ toDate(claim.created) }}</QItemLabel>
</QItemSection>
<QItemSection v-if="claim.claimState">
<QItemLabel caption>
{{ t('claim.summary.state') }}
</QItemLabel>
<QItemLabel>
<QChip
:color="stateColor(claim.claimState.code)"
dense
>
<template #body="{ entity: { claim, salesClaimed, developments } }">
<QCard class="vn-one">
<a class="header" :href="`#/claim/${entityId}/basic-data`">
{{ t('claim.pageTitles.basicData') }}
<QIcon name="open_in_new" color="primary" />
</a>
<VnLv
:label="t('claim.summary.created')"
:value="toDate(claim.created)"
/>
<VnLv :label="t('claim.summary.state')">
<template #value>
<QChip :color="stateColor(claim.claimState.code)" dense>
{{ claim.claimState.description }}
</QChip>
</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection v-if="claim.worker && claim.worker.user">
<QItemLabel caption>
{{ t('claim.summary.assignedTo') }}
</QItemLabel>
<QItemLabel>
</template>
</VnLv>
<VnLv :label="t('claim.summary.assignedTo')">
<template #value>
<span class="link">
{{ claim.worker.user.nickname }}
<WorkerDescriptorProxy :id="claim.workerFk" />
</span>
</QItemLabel>
</QItemSection>
<QItemSection
v-if="claim.client && claim.client.salesPersonUser"
>
<QItemLabel caption>
{{ t('claim.summary.attendedBy') }}
</QItemLabel>
<QItemLabel>
</template>
</VnLv>
<VnLv :label="t('claim.summary.attendedBy')">
<template #value>
<span class="link">
{{ claim.client.salesPersonUser.name }}
<WorkerDescriptorProxy
:id="claim.client.salesPersonFk"
/>
<WorkerDescriptorProxy :id="claim.client.salesPersonFk" />
</span>
</QItemLabel>
</QItemSection>
</QItem>
</QList>
</div>
</QCardSection>
<QCardSection class="q-pa-md" v-if="observations.length > 0">
<h6>{{ t('claim.summary.notes') }}</h6>
<div class="note-list" v-for="note in observations" :key="note.id">
<div class="note-caption">
<span
>{{ note.worker.firstName }} {{ note.worker.lastName }}
</span>
<span>{{ toDate(note.created) }}</span>
</div>
<div class="note-text">
<span>{{ note.text }}</span>
</div>
</div>
</QCardSection>
<QCardSection class="q-pa-md" v-if="salesClaimed.length > 0">
<h6>{{ t('claim.summary.details') }}</h6>
</template>
</VnLv>
</QCard>
<QCard class="vn-one">
<a class="header" :href="claimUrl + 'note/index'">
{{ t('claim.summary.notes') }}
<QIcon name="open_in_new" color="primary" />
</a>
<!-- Use VnNotes and maybe VirtualScroll-->
</QCard>
<QCard class="vn-max" v-if="salesClaimed.length > 0">
<a class="header" :href="claimUrl + 'note/index'">
{{ t('claim.summary.details') }}
<QIcon name="open_in_new" color="primary" />
</a>
<QTable :columns="detailsColumns" :rows="salesClaimed" flat>
<template #header="props">
<QTr :props="props">
@ -236,16 +220,19 @@ function openDialog(dmsId) {
</QTr>
</template>
</QTable>
</QCardSection>
<QCardSection class="q-pa-md" v-if="claimDms.length > 0">
<h6>{{ t('claim.summary.photos') }}</h6>
</QCard>
<QCard class="vn-max" v-if="claimDms.length > 0">
<a class="header" :href="`#/claim/${entityId}/photos`">
{{ t('claim.summary.photos') }}
<QIcon name="open_in_new" color="primary" />
</a>
<div class="container">
<div class="multimediaParent bg-transparent">
<div
class="multimedia-container"
v-for="(media, index) of claimDms"
:key="index"
class="relative-position"
>
<div class="relative-position">
<QIcon
name="play_circle"
color="primary"
@ -275,9 +262,12 @@ function openDialog(dmsId) {
</div>
</div>
</div>
</QCardSection>
<QCardSection class="q-pa-md" v-if="developments.length > 0">
<h6>{{ t('claim.summary.development') }}</h6>
</QCard>
<QCard class="vn-two" v-if="developments.length > 0">
<a class="header" :href="claimUrl + 'development'">
{{ t('claim.summary.development') }}
<QIcon name="open_in_new" color="primary" />
</a>
<QTable :columns="developmentColumns" :rows="developments" flat>
<template #header="props">
<QTr :props="props">
@ -287,11 +277,13 @@ function openDialog(dmsId) {
</QTr>
</template>
</QTable>
</QCardSection>
<QCardSection class="q-pa-md">
<h6>{{ t('claim.summary.actions') }}</h6>
<QSeparator />
<div id="slider-container">
</QCard>
<QCard class="vn-max" v-if="developments.length > 0">
<a class="header" :href="claimUrl + 'action'">
{{ t('claim.summary.actions') }}
<QIcon name="open_in_new" color="primary" />
</a>
<div id="slider-container" class="q-px-xl q-py-md">
<QSlider
v-model="claim.responsibility"
label
@ -308,7 +300,21 @@ function openDialog(dmsId) {
readonly
/>
</div>
</QCardSection>
</QCard>
<!-- <QCardSection class="q-pa-md" v-if="observations.length > 0">
<h6>{{ t('claim.summary.notes') }}</h6>
<div class="note-list" v-for="note in observations" :key="note.id">
<div class="note-caption">
<span
>{{ note.worker.firstName }} {{ note.worker.lastName }}
</span>
<span>{{ toDate(note.created) }}</span>
</div>
<div class="note-text">
<span>{{ note.text }}</span>
</div>
</div>
</QCardSection> -->
<QDialog
v-model="multimediaDialog"
transition-show="slide-up"
@ -352,22 +358,19 @@ function openDialog(dmsId) {
</CardSummary>
</template>
<style lang="scss" scoped>
.container {
min-width: 80%;
}
.q-dialog__inner--minimized > div {
max-width: 80%;
}
.multimediaParent {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(500px, 1fr));
grid-auto-rows: auto;
grid-gap: 1rem;
.container {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 15px;
flex-basis: 30%;
}
.multimedia-container {
flex: 1 0 21%;
}
.multimedia {
transition: all 0.5s;
opacity: 1;
@ -395,18 +398,4 @@ function openDialog(dmsId) {
.zindex {
z-index: 1;
}
.note-list {
width: 100%;
border: 0.1rem solid $grey-7;
padding: 0.5rem;
margin-bottom: 0.5rem;
}
.note-caption {
display: flex;
justify-content: space-between;
margin-bottom: 0.5rem;
color: $grey-7;
}
</style>

View File

@ -1,9 +1,11 @@
<script setup>
import { computed, ref } from 'vue';
import { computed, ref, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import { toCurrency, toPercentage, toDate } from 'src/filters';
import CardSummary from 'components/ui/CardSummary.vue';
import { getUrl } from 'src/composables/getUrl';
import VnLv from 'src/components/ui/VnLv.vue';
const route = useRoute();
const { t } = useI18n();
@ -16,8 +18,13 @@ const $props = defineProps({
});
const entityId = computed(() => $props.id || route.params.id);
const summary = ref();
const customer = computed(() => summary.value.entity);
const summary = ref();
const clientUrl = ref();
onMounted(async () => {
clientUrl.value = (await getUrl('client/')) + entityId.value + '/';
});
const balanceDue = computed(() => {
return (
@ -38,7 +45,7 @@ const priceIncreasingRate = computed(() => {
});
const debtWarning = computed(() => {
return customer.value.debt.debt > customer.value.credit ? 'negative' : '';
return customer.value?.debt?.debt > customer.value.credit ? 'negative' : '';
});
const creditWarning = computed(() => {
@ -53,478 +60,212 @@ const creditWarning = computed(() => {
<template>
<CardSummary ref="summary" :url="`Clients/${entityId}/summary`">
<template #body="{ entity }">
<QCardSection class="row q-pa-none QCol-gutter-md">
<div class="col">
<QList dense>
<QItemLabel header class="text-h6">
<QCard class="vn-one">
<a class="header" :href="clientUrl + `basic-data`">
{{ t('customer.summary.basicData') }}
<RouterLink
:to="{
name: 'CustomerBasicData',
params: { id: entity.id },
}"
target="_blank"
>
<QIcon name="open_in_new" />
</RouterLink>
</QItemLabel>
<QSeparator class="q-mb-md" />
<QItem class="row col">
<QItemLabel class="col" caption>
{{ t('customer.summary.customerId') }}
</QItemLabel>
<QItemLabel class="col q-ma-none">
{{ entity.id }}
</QItemLabel>
</QItem>
<QItem class="row col">
<QItemLabel class="col" caption>
{{ t('customer.summary.name') }}
</QItemLabel>
<QItemLabel class="col q-ma-none">
{{ entity.name }}
</QItemLabel>
</QItem>
<QItem class="row col">
<QItemLabel class="col" caption>
{{ t('customer.summary.contact') }}
</QItemLabel>
<QItemLabel class="col q-ma-none">
{{ entity.contact }}
</QItemLabel>
</QItem>
<QItem v-if="entity.salesPersonUser" class="row col">
<QItemLabel class="col" caption>
{{ t('customer.summary.salesPerson') }}
</QItemLabel>
<QItemLabel class="col q-ma-none">
{{ entity.salesPersonUser.name }}
</QItemLabel>
</QItem>
<QItem class="row col">
<QItemLabel class="col" caption>
{{ t('customer.summary.phone') }}
</QItemLabel>
<QItemLabel class="col q-ma-none">
{{ entity.phone }}
</QItemLabel>
</QItem>
<QItem class="row col">
<QItemLabel class="col" caption>
{{ t('customer.summary.mobile') }}
</QItemLabel>
<QItemLabel class="col q-ma-none">{{
entity.mobile
}}</QItemLabel>
</QItem>
<QItem v-if="entity.contactChannel" class="row col">
<QItemLabel class="col" caption>
{{ t('customer.summary.contactChannel') }}
</QItemLabel>
<QItemLabel class="col q-ma-none">
{{ entity.contactChannel.name }}
</QItemLabel>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>
{{ t('customer.summary.email') }}
</QItemLabel>
<QItemLabel>{{ entity.email }}</QItemLabel>
</QItemSection>
</QItem>
</QList>
</div>
<div class="col">
<QList>
<QItemLabel header class="text-h6">
<QIcon name="open_in_new" color="primary" />
</a>
<VnLv :label="t('customer.summary.customerId')" :value="entity.id" />
<VnLv :label="t('customer.summary.name')" :value="entity.name" />
<VnLv :label="t('customer.summary.contact')" :value="entity.contact" />
<VnLv :label="t('customer.summary.phone')" :value="entity.phone" />
<VnLv :label="t('customer.summary.mobile')" :value="entity.mobile" />
<VnLv :label="t('customer.summary.email')" :value="entity.email" />
<VnLv
:label="t('customer.summary.salesPerson')"
:value="entity?.salesPersonUser?.name"
/>
<VnLv
:label="t('customer.summary.contactChannel')"
:value="entity?.contactChannel?.name"
/>
<VnLv
:label="t('customer.summary.businessType')"
:value="entity.businessType.description"
/>
</QCard>
<QCard class="vn-one">
<a class="header" :href="clientUrl + `fiscal-data`">
{{ t('customer.summary.fiscalAddress') }}
</QItemLabel>
<QItem>
<QItemSection>
<QItemLabel caption>
{{ t('customer.summary.socialName') }}
</QItemLabel>
<QItemLabel>{{ entity.socialName }}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>
{{ t('customer.summary.fiscalId') }}
</QItemLabel>
<QItemLabel>{{ entity.fi }}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>
{{ t('customer.summary.postcode') }}
</QItemLabel>
<QItemLabel>{{ entity.postcode }}</QItemLabel>
</QItemSection>
</QItem>
<QItem v-if="entity.province">
<QItemSection>
<QItemLabel caption>
{{ t('customer.summary.province') }}
</QItemLabel>
<QItemLabel>{{ entity.province.name }}</QItemLabel>
</QItemSection>
</QItem>
<QItem v-if="entity.country">
<QItemSection>
<QItemLabel caption>
{{ t('customer.summary.country') }}
</QItemLabel>
<QItemLabel>{{ entity.country.country }}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>
{{ t('customer.summary.street') }}
</QItemLabel>
<QItemLabel>{{ entity.street }}</QItemLabel>
</QItemSection>
</QItem>
</QList>
</div>
<div class="col">
<QList>
<QItemLabel header class="text-h6">
{{ t('customer.summary.fiscalData') }}
</QItemLabel>
<QItem dense>
<QCheckbox
v-model="entity.isEqualizated"
<QIcon name="open_in_new" color="primary" />
</a>
<VnLv
:label="t('customer.summary.socialName')"
:value="entity.socialName"
/>
<VnLv :label="t('customer.summary.fiscalId')" :value="entity.fi" />
<VnLv :label="t('customer.summary.city')" :value="entity.city" />
<VnLv :label="t('customer.summary.postcode')" :value="entity.postcode" />
<VnLv
v-if="entity.province"
:label="t('customer.summary.province')"
:value="entity.province.name"
/>
<VnLv
v-if="entity.country"
:label="t('customer.summary.country')"
:value="entity.country.country"
/>
<VnLv :label="t('customer.summary.street')" :value="entity.street" />
</QCard>
<QCard class="vn-one">
<a class="header link" :href="clientUrl + `fiscal-data`" link>
{{ t('customer.summary.fiscalAddress') }}
<QIcon name="open_in_new" color="primary" />
</a>
<VnLv
:label="t('customer.summary.isEqualizated')"
disable
:value="entity.isEqualizated"
/>
</QItem>
<QItem dense>
<QCheckbox
v-model="entity.isActive"
:label="t('customer.summary.isActive')"
disable
/>
</QItem>
<QItem dense>
<QCheckbox
v-model="entity.hasToInvoiceByAddress"
<VnLv :label="t('customer.summary.isActive')" :value="entity.isActive" />
<VnLv
:label="t('customer.summary.invoiceByAddress')"
disable
:value="entity.hasToInvoiceByAddress"
/>
</QItem>
<QItem dense>
<QCheckbox
v-model="entity.isTaxDataChecked"
<VnLv
:label="t('customer.summary.verifiedData')"
disable
:value="entity.isTaxDataChecked"
/>
</QItem>
<QItem dense>
<QCheckbox
v-model="entity.hasToInvoice"
<VnLv
:label="t('customer.summary.hasToInvoice')"
disable
:value="entity.hasToInvoice"
/>
</QItem>
<QItem dense>
<QCheckbox
v-model="entity.isToBeMailed"
<VnLv
:label="t('customer.summary.notifyByEmail')"
disable
:value="entity.isToBeMailed"
/>
</QItem>
<QItem dense>
<QCheckbox
v-model="entity.isVies"
:label="t('customer.summary.vies')"
disable
/>
</QItem>
</QList>
</div>
<div class="col">
<QList>
<QItemLabel header class="text-h6">
<VnLv :label="t('customer.summary.vies')" :value="entity.isVies" />
</QCard>
<QCard class="vn-one">
<a class="header link" :href="clientUrl + `billing-data`" link>
{{ t('customer.summary.billingData') }}
</QItemLabel>
<QItem>
<QItemSection>
<QItemLabel caption>
{{ t('customer.summary.payMethod') }}
</QItemLabel>
<QItemLabel>{{ entity.payMethod.name }}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>
{{ t('customer.summary.bankAccount') }}
</QItemLabel>
<QItemLabel>{{ entity.iban }}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>
{{ t('customer.summary.dueDay') }}
</QItemLabel>
<QItemLabel>{{ entity.dueDay }}</QItemLabel>
</QItemSection>
</QItem>
<QItem dense>
<QCheckbox
v-model="entity.hasLcr"
:label="t('customer.summary.hasLcr')"
disable
<QIcon name="open_in_new" color="primary" />
</a>
<VnLv
:label="t('customer.summary.payMethod')"
:value="entity.payMethod.name"
/>
</QItem>
<QItem dense>
<QCheckbox
v-model="entity.hasCoreVnl"
<VnLv :label="t('customer.summary.bankAccount')" :value="entity.iban" />
<VnLv :label="t('customer.summary.dueDay')" :value="entity.dueDay" />
<VnLv :label="t('customer.summary.hasLcr')" :value="entity.hasLcr" />
<VnLv
:label="t('customer.summary.hasCoreVnl')"
disable
:value="entity.hasCoreVnl"
/>
</QItem>
<QItem dense>
<QCheckbox
v-model="entity.hasSepaVnl"
<VnLv
:label="t('customer.summary.hasB2BVnl')"
disable
:value="entity.hasSepaVnl"
/>
</QItem>
</QList>
</div>
<div class="col" v-if="entity.defaultAddress">
<QList>
<QItemLabel header class="text-h6">
</QCard>
<QCard class="vn-one" v-if="entity.defaultAddress">
<a class="header link" :href="clientUrl + `address/index`" link>
{{ t('customer.summary.consignee') }}
</QItemLabel>
<QItem>
<QItemSection>
<QItemLabel caption>
{{ t('customer.summary.addressName') }}
</QItemLabel>
<QItemLabel>
{{ entity.defaultAddress.nickname }}
</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>
{{ t('customer.summary.addressCity') }}
</QItemLabel>
<QItemLabel>{{ entity.defaultAddress.city }}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>
{{ t('customer.summary.addressStreet') }}
</QItemLabel>
<QItemLabel>
{{ entity.defaultAddress.street }}
</QItemLabel>
</QItemSection>
</QItem>
</QList>
</div>
<div class="col" v-if="entity.account">
<QList>
<QItemLabel header class="text-h6">
{{ t('customer.summary.webAccess') }}
</QItemLabel>
<QItem>
<QItemSection>
<QItemLabel caption>
{{ t('customer.summary.username') }}
</QItemLabel>
<QItemLabel>{{ entity.account.name }}</QItemLabel>
</QItemSection>
</QItem>
<QItem dense>
<QCheckbox
v-model="entity.account.active"
:label="t('customer.summary.webAccess')"
disable
<QIcon name="open_in_new" color="primary" />
</a>
<VnLv
:label="t('customer.summary.addressName')"
:value="entity.defaultAddress.nickname"
/>
</QItem>
</QList>
</div>
<div class="col">
<QList>
<QItemLabel header class="text-h6">
<VnLv
:label="t('customer.summary.addressCity')"
:value="entity.defaultAddress.city"
/>
<VnLv
:label="t('customer.summary.addressStreet')"
:value="entity.defaultAddress.street"
/>
</QCard>
<QCard class="vn-one" v-if="entity.account">
<a class="header link" :href="clientUrl + `web-access`">
{{ t('customer.summary.webAccess') }}
<QIcon name="open_in_new" color="primary" />
</a>
<VnLv
:label="t('customer.summary.username')"
:value="entity.account.name"
/>
<VnLv
:label="t('customer.summary.webAccess')"
:value="entity.account.active"
/>
</QCard>
<QCard class="vn-one" v-if="entity.account">
<div class="header">
{{ t('customer.summary.businessData') }}
</QItemLabel>
<QItem>
<QItemSection>
<QItemLabel caption>
{{ t('customer.summary.totalGreuge') }}
</QItemLabel>
<QItemLabel>
{{ toCurrency(entity.totalGreuge) }}
</QItemLabel>
</QItemSection>
</QItem>
<QItem v-if="entity.mana">
<QItemSection>
<QItemLabel caption>
{{ t('customer.summary.mana') }}
</QItemLabel>
<QItemLabel>
{{ toCurrency(entity.mana.mana) }}
</QItemLabel>
</QItemSection>
</QItem>
<QItem v-if="entity.claimsRatio">
<QItemSection>
<QItemLabel caption>
{{ t('customer.summary.priceIncreasingRate') }}
</QItemLabel>
<QItemLabel>
{{ toPercentage(priceIncreasingRate) }}
</QItemLabel>
</QItemSection>
</QItem>
<QItem v-if="entity.averageInvoiced">
<QItemSection>
<QItemLabel caption>
{{ t('customer.summary.averageInvoiced') }}
</QItemLabel>
<QItemLabel>
{{ toCurrency(entity.averageInvoiced.invoiced) }}
</QItemLabel>
</QItemSection>
</QItem>
<QItem v-if="entity.claimsRatio">
<QItemSection>
<QItemLabel caption>
{{ t('customer.summary.claimRate') }}
</QItemLabel>
<QItemLabel>{{ toPercentage(claimRate) }}</QItemLabel>
</QItemSection>
</QItem>
</QList>
</div>
<div class="col">
<QList>
<QItemLabel header class="text-h6">
<VnLv
:label="t('customer.summary.totalGreuge')"
:value="toCurrency(entity.totalGreuge)"
/>
<VnLv
:label="t('customer.summary.mana')"
:value="toCurrency(entity?.mana?.mana)"
/>
<VnLv
v-if="entity.claimsRatio"
:label="t('customer.summary.priceIncreasingRate')"
:value="toPercentage(priceIncreasingRate)"
/>
<VnLv
:label="t('customer.summary.averageInvoiced')"
:value="toCurrency(entity?.averageInvoiced?.invoiced)"
/>
<VnLv
v-if="entity.claimsRatio"
:label="t('customer.summary.claimRate')"
:value="toPercentage(claimRate)"
/>
</QCard>
<QCard class="vn-one" v-if="entity.account">
<a
class="header link"
:href="`https://grafana.verdnatura.es/d/40buzE4Vk/comportamiento-pagos-clientes?orgId=1&var-clientFk=${entityId}`"
link
>
{{ t('customer.summary.financialData') }}
</QItemLabel>
<QItem v-if="entity.debt">
<QItemSection>
<QItemLabel caption>
{{ t('customer.summary.risk') }}
</QItemLabel>
<QItemLabel :class="debtWarning">
{{ toCurrency(entity.debt.debt) }}
</QItemLabel>
</QItemSection>
<QItemSection side>
<QIcon name="vn:info">
<QTooltip>
{{ t('customer.summary.riskInfo') }}
</QTooltip>
</QIcon>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>
{{ t('customer.summary.credit') }}
</QItemLabel>
<QItemLabel :class="creditWarning">
{{ toCurrency(entity.credit) }}
</QItemLabel>
</QItemSection>
<QItemSection side>
<QIcon name="vn:info">
<QTooltip>
{{ t('customer.summary.creditInfo') }}
</QTooltip>
</QIcon>
</QItemSection>
</QItem>
<QItem v-if="entity.creditInsurance">
<QItemSection>
<QItemLabel caption>
{{ t('customer.summary.securedCredit') }}
</QItemLabel>
<QItemLabel>
{{ toCurrency(entity.creditInsurance) }}
</QItemLabel>
</QItemSection>
<QItemSection side>
<QIcon name="vn:info">
<QTooltip>
{{ t('customer.summary.securedCreditInfo') }}
</QTooltip>
</QIcon>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>
{{ t('customer.summary.balance') }}
</QItemLabel>
<QItemLabel>
{{ toCurrency(entity.sumRisk) || toCurrency(0) }}
</QItemLabel>
</QItemSection>
<QItemSection side>
<QIcon name="vn:info">
<QTooltip>
{{ t('customer.summary.balanceInfo') }}
</QTooltip>
</QIcon>
</QItemSection>
</QItem>
<QItem v-if="entity.defaulters">
<QItemSection>
<QItemLabel caption>
{{ t('customer.summary.balanceDue') }}
</QItemLabel>
<QItemLabel :class="balanceDueWarning">
{{ toCurrency(balanceDue) }}
</QItemLabel>
</QItemSection>
<QItemSection side>
<QIcon name="vn:info">
<QTooltip>
{{ t('customer.summary.balanceDueInfo') }}
</QTooltip>
</QIcon>
</QItemSection>
</QItem>
<QItem v-if="entity.recovery">
<QItemSection>
<QItemLabel caption>
{{ t('customer.summary.recoverySince') }}
</QItemLabel>
<QItemLabel>
{{ toDate(entity.recovery.started) }}
</QItemLabel>
</QItemSection>
</QItem>
</QList>
</div>
</QCardSection>
<QIcon name="vn:grafana" color="primary" />
</a>
<VnLv
:label="t('customer.summary.risk')"
:value="toCurrency(entity?.debt?.debt)"
:class="debtWarning"
:info="t('customer.summary.riskInfo')"
/>
<VnLv
:label="t('customer.summary.credit')"
:value="toCurrency(entity.credit)"
:class="creditWarning"
:info="t('customer.summary.creditInfo')"
/>
<VnLv
v-if="entity.creditInsurance"
:label="t('customer.summary.securedCredit')"
:value="toCurrency(entity.creditInsurance)"
:info="t('customer.summary.securedCreditInfo')"
/>
<VnLv
:label="t('customer.summary.balance')"
:value="toCurrency(entity.sumRisk) || toCurrency(0)"
:info="t('customer.summary.balanceInfo')"
/>
<VnLv
v-if="entity.defaulters"
:label="t('customer.summary.balanceDue')"
:value="toCurrency(balanceDue)"
:class="balanceDueWarning"
:info="t('customer.summary.balanceDueInfo')"
/>
<VnLv
v-if="entity.recovery"
:label="t('customer.summary.recoverySince')"
:value="toDate(entity.recovery.started)"
/>
</QCard>
</template>
</CardSummary>
</template>
<style lang="scss">
.q-item__label + .q-item__label {
margin: 0;
}
</style>

View File

@ -4,8 +4,15 @@ import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import axios from 'axios';
import { toCurrency, toDate } from 'src/filters';
import SkeletonSummary from 'components/ui/SkeletonSummary.vue';
onMounted(() => fetch());
import CardSummary from 'components/ui/CardSummary.vue';
import VnLv from 'src/components/ui/VnLv.vue';
import { getUrl } from 'src/composables/getUrl';
onMounted(async () => {
fetch();
salixUrl.value = await getUrl('');
invoiceOutUrl.value = salixUrl.value + `invoiceOut/${entityId.value}/`;
});
const route = useRoute();
const { t } = useI18n();
@ -19,20 +26,13 @@ const $props = defineProps({
const entityId = computed(() => $props.id || route.params.id);
const invoiceOut = ref(null);
const tax = ref(null);
const tikets = ref(null);
const salixUrl = ref();
const invoiceOutUrl = ref();
const tickets = ref(null);
function fetch() {
const id = entityId.value;
axios.get(`InvoiceOuts/${id}/summary`).then(({ data }) => {
invoiceOut.value = data.invoiceOut;
tax.value = data.invoiceOut.taxesBreakdown;
});
axios.get(`InvoiceOuts/${id}/getTickets`).then(({ data }) => {
tikets.value = data;
axios.get(`InvoiceOuts/${entityId.value}/getTickets`).then(({ data }) => {
tickets.value = data;
});
}
@ -95,124 +95,64 @@ const ticketsColumns = ref([
</script>
<template>
<div class="summary container">
<QCard>
<SkeletonSummary v-if="!invoiceOut" />
<template v-if="invoiceOut">
<div class="header bg-primary q-pa-sm q-mb-md">
{{ invoiceOut.ref }} - {{ invoiceOut.client.socialName }}
<CardSummary ref="summary" :url="`InvoiceOuts/${entityId}/summary`">
<template #header="{ entity: { invoiceOut } }">
<div>{{ invoiceOut.ref }} - {{ invoiceOut.client?.socialName }}</div>
</template>
<template #body="{ entity: { invoiceOut } }">
<QCard class="vn-one">
<div class="header">
{{ t('invoiceOut.pageTitles.basicData') }}
</div>
<QList>
<QItem>
<QItemSection>
<QItemLabel caption>{{
t('invoiceOut.summary.issued')
}}</QItemLabel>
<QItemLabel>{{ toDate(invoiceOut.issued) }}</QItemLabel>
</QItemSection>
<QItemSection>
<QItemLabel caption>{{
t('invoiceOut.summary.dued')
}}</QItemLabel>
<QItemLabel>{{ toDate(invoiceOut.dued) }}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>{{
t('invoiceOut.summary.created')
}}</QItemLabel>
<QItemLabel>{{ toDate(invoiceOut.created) }}</QItemLabel>
</QItemSection>
<QItemSection>
<QItemLabel caption>{{
t('invoiceOut.summary.booked')
}}</QItemLabel>
<QItemLabel>{{ toDate(invoiceOut.booked) }}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>{{
t('invoiceOut.summary.company')
}}</QItemLabel>
<QItemLabel>{{ invoiceOut.company.code }}</QItemLabel>
</QItemSection>
</QItem>
</QList>
<QCardSection class="q-pa-md">
<h6>{{ t('invoiceOut.summary.taxBreakdown') }}</h6>
<QTable :columns="taxColumns" :rows="tax" flat>
<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>
</QCardSection>
<QCardSection class="q-pa-md">
<h6>{{ t('invoiceOut.summary.tickets') }}</h6>
<QTable :columns="ticketsColumns" :rows="tikets" flat>
<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>
</QCardSection>
</template>
<VnLv
:label="t('invoiceOut.summary.issued')"
:value="toDate(invoiceOut.issued)"
/>
<VnLv
:label="t('invoiceOut.summary.dued')"
:value="toDate(invoiceOut.dued)"
/>
<VnLv
:label="t('invoiceOut.summary.created')"
:value="toDate(invoiceOut.created)"
/>
<VnLv
:label="t('invoiceOut.summary.booked')"
:value="toDate(invoiceOut.booked)"
/>
<VnLv
:label="t('invoiceOut.summary.company')"
:value="invoiceOut.company.code"
/>
</QCard>
<QCard class="vn-three">
<div class="header">
{{ t('invoiceOut.summary.taxBreakdown') }}
</div>
<QTable :columns="taxColumns" :rows="invoiceOut.taxesBreakdown" flat>
<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-three">
<div class="header">
{{ t('invoiceOut.summary.tickets') }}
</div>
<QTable :columns="ticketsColumns" :rows="tickets" flat>
<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>
</template>
</CardSummary>
</template>
<style lang="scss" scoped>
.container {
display: flex;
justify-content: center;
}
.q-card {
width: 100%;
min-width: 950px;
max-width: 950px;
}
.summary {
.header {
text-align: center;
font-size: 18px;
}
#slider-container {
max-width: 80%;
margin: 0 auto;
.q-slider {
.q-slider__marker-labels:nth-child(1) {
transform: none;
}
.q-slider__marker-labels:nth-child(2) {
transform: none;
left: auto !important;
right: 0%;
}
}
}
}
.q-dialog .summary {
max-width: 1200px;
}
</style>

View File

@ -4,14 +4,15 @@ import { useRoute, useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
import axios from 'axios';
import { dashIfEmpty, toDate, toCurrency } from 'src/filters';
import SkeletonSummary from 'components/ui/SkeletonSummary.vue';
import CardSummary from 'components/ui/CardSummary.vue';
import FetchData from 'components/FetchData.vue';
import FetchedTags from 'components/ui/FetchedTags.vue';
import InvoiceOutDescriptorProxy from 'pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue';
import WorkerDescriptorProxy from 'pages/Worker/Card/WorkerDescriptorProxy.vue';
import VnLv from 'src/components/ui/VnLv.vue';
import { getUrl } from 'src/composables/getUrl';
onMounted(() => fetch());
onUpdated(() => fetch());
onUpdated(() => summaryRef.value.fetch());
const route = useRoute();
const router = useRouter();
@ -28,25 +29,22 @@ const $props = defineProps({
const entityId = computed(() => $props.id || route.params.id);
const summaryRef = ref();
const ticket = ref();
const salesLines = ref(null);
const editableStates = ref([]);
const ticketUrl = ref();
async function fetch() {
const { data } = await axios.get(`Tickets/${entityId.value}/summary`);
onMounted(async () => {
ticketUrl.value = (await getUrl('ticket/')) + entityId.value + '/';
});
async function setData(data) {
if (data) {
ticket.value = data;
salesLines.value = data.sales;
}
}
function stateColor(state) {
if (state.code === 'OK') return 'text-green';
if (state.code === 'FREE') return 'text-blue-3';
if (state.alertLevel === 1) return 'text-primary';
if (state.alertLevel === 0) return 'text-red';
}
function formattedAddress() {
if (!ticket.value) return '';
@ -76,7 +74,7 @@ async function changeState(value) {
};
await axios.post(`TicketTrackings/changeState`, formData);
await router.go(route.fullPath);
router.go(route.fullPath);
}
</script>
@ -86,17 +84,20 @@ async function changeState(value) {
@on-fetch="(data) => (editableStates = data)"
auto-load
/>
<div class="summary container">
<QCard>
<SkeletonSummary v-if="!ticket" />
<template v-if="ticket">
<div class="header bg-primary q-pa-sm q-mb-md">
<span>
Ticket #{{ ticket.id }} - {{ ticket.client.name }} ({{
ticket.client.id
<CardSummary
ref="summaryRef"
:url="`Tickets/${entityId}/summary`"
@on-fetch="(data) => setData(data)"
>
<template #header="{ entity }">
<div>
Ticket #{{ entity.id }} - {{ entity.client.name }} ({{
entity.client.id
}}) -
{{ ticket.nickname }}
</span>
{{ entity.nickname }}
</div>
</template>
<template #header-right>
<QBtnDropdown
side
top
@ -126,249 +127,145 @@ async function changeState(value) {
</QVirtualScroll>
</QList>
</QBtnDropdown>
</template>
<template #body>
<QCard class="vn-max">
<div class="taxes">
<VnLv
:label="t('ticket.summary.subtotal')"
:value="toCurrency(ticket.totalWithoutVat)"
/>
<VnLv
:label="t('ticket.summary.vat')"
:value="toCurrency(ticket.totalWithVat - ticket.totalWithoutVat)"
/>
<VnLv
:label="t('ticket.summary.total')"
:value="toCurrency(ticket.totalWithVat)"
/>
</div>
<div class="row q-pa-md q-col-gutter-md q-mb-md">
<div class="col">
<QList>
<QItem>
<QItemSection>
<QItemLabel caption>
{{ t('ticket.summary.state') }}
</QItemLabel>
<QItemLabel>
<QBadge
:color="
ticket.ticketState.state.classColor
? ticket.ticketState.state.classColor
: 'dark'
"
>
</QCard>
<QCard class="vn-one">
<a class="header link" :href="ticketUrl + 'basic-data/step-one'">
{{ t('globals.summary.basicData') }}
<QIcon name="open_in_new" color="primary" />
</a>
<VnLv :label="t('ticket.summary.state')">
<template #value>
<QChip :color="ticket.ticketState.state.classColor ?? 'dark'">
{{ ticket.ticketState.state.name }}
</QBadge>
</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>{{
t('ticket.summary.salesPerson')
}}</QItemLabel>
<QItemLabel>
</QChip>
</template>
</VnLv>
<VnLv :label="t('ticket.summary.salesPerson')">
<template #value>
<span class="link">
{{ ticket.client.salesPersonUser.name }}
<WorkerDescriptorProxy
:id="ticket.client.salesPersonFk"
<WorkerDescriptorProxy :id="ticket.client.salesPersonFk" />
</span>
</template>
</VnLv>
<VnLv
:label="t('ticket.summary.agency')"
:value="ticket.agencyMode.name"
/>
<VnLv :label="t('ticket.summary.zone')" :value="ticket.zone.name" />
<VnLv
:label="t('ticket.summary.warehouse')"
:value="ticket.warehouse.name"
/>
<VnLv :label="t('ticket.summary.route')" :value="ticket.routeFk" />
<VnLv :label="t('ticket.summary.invoice')">
<template #value>
<span class="link">
{{ dashIfEmpty(ticket.refFk) }}
<InvoiceOutDescriptorProxy
:id="ticket.id"
v-if="ticket.refFk"
/>
</span>
</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>{{
t('ticket.summary.agency')
}}</QItemLabel>
<QItemLabel>{{ ticket.agencyMode.name }}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>{{
t('ticket.summary.zone')
}}</QItemLabel>
<QItemLabel class="link">{{
ticket.routeFk
}}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>{{
t('ticket.summary.warehouse')
}}</QItemLabel>
<QItemLabel>{{ ticket.warehouse.name }}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>{{
t('ticket.summary.invoice')
}}</QItemLabel>
<QItemLabel v-if="ticket.refFk">
<span class="link">
{{ ticket.refFk }}
<InvoiceOutDescriptorProxy :id="ticket.id" />
</span>
</QItemLabel>
</QItemSection>
</QItem>
</QList>
</div>
<div class="col">
<QList>
<QItem>
<QItemSection>
<QItemLabel caption>{{
t('ticket.summary.shipped')
}}</QItemLabel>
<QItemLabel>{{ toDate(ticket.shipped) }}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>{{
t('ticket.summary.landed')
}}</QItemLabel>
<QItemLabel>{{ toDate(ticket.landed) }}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>{{
t('ticket.summary.packages')
}}</QItemLabel>
<QItemLabel>{{ ticket.packages }}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>{{
t('ticket.summary.consigneePhone')
}}</QItemLabel>
<QItemLabel>{{ ticket.address.phone }}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>{{
t('ticket.summary.consigneeMobile')
}}</QItemLabel>
<QItemLabel>{{ ticket.address.mobile }}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>{{
t('ticket.summary.clientPhone')
}}</QItemLabel>
<QItemLabel>{{ ticket.client.phone }}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>{{
t('ticket.summary.clientMobile')
}}</QItemLabel>
<QItemLabel>{{ ticket.client.mobile }}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>{{
t('ticket.summary.consignee')
}}</QItemLabel>
<QItemLabel>{{ formattedAddress() }}</QItemLabel>
</QItemSection>
</QItem>
</QList>
</div>
<div class="col">
<QList>
<QItem v-for="note in ticket.notes" :key="note.id">
<QItemSection>
<QItemLabel caption>
{{ note.observationType.description }}
</QItemLabel>
<QItemLabel>
{{ note.description }}
</QItemLabel>
</QItemSection>
</QItem>
</QList>
</div>
<div class="col">
<QList class="taxes">
<QItem>
<QItemSection>
<QItemLabel caption>{{
t('ticket.summary.subtotal')
}}</QItemLabel>
<QItemLabel>{{
toCurrency(ticket.totalWithoutVat)
}}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>{{
t('ticket.summary.vat')
}}</QItemLabel>
<QItemLabel>{{
toCurrency(
ticket.totalWithVat - ticket.totalWithoutVat
)
}}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>{{
t('ticket.summary.total')
}}</QItemLabel>
<QItemLabel>{{
toCurrency(ticket.totalWithVat)
}}</QItemLabel>
</QItemSection>
</QItem>
</QList>
</div>
</div>
<div class="row q-pa-md" v-if="salesLines.length > 0">
<div class="col">
<QList>
<QItemLabel header class="text-h6">
{{ t('ticket.summary.saleLines') }}
<RouterLink
:to="{
name: 'TicketBasicData',
params: { id: entityId },
}"
target="_blank"
</template>
</VnLv>
<VnLv
:label="t('ticket.summary.weight')"
:value="dashIfEmpty(ticket.weight)"
/>
</QCard>
<QCard class="vn-one">
<a class="header link" :href="ticketUrl + 'basic-data/step-one'">
{{ t('globals.summary.basicData') }}
<QIcon name="open_in_new" color="primary" />
</a>
<VnLv
:label="t('ticket.summary.shipped')"
:value="toDate(ticket.shipped)"
/>
<VnLv
:label="t('ticket.summary.landed')"
:value="toDate(ticket.landed)"
/>
<VnLv :label="t('ticket.summary.packages')" :value="ticket.packages" />
<VnLv
:label="t('ticket.summary.consigneePhone')"
:value="ticket.address.phone"
/>
<VnLv
:label="t('ticket.summary.consigneeMobile')"
:value="ticket.address.mobile"
/>
<VnLv
:label="t('ticket.summary.clientPhone')"
:value="ticket.client.phone"
/>
<VnLv
:label="t('ticket.summary.clientMobile')"
:value="ticket.client.mobile"
/>
<VnLv
:label="t('ticket.summary.consignee')"
:value="formattedAddress()"
/>
</QCard>
<QCard class="vn-one">
<a class="header link" :href="ticketUrl + 'observation'">
{{ t('ticket.pageTitles.notes') }}
<QIcon name="open_in_new" color="primary" />
</a>
<VnLv
v-for="note in ticket.notes"
:key="note.id"
:label="note.observationType.description"
:value="note.description"
>
<QIcon name="open_in_new" />
</RouterLink>
</QItemLabel>
<QTable :rows="ticket.sales" flat>
<template #value>
<QInput
v-model="note.description"
filled
type="textarea"
class="notes"
readonly
/>
</template>
</VnLv>
</QCard>
<QCard class="vn-max">
<a class="header link" :href="ticketUrl + 'sale'">
{{ t('ticket.summary.saleLines') }}
<QIcon name="open_in_new" color="primary" />
</a>
<QTable :rows="ticket.sales">
<template #header="props">
<QTr :props="props">
<QTh auto-width></QTh>
<QTh auto-width>{{
t('ticket.summary.item')
}}</QTh>
<QTh auto-width>{{
t('ticket.summary.visible')
}}</QTh>
<QTh auto-width>{{
t('ticket.summary.available')
}}</QTh>
<QTh auto-width>{{
t('ticket.summary.quantity')
}}</QTh>
<QTh auto-width>{{
t('ticket.summary.description')
}}</QTh>
<QTh auto-width>{{
t('ticket.summary.price')
}}</QTh>
<QTh auto-width>{{
t('ticket.summary.discount')
}}</QTh>
<QTh auto-width>{{
t('ticket.summary.amount')
}}</QTh>
<QTh auto-width>{{
t('ticket.summary.packing')
}}</QTh>
<QTh auto-width>{{ t('ticket.summary.item') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.visible') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.available') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.quantity') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.description') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.price') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.discount') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.amount') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.packing') }}</QTh>
</QTr>
</template>
<template #body="props">
@ -390,9 +287,7 @@ async function changeState(value) {
>
<QTooltip
>{{ t('ticket.summary.claim') }}:
{{
props.row.claim.claimFk
}}</QTooltip
{{ props.row.claim.claimFk }}</QTooltip
>
</QBtn>
<QBtn
@ -405,16 +300,13 @@ async function changeState(value) {
:to="{
name: 'ClaimCard',
params: {
id: props.row.claimBeginning
.claimFk,
id: props.row.claimBeginning.claimFk,
},
}"
>
<QTooltip
>{{ t('ticket.summary.claim') }}:
{{
props.row.claimBeginning.claimFk
}}</QTooltip
{{ props.row.claimBeginning.claimFk }}</QTooltip
>
</QBtn>
<QIcon
@ -455,11 +347,7 @@ async function changeState(value) {
color="primary"
>
<QTooltip>
{{
t(
'ticket.summary.hasComponentLack'
)
}}
{{ t('ticket.summary.hasComponentLack') }}
</QTooltip>
</QIcon>
</QTd>
@ -470,11 +358,9 @@ async function changeState(value) {
<QTd>
<div class="fetched-tags">
<span>{{ props.row.item.name }}</span>
<span
v-if="props.row.item.subName"
class="subName"
>{{ props.row.item.subName }}</span
>
<span v-if="props.row.item.subName" class="subName">{{
props.row.item.subName
}}</span>
</div>
<fetched-tags
:item="props.row.item"
@ -492,37 +378,25 @@ async function changeState(value) {
)
}}
</QTd>
<QTd>{{
dashIfEmpty(props.row.item.itemPackingTypeFk)
}}</QTd>
<QTd>{{ dashIfEmpty(props.row.item.itemPackingTypeFk) }}</QTd>
</QTr>
</template>
</QTable>
</QList>
</div>
</div>
<div
class="row q-pa-md"
</QCard>
<QCard
class="vn-max"
v-if="ticket.packagings.length > 0 || ticket.services.length > 0"
>
<div class="col" v-if="ticket.packagings.length > 0">
<QList>
<QItemLabel header class="text-h6">
<a class="header link" :href="ticketUrl + 'package'">
{{ t('ticket.summary.packages') }}
<QIcon name="open_in_new" />
</QItemLabel>
<QIcon name="open_in_new" color="primary" />
</a>
<QTable :rows="ticket.packagings" flat>
<template #header="props">
<QTr :props="props">
<QTh auto-width>{{
t('ticket.summary.created')
}}</QTh>
<QTh auto-width>{{
t('ticket.summary.package')
}}</QTh>
<QTh auto-width>{{
t('ticket.summary.quantity')
}}</QTh>
<QTh auto-width>{{ t('ticket.summary.created') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.package') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.quantity') }}</QTh>
</QTr>
</template>
<template #body="props">
@ -533,32 +407,19 @@ async function changeState(value) {
</QTr>
</template>
</QTable>
</QList>
</div>
<div class="col" v-if="ticket.services.length > 0">
<QList>
<QItemLabel header class="text-h6">
{{ t('ticket.summary.services') }}
<QIcon name="open_in_new" />
</QItemLabel>
<a class="header link q-mt-xl" :href="ticketUrl + 'service'">
{{ t('ticket.summary.service') }}
<QIcon name="open_in_new" color="primary" />
</a>
<QTable :rows="ticket.services" flat>
<template #header="props">
<QTr :props="props">
<QTh auto-width>{{
t('ticket.summary.quantity')
}}</QTh>
<QTh auto-width>{{
t('ticket.summary.description')
}}</QTh>
<QTh auto-width>{{
t('ticket.summary.price')
}}</QTh>
<QTh auto-width>{{
t('ticket.summary.taxClass')
}}</QTh>
<QTh auto-width>{{
t('ticket.summary.amount')
}}</QTh>
<QTh auto-width>{{ t('ticket.summary.quantity') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.description') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.price') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.taxClass') }}</QTh>
<QTh auto-width>{{ t('ticket.summary.amount') }}</QTh>
</QTr>
</template>
<template #body="props">
@ -568,156 +429,34 @@ async function changeState(value) {
<QTd>{{ toCurrency(props.row.price) }}</QTd>
<QTd>{{ props.row.taxClass.description }}</QTd>
<QTd>{{
toCurrency(
props.row.quantity * props.row.price
)
toCurrency(props.row.quantity * props.row.price)
}}</QTd>
</QTr>
</template>
</QTable>
</QList>
</div>
</div>
<div class="row q-pa-md" v-if="ticket.requests.length > 0">
<div class="col">
<QList>
<QItemLabel header class="text-h6">
{{ t('ticket.summary.request') }}
<QIcon name="open_in_new" />
</QItemLabel>
<QTable :rows="ticket.requests" flat>
<template #header="props">
<QTr :props="props">
<QTh auto-width>{{
t('ticket.summary.description')
}}</QTh>
<QTh auto-width>{{
t('ticket.summary.created')
}}</QTh>
<QTh auto-width>{{
t('ticket.summary.requester')
}}</QTh>
<QTh auto-width>{{
t('ticket.summary.atender')
}}</QTh>
<QTh auto-width>{{
t('ticket.summary.quantity')
}}</QTh>
<QTh auto-width>{{
t('ticket.summary.price')
}}</QTh>
<QTh auto-width>{{
t('ticket.summary.item')
}}</QTh>
<QTh auto-width>Ok</QTh>
</QTr>
</template>
<template #body="props">
<QTr :props="props">
<QTd>{{ props.row.description }}</QTd>
<QTd>{{ toDate(props.row.created) }}</QTd>
<QTd>{{ props.row.requester.user.name }}</QTd>
<QTd>{{ props.row.atender.user.name }}</QTd>
<QTd>{{ props.row.quantity }}</QTd>
<QTd>{{ toCurrency(props.row.price) }}</QTd>
<QTd v-if="!props.row.sale">-</QTd>
<QTd v-if="props.row.sale" class="link">{{
props.row.sale.itemFk
}}</QTd>
<QTd
><QCheckbox
v-model="props.row.isOk"
:disable="true"
/></QTd>
</QTr>
</template>
</QTable>
</QList>
</div>
</div>
</template>
</QCard>
</div>
</template>
</CardSummary>
</template>
<style lang="scss" scoped>
.container {
display: flex;
justify-content: center;
.notes {
width: max-content;
}
.q-card {
width: 100%;
height: 100%;
max-width: 1200px;
}
.summary {
.q-list {
.q-item__label--header {
display: flex;
justify-content: space-between;
a {
color: $primary;
}
}
}
.fetched-tags {
display: flex;
flex-wrap: wrap;
align-items: center;
& span {
flex-basis: 50%;
}
& span.subName {
flex-basis: 50%;
color: $secondary;
text-transform: uppercase;
font-size: 0.75rem;
}
}
.q-table__container {
text-align: left;
.q-icon {
padding: 2%;
}
}
.taxes {
border: $border-thin-light;
.cardSummary .summaryBody > .q-card > .taxes {
border: 2px solid gray;
padding: 0;
> .vn-label-value {
text-align: right;
padding: 8px;
}
.row {
flex-wrap: wrap;
.col {
min-width: 250px;
padding-left: 1.5%;
padding-right: 1.5%;
}
}
.header {
font-size: 18px;
display: flex;
justify-content: space-between;
align-items: center;
align-content: center;
margin: 0;
text-align: center;
span {
flex: 1;
}
.q-btn {
flex: none;
}
flex-direction: row;
margin-top: 5px;
justify-content: flex-end;
padding-right: 20px;
}
}
.q-dialog .summary {
max-width: 1200px;
.cardSummary .summaryBody > .q-card:has(.taxes) {
margin-top: 2px;
padding: 0;
}
</style>

View File

@ -3,11 +3,11 @@ import axios from 'axios';
import { ref, onMounted, computed, onUpdated } from 'vue';
import { useRoute } from 'vue-router';
import { useI18n } from 'vue-i18n';
import SkeletonSummary from 'components/ui/SkeletonSummary.vue';
import CardSummary from 'components/ui/CardSummary.vue';
import { getUrl } from 'src/composables/getUrl';
import VnLv from 'src/components/ui/VnLv.vue';
import WorkerDescriptorProxy from './WorkerDescriptorProxy.vue';
onMounted(() => fetch());
onUpdated(() => fetch());
import { dashIfEmpty } from 'src/filters';
const route = useRoute();
const { t } = useI18n();
@ -20,8 +20,11 @@ const $props = defineProps({
});
const entityId = computed(() => $props.id || route.params.id);
const workerUrl = ref();
const worker = ref(null);
onMounted(async () => {
workerUrl.value = (await getUrl('')) + `worker/${entityId.value}/`;
});
const filter = {
include: [
@ -59,230 +62,59 @@ const filter = {
},
],
};
function fetch() {
const id = entityId.value;
axios.get(`/Workers/${id}`, { params: { filter } }).then((response) => {
worker.value = response.data;
});
}
function sipExtension() {
if (worker.value.sip) return worker.value.sip.extension;
return '-';
}
</script>
<template>
<div class="summary container">
<QCard>
<SkeletonSummary v-if="!worker" />
<template v-if="worker">
<div class="header bg-primary q-pa-sm q-mb-md">
{{ worker.id }} - {{ worker.firstName }} {{ worker.lastName }}
</div>
<div class="row q-pa-md q-col-gutter-md q-mb-md">
<div class="col">
<QList>
<QItemLabel header class="text-h6">
{{ t('worker.summary.basicData') }}
</QItemLabel>
<QItem>
<QItemSection>
<QItemLabel caption> ID </QItemLabel>
<QItemLabel>{{ worker.id }}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption
>{{ t('worker.card.name') }}
</QItemLabel>
<QItemLabel>
{{ worker.user.nickname }}
</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption
>{{ t('worker.list.department') }}
</QItemLabel>
<QItemLabel>{{
worker.department.department.name
}}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption
>{{ t('worker.list.email') }}
</QItemLabel>
<QItemLabel>{{ worker.user.email }}</QItemLabel>
</QItemSection>
</QItem>
<QItem
class="items-start cursor-pointer q-hoverable"
v-if="worker.boss"
>
<QItemSection>
<QItemLabel caption>
{{ t('worker.summary.boss') }}
</QItemLabel>
<QItemLabel>
<span class="link">
{{ worker.boss.name }}
<WorkerDescriptorProxy :id="worker.bossFk" />
</span>
</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption
>{{ t('worker.summary.phoneExtension') }}
</QItemLabel>
<QItemLabel>
{{
worker.mobileExtension == ''
? worker.mobileExtension
: '-'
}}
</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption
>{{ t('worker.summary.entPhone') }}
</QItemLabel>
<QItemLabel>{{
worker.phone == '' ? worker.phone : '-'
}}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption
>{{ t('worker.summary.personalPhone') }}
</QItemLabel>
<QItemLabel>{{
worker.client.phone == ''
? worker.client.phone
: '-'
}}</QItemLabel>
</QItemSection>
</QItem>
</QList>
</div>
<div class="col">
<QList>
<QItemLabel header class="text-h6">
{{ t('worker.summary.userData') }}
</QItemLabel>
<QItem>
<QItemSection>
<QItemLabel caption>
{{ t('worker.summary.userId') }}
</QItemLabel>
<QItemLabel>{{ worker.user.id }}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption
>{{ t('worker.card.name') }}
</QItemLabel>
<QItemLabel>{{ worker.user.nickname }}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption
>{{ t('worker.summary.role') }}
</QItemLabel>
<QItemLabel>{{ worker.user.role.name }}</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption
>{{ t('worker.summary.sipExtension') }}
</QItemLabel>
<QItemLabel>{{ sipExtension() }}</QItemLabel>
</QItemSection>
</QItem>
</QList>
</div>
</div>
<CardSummary ref="summary" :url="`Workers/${entityId}`" :filter="filter">
<template #header="{ entity }">
<div>{{ entity.id }} - {{ entity.firstName }} {{ entity.lastName }}</div>
</template>
<template #body="{ entity: worker }">
<QCard class="vn-one">
<a class="header" :href="workerUrl + `basic-data`">
{{ t('worker.summary.basicData') }}
<QIcon name="open_in_new" color="primary" />
</a>
<VnLv :label="t('worker.card.name')" :value="worker.user.nickname" />
<VnLv
:label="t('worker.list.department')"
:value="worker.department.department.name"
/>
<VnLv :label="t('worker.list.email')" :value="worker.user.email" />
<VnLv :label="t('worker.summary.boss')" link>
<template #value>
<span class="link">
{{ dashIfEmpty(worker.boss?.name) }}
<WorkerDescriptorProxy
:id="worker.bossFk"
v-if="worker.boss"
/>
</span>
</template>
</VnLv>
<VnLv
:label="t('worker.summary.phoneExtension')"
:value="worker.mobileExtension"
/>
<VnLv :label="t('worker.summary.entPhone')" :value="worker.phone" />
<VnLv
:label="t('worker.summary.personalPhone')"
:value="worker.client.phone"
/>
<VnLv :label="t('worker.summary.locker')" :value="worker.locker" />
</QCard>
<QCard class="vn-one">
<div class="header">
{{ t('worker.summary.userData') }}
</div>
<VnLv :label="t('worker.summary.userId')" :value="worker.user.id" />
<VnLv :label="t('worker.card.name')" :value="worker.user.nickname" />
<VnLv :label="t('worker.summary.role')" :value="worker.user.role.name" />
<VnLv
:label="t('worker.summary.sipExtension')"
:value="worker?.sip?.extension"
/>
</QCard>
</template>
</CardSummary>
</template>
<style lang="scss" scoped>
.avatar {
width: 40px;
height: 40px;
border-radius: 50%;
margin-right: 10px;
}
.container {
display: flex;
justify-content: center;
}
.q-card {
width: 100%;
max-width: 1200px;
}
.negative {
color: red;
}
.summary {
.q-list {
.q-item__label--header {
display: flex;
justify-content: space-between;
a {
color: $primary;
}
}
}
.row {
flex-wrap: wrap;
.col {
min-width: 250px;
}
}
.header {
text-align: center;
font-size: 18px;
}
#slider-container {
max-width: 80%;
margin: 0 auto;
.q-slider {
.q-slider__marker-labels:nth-child(1) {
transform: none;
}
.q-slider__marker-labels:nth-child(2) {
transform: none;
left: auto !important;
right: 0%;
}
}
}
}
.q-dialog .summary {
max-width: 1200px;
}
</style>

View File

@ -1,15 +1,15 @@
describe('WorkerSummary', () => {
beforeEach(() => {
cy.viewport(1280, 720)
cy.login('developer')
cy.viewport(1280, 720);
cy.login('developer');
cy.visit('/#/worker/19/summary');
});
it('should load worker summary', () => {
cy.get('div[class="header bg-primary q-pa-sm q-mb-md"').should('have.text', '19 - salesBoss salesBoss');
cy.get('div[class="q-item__label q-item__label--header text-h6"]').eq(0).should('have.text', 'Basic data');
cy.get('div[class="q-item__label q-item__label--header text-h6"]').eq(1).should('have.text', 'User data');
cy.get('div[class="q-item__section column q-item__section--main justify-center"]').eq(0).should('have.text', 'NamesalesBossNick');
cy.get('.summaryHeader > div').should('have.text', '19 - salesBoss salesBoss');
cy.get(':nth-child(1) > :nth-child(2) > .value > span').should(
'have.text',
'salesBossNick'
);
});
});