salix-front/src/pages/Ticket/Card/TicketSummary.vue

718 lines
33 KiB
Vue

<script setup>
import { onMounted, ref, computed, onUpdated } from 'vue';
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 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';
onMounted(() => fetch());
onUpdated(() => fetch());
const route = useRoute();
const router = useRouter();
const { t } = useI18n();
const $props = defineProps({
id: {
type: Number,
required: false,
default: null,
},
});
const entityId = computed(() => $props.id || route.params.id);
const ticket = ref();
const salesLines = ref(null);
const editableStates = ref([]);
async function fetch() {
const { data } = await axios.get(`Tickets/${entityId.value}/summary`);
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 '';
const address = this.ticket.address;
const postcode = address.postalCode;
const province = address.province ? `(${address.province.name})` : '';
return `${address.street} - ${postcode} - ${address.city} ${province}`;
}
function isEditable() {
try {
return !this.ticket.ticketState.state.alertLevel;
} catch (e) {
console.error(e);
}
return true;
}
async function changeState(value) {
if (!this.ticket.id) return;
const formData = {
ticketFk: this.ticket.id,
code: value,
};
await axios.post(`TicketTrackings/changeState`, formData);
await router.go(route.fullPath);
}
</script>
<template>
<FetchData
url="States/editableStates"
@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
}}) -
{{ ticket.nickname }}
</span>
<QBtnDropdown
side
top
color="orange-11"
text-color="black"
:label="t('ticket.summary.changeState')"
:disable="!isEditable()"
>
<QList>
<QVirtualScroll
style="max-height: 300px"
:items="editableStates"
separator
v-slot="{ item, index }"
>
<QItem
:key="index"
dense
clickable
v-close-popup
@click="changeState(item.code)"
>
<QItemSection>
<QItemLabel>{{ item.name }}</QItemLabel>
</QItemSection>
</QItem>
</QVirtualScroll>
</QList>
</QBtnDropdown>
</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
:class="stateColor(ticket.ticketState.state)"
>
{{ ticket.ticketState.state.name }}
</QItemLabel>
</QItemSection>
</QItem>
<QItem>
<QItemSection>
<QItemLabel caption>{{
t('ticket.summary.salesPerson')
}}</QItemLabel>
<QItemLabel>
<span class="link">
{{ ticket.client.salesPersonUser.name }}
<WorkerDescriptorProxy
:id="ticket.client.salesPersonFk"
/>
</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"
>
<QIcon name="open_in_new" />
</RouterLink>
</QItemLabel>
<QTable :rows="ticket.sales" flat>
<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>
</QTr>
</template>
<template #body="props">
<QTr :props="props">
<QTd>
<QBtn
flat
round
size="xs"
icon="vn:claims"
v-if="props.row.claim"
color="primary"
:to="{
name: 'ClaimCard',
params: {
id: props.row.claim.claimFk,
},
}"
>
<QTooltip
>{{ t('ticket.summary.claim') }}:
{{
props.row.claim.claimFk
}}</QTooltip
>
</QBtn>
<QBtn
flat
round
size="xs"
icon="vn:claims"
v-if="props.row.claimBeginning"
color="primary"
:to="{
name: 'ClaimCard',
params: {
id: props.row.claimBeginning
.claimFk,
},
}"
>
<QTooltip
>{{ t('ticket.summary.claim') }}:
{{
props.row.claimBeginning.claimFk
}}</QTooltip
>
</QBtn>
<QIcon
name="warning"
v-show="props.row.visible < 0"
size="xs"
color="primary"
>
<QTooltip
>{{ t('ticket.summary.visible') }}:
{{ props.row.visible }}</QTooltip
>
</QIcon>
<QIcon
name="vn:reserva"
v-show="props.row.reserved"
size="xs"
color="primary"
>
<QTooltip>
{{ t('ticket.summary.reserved') }}
</QTooltip>
</QIcon>
<QIcon
name="vn:unavailable"
v-show="props.row.itemShortage"
size="xs"
color="primary"
>
<QTooltip>
{{ t('ticket.summary.itemShortage') }}
</QTooltip>
</QIcon>
<QIcon
name="vn:components"
v-show="props.row.hasComponentLack"
size="xs"
color="primary"
>
<QTooltip>
{{
t(
'ticket.summary.hasComponentLack'
)
}}
</QTooltip>
</QIcon>
</QTd>
<QTd class="link">{{ props.row.itemFk }}</QTd>
<QTd>{{ props.row.visible }}</QTd>
<QTd>{{ props.row.available }}</QTd>
<QTd>{{ props.row.quantity }}</QTd>
<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
>
</div>
<fetched-tags
:item="props.row.item"
:max-length="5"
></fetched-tags>
</QTd>
<QTd>{{ props.row.price }}</QTd>
<QTd>{{ props.row.discount }} %</QTd>
<QTd
>{{
toCurrency(
props.row.quantity *
props.row.price *
((100 - props.row.discount) / 100)
)
}}
</QTd>
<QTd>{{
dashIfEmpty(props.row.item.itemPackingTypeFk)
}}</QTd>
</QTr>
</template>
</QTable>
</QList>
</div>
</div>
<div
class="row q-pa-md"
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">
{{ t('ticket.summary.packages') }}
<QIcon name="open_in_new" />
</QItemLabel>
<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>
</QTr>
</template>
<template #body="props">
<QTr :props="props">
<QTd>{{ toDate(props.row.created) }}</QTd>
<QTd>{{ props.row.packaging.item.name }}</QTd>
<QTd>{{ props.row.quantity }}</QTd>
</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>
<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>
</QTr>
</template>
<template #body="props">
<QTr :props="props">
<QTd>{{ props.row.quantity }}</QTd>
<QTd>{{ props.row.description }}</QTd>
<QTd>{{ toCurrency(props.row.price) }}</QTd>
<QTd>{{ props.row.taxClass.description }}</QTd>
<QTd>{{
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>
<style lang="scss" scoped>
.container {
display: flex;
justify-content: center;
}
.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;
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;
}
}
}
.q-dialog .summary {
max-width: 1200px;
}
</style>