Tickets future #461

Merged
jsegarra merged 10 commits from :feature/TicketsFuture into dev 2024-06-25 12:30:35 +00:00
6 changed files with 600 additions and 2 deletions

View File

@ -445,6 +445,7 @@ ticket:
notes: Notes
sale: Sale
ticketAdvance: Advance tickets
futureTickets: Future tickets
list:
nickname: Nickname
state: State

View File

@ -444,6 +444,7 @@ ticket:
notes: Notas
sale: Lineas del pedido
ticketAdvance: Adelantar tickets
futureTickets: Tickets a futuro
list:
nickname: Alias
state: Estado

View File

@ -0,0 +1,533 @@
<script setup>
import { onMounted, ref, computed, reactive } from 'vue';
import { useI18n } from 'vue-i18n';
import FetchData from 'components/FetchData.vue';
import VnInput from 'src/components/common/VnInput.vue';
import VnSelect from 'src/components/common/VnSelect.vue';
import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
import { dashIfEmpty, toCurrency } from 'src/filters';
import { useVnConfirm } from 'composables/useVnConfirm';
import { useArrayData } from 'composables/useArrayData';
import { getDateQBadgeColor } from 'src/composables/getDateQBadgeColor.js';
import useNotify from 'src/composables/useNotify.js';
import { useState } from 'src/composables/useState';
import { toDateTimeFormat } from 'src/filters/date.js';
import axios from 'axios';
const state = useState();
const { t } = useI18n();
const { openConfirmationModal } = useVnConfirm();
const { notify } = useNotify();
const user = state.getUser();
const itemPackingTypesOptions = ref([]);
const selectedTickets = ref([]);
const exprBuilder = (param, value) => {
switch (param) {
case 'id':
return { id: value };
case 'futureId':
return { futureId: value };
case 'liters':
return { liters: value };
case 'lines':
return { lines: value };
case 'ipt':
return { ipt: { like: `%${value}%` } };
case 'futureIpt':
return { futureIpt: { like: `%${value}%` } };
case 'totalWithVat':
return { totalWithVat: value };
}
};
const userParams = reactive({
futureDated: Date.vnNew(),
originDated: Date.vnNew(),
warehouseFk: user.value.warehouseFk,
});
const arrayData = useArrayData('FutureTickets', {
url: 'Tickets/getTicketsFuture',
userParams: userParams,
exprBuilder: exprBuilder,
});
const { store } = arrayData;
const params = reactive({
futureDated: Date.vnNew(),
originDated: Date.vnNew(),
warehouseFk: user.value.warehouseFk,
});
const applyColumnFilter = async (col) => {
try {
const paramKey = col.columnFilter?.filterParamKey || col.field;
params[paramKey] = col.columnFilter.filterValue;
await arrayData.addFilter({ params });
} catch (err) {
console.error('Error applying column filter', err);
}
};
const getInputEvents = (col) => {
return col.columnFilter.type === 'select'
? { 'update:modelValue': () => applyColumnFilter(col) }
: {
'keyup.enter': () => applyColumnFilter(col),
};
};
const ticketColumns = computed(() => [
Review

Revisamos el comportamiento de los filtros porque el valor desaparece y no filtra

Revisamos el comportamiento de los filtros porque el valor desaparece y no filtra
{
label: t('futureTickets.problems'),
name: 'problems',
align: 'left',
columnFilter: null,
},
{
label: t('futureTickets.ticketId'),
name: 'ticketId',
align: 'center',
sortable: true,
columnFilter: {
component: VnInput,
type: 'text',
filterValue: null,
filterParamKey: 'id',
event: getInputEvents,
attrs: {
dense: true,
},
},
},
{
label: t('futureTickets.shipped'),
name: 'shipped',
align: 'left',
sortable: true,
columnFilter: null,
},
{
label: t('futureTickets.ipt'),
name: 'ipt',
field: 'ipt',
align: 'left',
sortable: true,
columnFilter: {
component: VnSelect,
filterParamKey: 'ipt',
type: 'select',
filterValue: null,
event: getInputEvents,
attrs: {
options: itemPackingTypesOptions.value,
'option-value': 'code',
'option-label': 'description',
dense: true,
},
},
format: (val) => dashIfEmpty(val),
},
{
label: t('futureTickets.state'),
name: 'state',
align: 'left',
sortable: true,
columnFilter: null,
},
{
label: t('futureTickets.liters'),
name: 'liters',
field: 'liters',
align: 'left',
sortable: true,
columnFilter: {
component: VnInput,
type: 'text',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
},
},
{
label: t('futureTickets.import'),
field: 'import',
name: 'import',
align: 'left',
sortable: true,
},
{
label: t('futureTickets.availableLines'),
name: 'lines',
field: 'lines',
align: 'left',
sortable: true,
columnFilter: {
component: VnInput,
type: 'text',
filterValue: null,
event: getInputEvents,
attrs: {
dense: true,
},
},
format: (val) => dashIfEmpty(val),
},
{
label: t('futureTickets.futureId'),
name: 'futureId',
align: 'left',
sortable: true,
columnFilter: {
component: VnInput,
type: 'text',
filterValue: null,
filterParamKey: 'futureId',
event: getInputEvents,
attrs: {
dense: true,
},
},
},
{
label: t('futureTickets.futureShipped'),
name: 'futureShipped',
align: 'left',
sortable: true,
columnFilter: null,
format: (val) => dashIfEmpty(val),
},
{
label: t('futureTickets.futureIpt'),
name: 'futureIpt',
field: 'futureIpt',
align: 'left',
sortable: true,
columnFilter: {
component: VnSelect,
filterParamKey: 'futureIpt',
type: 'select',
filterValue: null,
event: getInputEvents,
attrs: {
options: itemPackingTypesOptions.value,
'option-value': 'code',
'option-label': 'description',
dense: true,
},
},
format: (val) => dashIfEmpty(val),
},
{
label: t('futureTickets.futureState'),
name: 'futureState',
align: 'left',
sortable: true,
columnFilter: null,
format: (val) => dashIfEmpty(val),
},
]);
const isLessThan50 = (totalWithVat) =>
parseInt(totalWithVat) > 0 && parseInt(totalWithVat) < 50;
const totalPriceColor = (totalWithVat) =>
isLessThan50(totalWithVat) ? 'warning' : 'transparent';
const moveTicketsFuture = async () => {
try {
const ticketsToMove = selectedTickets.value.map((ticket) => ({
originId: ticket.id,
destinationId: ticket.futureId,
originShipped: ticket.shipped,
destinationShipped: ticket.futureShipped,
workerFk: ticket.workerFk,
}));
let params = { tickets: ticketsToMove };
await axios.post('Tickets/merge', params);
notify(t('futureTickets.moveTicketSuccess'), 'positive');
selectedTickets.value = [];
arrayData.fetch({ append: false });
} catch (error) {
console.error('Error moving tickets to future', error);
}
};
onMounted(async () => {
await arrayData.fetch({ append: false });
});
</script>
<template>
<FetchData
url="itemPackingTypes"
:filter="{
fields: ['code', 'description'],
order: 'description ASC',
where: { isActive: true },
}"
auto-load
@on-fetch="(data) => (itemPackingTypesOptions = data)"
/>
<VnSearchbar
data-key="FutureTickets"
:label="t('Search ticket')"
:info="t('futureTickets.searchInfo')"
/>
<VnSubToolbar>
<template #st-data>
<QBtn
icon="keyboard_double_arrow_right"
color="primary"
jsegarra marked this conversation as resolved
Review

Añadir tooltip

Añadir tooltip
Review

Añadido.

Commit: 4e7c09fa44

Añadido. Commit: https://gitea.verdnatura.es/verdnatura/salix-front/commit/4e7c09fa44fa60ff29bf3647711979e33bfb455e
:disable="!selectedTickets.length"
@click.stop="
openConfirmationModal(
t('futureTickets.moveTicketTitle'),
t(`futureTickets.moveTicketDialogSubtitle`, {
selectedTickets: selectedTickets.length,
}),
moveTicketsFuture
)
"
>
<QTooltip>
{{ t('futureTickets.futureTicket') }}
</QTooltip>
jsegarra marked this conversation as resolved Outdated

No veo el selector/checkbox global

No veo el selector/checkbox global

Checkbox añadido.

Commit: 79c606fc70

Checkbox añadido. Commit: https://gitea.verdnatura.es/verdnatura/salix-front/commit/79c606fc7059c51c911d96348a7a5de96000fcb9
</QBtn>
</template>
</VnSubToolbar>
<QPage class="column items-center q-pa-md">
<QTable
:rows="store.data"
:columns="ticketColumns"
row-key="id"
jsegarra marked this conversation as resolved
Review

Propuesta, añadir hide-bottom

Propuesta, añadir hide-bottom
Review

Si quieres lo mantenemos pero cuando se cierre el modal que no aparezca el texto de que hay una fila seleccionada, cuando no la hay

Si quieres lo mantenemos pero cuando se cierre el modal que no aparezca el texto de que hay una fila seleccionada, cuando no la hay
Review

Fixed.

Commit: 6cd8a478fa

Fixed. Commit: https://gitea.verdnatura.es/verdnatura/salix-front/commit/6cd8a478fa39d39243e99efd191e386b666328e7
selection="multiple"
v-model:selected="selectedTickets"
:pagination="{ rowsPerPage: 0 }"
:no-data-label="t('globals.noResults')"
style="max-width: 99%"
jsegarra marked this conversation as resolved
Review

text-transform: uppercase
Y diria que tambien falta poner en texto ligthgray

text-transform: uppercase Y diria que tambien falta poner en texto ligthgray
Review

Cambios añadidos.

Commit: b6ec10994a

Cambios añadidos. Commit: https://gitea.verdnatura.es/verdnatura/salix-front/commit/b6ec10994ae48a687198bf9d824bdcfa7bd7d699
>
<template #header="props">
<QTr>
<QTh class="horizontal-separator" />
<QTh
class="horizontal-separator text-uppercase color-vn-label"
colspan="8"
translate
>
{{ t('futureTickets.origin') }}
</QTh>
<QTh
class="horizontal-separator text-uppercase color-vn-label"
colspan="4"
translate
>
{{ t('futureTickets.destination') }}
</QTh>
</QTr>
<QTr>
<QTh>
<QCheckbox v-model="props.selected" />
</QTh>
<QTh
v-for="(col, index) in ticketColumns"
:key="index"
:class="{ 'vertical-separator': col.name === 'futureId' }"
>
{{ col.label }}
</QTh>
</QTr>
</template>
<template #top-row="{ cols }">
<QTr>
<QTd />
<QTd
v-for="(col, index) in cols"
:key="index"
style="max-width: 100px"
>
<component
:is="col.columnFilter.component"
v-if="col.columnFilter"
v-model="col.columnFilter.filterValue"
v-bind="col.columnFilter.attrs"
v-on="col.columnFilter.event(col)"
dense
/>
</QTd>
</QTr>
</template>
<template #header-cell-availableLines="{ col }">
<QTh class="vertical-separator">
{{ col.label }}
</QTh>
</template>
<template #body-cell-problems="{ row }">
<QTd class="q-gutter-x-xs">
<QIcon
v-if="row.isTaxDataChecked === 0"
color="primary"
name="vn:no036"
size="xs"
>
<QTooltip>
{{ t('futureTickets.noVerified') }}
</QTooltip>
</QIcon>
<QIcon
v-if="row.hasTicketRequest"
color="primary"
name="vn:buyrequest"
size="xs"
>
<QTooltip>
{{ t('futureTickets.purchaseRequest') }}
</QTooltip>
</QIcon>
<QIcon
v-if="row.itemShortage"
color="primary"
name="vn:unavailable"
size="xs"
>
<QTooltip>
{{ t('futureTickets.noVisible') }}
</QTooltip>
</QIcon>
<QIcon
v-if="row.isFreezed"
color="primary"
name="vn:frozen"
size="xs"
>
<QTooltip>
{{ t('futureTickets.clientFrozen') }}
</QTooltip>
</QIcon>
<QIcon v-if="row.risk" color="primary" name="vn:risk" size="xs">
<QTooltip>
{{ t('futureTickets.risk') }}: {{ row.risk }}
</QTooltip>
</QIcon>
<QIcon
v-if="row.hasComponentLack"
color="primary"
name="vn:components"
size="xs"
>
<QTooltip>
{{ t('futureTickets.componentLack') }}
</QTooltip>
</QIcon>
<QIcon
v-if="row.hasRounding"
color="primary"
name="sync_problem"
size="xs"
>
<QTooltip>
{{ t('futureTickets.rounding') }}
</QTooltip>
</QIcon>
</QTd>
</template>
<template #body-cell-ticketId="{ row }">
<QTd>
<QBtn flat color="primary">
{{ row.id }}
<TicketDescriptorProxy :id="row.id" />
</QBtn>
</QTd>
</template>
<template #body-cell-shipped="{ row }">
<QTd>
<QBadge
text-color="black"
:color="getDateQBadgeColor(row.shipped)"
class="q-ma-none"
>
{{ toDateTimeFormat(row.shipped) }}
</QBadge>
</QTd>
</template>
<template #body-cell-state="{ row }">
<QTd>
<QBadge
text-color="black"
:color="row.classColor"
class="q-ma-none"
dense
>
{{ row.state }}
</QBadge>
</QTd>
</template>
<template #body-cell-import="{ row }">
<QTd>
<QBadge
:text-color="
totalPriceColor(row.totalWithVat) === 'warning'
? 'black'
: 'white'
"
:color="totalPriceColor(row.totalWithVat)"
class="q-ma-none"
dense
>
{{ toCurrency(row.totalWithVat || 0) }}
</QBadge>
</QTd>
</template>
<template #body-cell-futureId="{ row }">
<QTd class="vertical-separator">
<QBtn flat color="primary" dense>
{{ row.futureId }}
<TicketDescriptorProxy :id="row.futureId" />
</QBtn>
</QTd>
</template>
<template #body-cell-futureShipped="{ row }">
<QTd>
<QBadge
text-color="black"
:color="getDateQBadgeColor(row.futureShipped)"
class="q-ma-none"
>
{{ toDateTimeFormat(row.futureShipped) }}
</QBadge>
</QTd>
</template>
<template #body-cell-futureState="{ row }">
<QTd>
<QBadge
text-color="black"
:color="row.futureClassColor"
class="q-ma-none"
dense
>
{{ row.futureState }}
</QBadge>
</QTd>
</template>
</QTable>
</QPage>
</template>
<style scoped lang="scss">
.vertical-separator {
border-left: 4px solid white !important;
}
.horizontal-separator {
border-bottom: 4px solid white !important;
}
</style>

View File

@ -53,3 +53,30 @@ advanceTickets:
advanceWithoutNegativeTitle: Advance tickets (without negatives)
advanceWithoutNegativeSubtitle: Advance {selectedTickets} tickets confirmation
errorsList: Errors list
futureTickets:
problems: Problems
ticketId: ID
shipped: Date
ipt: IPT
state: State
liters: Liters
import: Import
availableLines: Available lines
futureId: ID
futureShipped: Date
futureIpt: IPT
futureState: State
noVerified: No verified data
noVisible: Not visible
purchaseRequest: Purchase request
clientFrozen: Client frozen
componentLack: Component lack
rounding: Rounding
risk: Risk
origin: Origin
destination: Destination
moveTicketTitle: Move tickets
moveTicketDialogSubtitle: 'Do you want to move {selectedTickets} tickets to the future?'
moveTicketSuccess: Tickets moved successfully!
searchInfo: Search future tickets by date
futureTicket: Future tickets

View File

@ -26,7 +26,34 @@ advanceTickets:
advanceWithoutNegativeTitle: Adelantar tickets (sin negativos)
advanceWithoutNegativeSubtitle: '¿Desea adelantar {selectedTickets} tickets?'
errorsList: Lista de errores
Search ticket: Buscar ticket
futureTickets:
problems: Problemas
ticketId: ID
shipped: Fecha
ipt: IPT
state: Estado
liters: Litros
import: Importe
availableLines: Líneas disponibles
futureId: ID
futureShipped: Fecha
futureIpt: IPT
futureState: Estado
noVerified: Sin datos comprobados
noVisible: No visible
purchaseRequest: Petición de compra
clientFrozen: Cliente congelado
risk: Riesgo
componentLack: Faltan componentes
rounding: Redondeo
origin: Origen
destination: Destino
moveTicketTitle: Mover tickets
moveTicketDialogSubtitle: '¿Desea mover {selectedTickets} tickets hacia el futuro?'
moveTicketSuccess: Tickets movidos correctamente
searchInfo: Buscar tickets por fecha
futureTicket: Tickets a futuro
Search ticket: Buscar tickets
You can search by ticket id or alias: Puedes buscar por id o alias del ticket
ticketSale:
id: Id

View File

@ -11,7 +11,7 @@ export default {
component: RouterView,
redirect: { name: 'TicketMain' },
menus: {
main: ['TicketList', 'TicketAdvance'],
main: ['TicketList', 'TicketAdvance', 'TicketFuture'],
card: ['TicketBoxing', 'TicketSms', 'TicketSale', 'TicketLog'],
},
children: [
@ -49,6 +49,15 @@ export default {
},
component: () => import('src/pages/Ticket/TicketAdvance.vue'),
},
{
name: 'TicketFuture',
path: 'future',
meta: {
title: 'futureTickets',
icon: 'keyboard_double_arrow_right',
},
component: () => import('src/pages/Ticket/TicketFuture.vue'),
},
],
},
{