From 2b2ccbc6a105da40fb07f20fc012d4cb87589f9f Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Mon, 21 Oct 2024 12:14:54 +0200
Subject: [PATCH 1/8] feat: refs #7524 myTeam filter & default params

---
 src/i18n/locale/en.yml                   |  1 +
 src/i18n/locale/es.yml                   |  1 +
 src/pages/Ticket/TicketAdvance.vue       |  3 +++
 src/pages/Ticket/TicketAdvanceFilter.vue | 19 +++++++++++++++++--
 src/pages/Ticket/locale/es.yml           |  4 ++--
 5 files changed, 24 insertions(+), 4 deletions(-)

diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index 7eb3829fe..17a3e097a 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -296,6 +296,7 @@ globals:
         from: From
         To: To
         stateFk: State
+        myTeam: My team
 errors:
     statusUnauthorized: Access denied
     statusInternalServerError: An internal server error has ocurred
diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml
index 9d5cd53f3..fc61936d2 100644
--- a/src/i18n/locale/es.yml
+++ b/src/i18n/locale/es.yml
@@ -300,6 +300,7 @@ globals:
         from: Desde
         To: Hasta
         stateFk: Estado
+        myTeam: Mi equipo
 errors:
     statusUnauthorized: Acceso denegado
     statusInternalServerError: Ha ocurrido un error interno del servidor
diff --git a/src/pages/Ticket/TicketAdvance.vue b/src/pages/Ticket/TicketAdvance.vue
index 177b3a29b..2cce1dba8 100644
--- a/src/pages/Ticket/TicketAdvance.vue
+++ b/src/pages/Ticket/TicketAdvance.vue
@@ -462,6 +462,9 @@ onMounted(async () => {
     userParams.dateFuture = tomorrow;
     userParams.dateToAdvance = today;
     userParams.warehouseFk = user.value.warehouseFk;
+    userParams.ipt = 'H';
+    userParams.futureIpt = 'H';
+    userParams.isFullMovable = true;
     const filter = { limit: 0 };
     await arrayData.addFilter({ filter, userParams });
 });
diff --git a/src/pages/Ticket/TicketAdvanceFilter.vue b/src/pages/Ticket/TicketAdvanceFilter.vue
index 209a1a307..38a0dd4c2 100644
--- a/src/pages/Ticket/TicketAdvanceFilter.vue
+++ b/src/pages/Ticket/TicketAdvanceFilter.vue
@@ -10,7 +10,7 @@ import VnInputDate from 'src/components/common/VnInputDate.vue';
 import axios from 'axios';
 import { onMounted } from 'vue';
 
-const { t } = useI18n();
+const { t, te } = useI18n();
 const props = defineProps({
     dataKey: {
         type: String,
@@ -42,6 +42,11 @@ const getItemPackingTypes = async () => {
     }
 };
 
+const getLocale = (val) => {
+    const param = `params.${val}`;
+    return te(param) ? t(param) : t(`globals.${param}`);
+};
+
 onMounted(async () => await getItemPackingTypes());
 </script>
 
@@ -59,7 +64,7 @@ onMounted(async () => await getItemPackingTypes());
     >
         <template #tags="{ tag, formatFn }">
             <div class="q-gutter-x-xs">
-                <strong>{{ t(`params.${tag.label}`) }}: </strong>
+                <strong>{{ getLocale(tag.label) }}: </strong>
                 <span>{{ formatFn(tag.value) }}</span>
             </div>
         </template>
@@ -142,6 +147,16 @@ onMounted(async () => await getItemPackingTypes());
                     </VnSelect>
                 </QItemSection>
             </QItem>
+            <QItem>
+                <QItemSection>
+                    <QCheckbox
+                        :label="t('globals.params.myTeam')"
+                        v-model="params.myTeam"
+                        toggle-indeterminate
+                        @update:model-value="searchFn()"
+                    />
+                </QItemSection>
+            </QItem>
         </template>
     </VnFilterPanel>
 </template>
diff --git a/src/pages/Ticket/locale/es.yml b/src/pages/Ticket/locale/es.yml
index 0a27519ad..d4ba1f26a 100644
--- a/src/pages/Ticket/locale/es.yml
+++ b/src/pages/Ticket/locale/es.yml
@@ -86,9 +86,9 @@ weeklyTickets:
     search: Buscar por tickets programados
     searchInfo: Buscar tickets programados por el identificador o el identificador del cliente
 advanceTickets:
-    preparation: Preparación    
+    preparation: Preparación
     origin: Origen
-    destination: Destinatario
+    destination: Destino
     originAgency: 'Agencia origen: {agency}'
     destinationAgency: 'Agencia destino: {agency}'
     ticketId: ID

From a4358ec0edbcebd07fcf427c8a49097913b62dcb Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Mon, 21 Oct 2024 12:36:16 +0200
Subject: [PATCH 2/8] chore: refs #7524 refactor order

---
 src/pages/Ticket/TicketAdvanceFilter.vue | 16 ++++++----------
 1 file changed, 6 insertions(+), 10 deletions(-)

diff --git a/src/pages/Ticket/TicketAdvanceFilter.vue b/src/pages/Ticket/TicketAdvanceFilter.vue
index 38a0dd4c2..07c745250 100644
--- a/src/pages/Ticket/TicketAdvanceFilter.vue
+++ b/src/pages/Ticket/TicketAdvanceFilter.vue
@@ -129,6 +129,12 @@ onMounted(async () => await getItemPackingTypes());
                         toggle-indeterminate
                         @update:model-value="searchFn()"
                     />
+                    <QCheckbox
+                        :label="t('globals.params.myTeam')"
+                        v-model="params.myTeam"
+                        toggle-indeterminate
+                        @update:model-value="searchFn()"
+                    />
                 </QItemSection>
             </QItem>
             <QItem>
@@ -147,16 +153,6 @@ onMounted(async () => await getItemPackingTypes());
                     </VnSelect>
                 </QItemSection>
             </QItem>
-            <QItem>
-                <QItemSection>
-                    <QCheckbox
-                        :label="t('globals.params.myTeam')"
-                        v-model="params.myTeam"
-                        toggle-indeterminate
-                        @update:model-value="searchFn()"
-                    />
-                </QItemSection>
-            </QItem>
         </template>
     </VnFilterPanel>
 </template>

From 0fbd5f45e1c929aaaa1c1918c346ee9e7fcb56eb Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Mon, 21 Oct 2024 14:29:29 +0200
Subject: [PATCH 3/8] fix: refs #7524 select department

---
 src/i18n/locale/en.yml                   |  1 +
 src/i18n/locale/es.yml                   |  1 +
 src/pages/Ticket/TicketAdvanceFilter.vue | 17 ++++++++++++-----
 3 files changed, 14 insertions(+), 5 deletions(-)

diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index 17a3e097a..1c17e92f9 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -297,6 +297,7 @@ globals:
         To: To
         stateFk: State
         myTeam: My team
+        departmentFk: Department
 errors:
     statusUnauthorized: Access denied
     statusInternalServerError: An internal server error has ocurred
diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml
index fc61936d2..5d3d9d859 100644
--- a/src/i18n/locale/es.yml
+++ b/src/i18n/locale/es.yml
@@ -301,6 +301,7 @@ globals:
         To: Hasta
         stateFk: Estado
         myTeam: Mi equipo
+        departmentFk: Departamento
 errors:
     statusUnauthorized: Acceso denegado
     statusInternalServerError: Ha ocurrido un error interno del servidor
diff --git a/src/pages/Ticket/TicketAdvanceFilter.vue b/src/pages/Ticket/TicketAdvanceFilter.vue
index 07c745250..182f715a3 100644
--- a/src/pages/Ticket/TicketAdvanceFilter.vue
+++ b/src/pages/Ticket/TicketAdvanceFilter.vue
@@ -129,11 +129,18 @@ onMounted(async () => await getItemPackingTypes());
                         toggle-indeterminate
                         @update:model-value="searchFn()"
                     />
-                    <QCheckbox
-                        :label="t('globals.params.myTeam')"
-                        v-model="params.myTeam"
-                        toggle-indeterminate
-                        @update:model-value="searchFn()"
+                </QItemSection>
+            </QItem>
+            <QItem>
+                <QItemSection>
+                    <VnSelect
+                        :label="t('globals.params.departmentFk')"
+                        v-model="params.departmentFk"
+                        url="Departments"
+                        :fields="['id', 'name']"
+                        dense
+                        outlined
+                        rounded
                     />
                 </QItemSection>
             </QItem>

From f751408de2bfc1e34e6294f294de7365dd699a07 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Tue, 22 Oct 2024 13:22:43 +0200
Subject: [PATCH 4/8] feat: refs #8083 add change state btn

---
 src/components/VnTable/VnTable.vue            |   3 +
 src/components/common/VnBtnSelect.vue         |  19 +
 src/i18n/locale/en.yml                        |   2 +-
 src/i18n/locale/es.yml                        |   2 +-
 src/pages/Claim/Card/ClaimSummary.vue         |   2 +-
 src/pages/Ticket/Card/TicketExpedition.vue    | 338 +++++++-----------
 src/pages/Ticket/Card/TicketSummary.vue       |   2 +-
 .../ticket/ticketExpedition.spec.js           |  28 ++
 test/cypress/support/commands.js              |   8 +
 9 files changed, 190 insertions(+), 214 deletions(-)
 create mode 100644 src/components/common/VnBtnSelect.vue
 create mode 100644 test/cypress/integration/ticket/ticketExpedition.spec.js

diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index f18892a31..9d64591e9 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -127,6 +127,7 @@ const splittedColumns = ref({ columns: [] });
 const columnsVisibilitySkipped = ref();
 const createForm = ref();
 const tableFilterRef = ref([]);
+const tableRef = ref();
 
 const tableModes = [
     {
@@ -308,6 +309,7 @@ defineExpose({
     selected,
     CrudModelRef,
     params,
+    tableRef,
 });
 
 function handleOnDataSaved(_) {
@@ -398,6 +400,7 @@ function handleOnDataSaved(_) {
         </template>
         <template #body="{ rows }">
             <QTable
+                ref="tableRef"
                 v-bind="table"
                 class="vnTable"
                 :columns="splittedColumns.columns"
diff --git a/src/components/common/VnBtnSelect.vue b/src/components/common/VnBtnSelect.vue
new file mode 100644
index 000000000..b0616a6b2
--- /dev/null
+++ b/src/components/common/VnBtnSelect.vue
@@ -0,0 +1,19 @@
+<script setup>
+import VnSelect from './VnSelect.vue';
+
+defineProps({
+    selectProps: { type: Object, required: true },
+    promise: { type: Function, default: () => {} },
+});
+</script>
+<template>
+    <QBtnDropdown v-bind="$attrs" color="primary">
+        <VnSelect
+            v-bind="selectProps"
+            hide-selected
+            hide-dropdown-icon
+            focus-on-mount
+            @update:model-value="promise"
+        />
+    </QBtnDropdown>
+</template>
diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index 7eb3829fe..33ecb4850 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -296,6 +296,7 @@ globals:
         from: From
         To: To
         stateFk: State
+    changeState: Change state
 errors:
     statusUnauthorized: Access denied
     statusInternalServerError: An internal server error has ocurred
@@ -538,7 +539,6 @@ ticket:
         package: Package
         taxClass: Tax class
         services: Services
-        changeState: Change state
         requester: Requester
         atender: Atender
         request: Request
diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml
index 9d5cd53f3..f8a796116 100644
--- a/src/i18n/locale/es.yml
+++ b/src/i18n/locale/es.yml
@@ -300,6 +300,7 @@ globals:
         from: Desde
         To: Hasta
         stateFk: Estado
+    changeState: Cambiar estado
 errors:
     statusUnauthorized: Acceso denegado
     statusInternalServerError: Ha ocurrido un error interno del servidor
@@ -547,7 +548,6 @@ ticket:
         package: Embalaje
         taxClass: Tipo IVA
         services: Servicios
-        changeState: Cambiar estado
         requester: Solicitante
         atender: Comprador
         request: Petición de compra
diff --git a/src/pages/Claim/Card/ClaimSummary.vue b/src/pages/Claim/Card/ClaimSummary.vue
index d77f718c6..edfa52b4b 100644
--- a/src/pages/Claim/Card/ClaimSummary.vue
+++ b/src/pages/Claim/Card/ClaimSummary.vue
@@ -204,7 +204,7 @@ function claimUrl(section) {
                 top
                 color="black"
                 text-color="white"
-                :label="t('ticket.summary.changeState')"
+                :label="t('globals.changeState')"
             >
                 <QList>
                     <QVirtualScroll
diff --git a/src/pages/Ticket/Card/TicketExpedition.vue b/src/pages/Ticket/Card/TicketExpedition.vue
index c4ab63b39..987862c22 100644
--- a/src/pages/Ticket/Card/TicketExpedition.vue
+++ b/src/pages/Ticket/Card/TicketExpedition.vue
@@ -1,39 +1,38 @@
 <script setup>
-import { onMounted, ref, computed, onUnmounted, reactive, watch } from 'vue';
+import { onMounted, ref, computed, onUnmounted, watch } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useRoute } from 'vue-router';
 
-import VnInput from 'src/components/common/VnInput.vue';
 import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
-import TicketEditManaProxy from './TicketEditMana.vue';
-import TableVisibleColumns from 'src/components/common/TableVisibleColumns.vue';
 import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
 import ExpeditionNewTicket from './ExpeditionNewTicket.vue';
 import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
-import VnSelect from 'src/components/common/VnSelect.vue';
 
 import { useStateStore } from 'stores/useStateStore';
-import { toCurrency, toPercentage } from 'src/filters';
 import { useArrayData } from 'composables/useArrayData';
 import { useVnConfirm } from 'composables/useVnConfirm';
 import useNotify from 'src/composables/useNotify.js';
 import { toDateTimeFormat } from 'src/filters/date';
 import axios from 'axios';
+import VnTable from 'src/components/VnTable/VnTable.vue';
+import VnBtnSelect from 'src/components/common/VnBtnSelect.vue';
+import FetchData from 'src/components/FetchData.vue';
 
 const route = useRoute();
 const stateStore = useStateStore();
 const { t } = useI18n();
 const { notify } = useNotify();
 const { openConfirmationModal } = useVnConfirm();
-const editPriceProxyRef = ref(null);
 const newTicketDialogRef = ref(null);
 const logsTableDialogRef = ref(null);
-
+const vnTableRef = ref();
 const expeditionsLogsData = ref([]);
 const selectedExpeditions = ref([]);
 const allColumnNames = ref([]);
-const visibleColumns = ref([]);
 const newTicketWithRoute = ref(false);
+const selectedRows = ref([]);
+const hasSelectedRows = computed(() => selectedRows.value.length > 0);
+const expeditionStateTypes = ref([]);
 
 const exprBuilder = (param, value) => {
     switch (param) {
@@ -54,8 +53,6 @@ const expeditionsArrayData = useArrayData('ticketExpeditions', {
     filter: expeditionsFilter.value,
     exprBuilder: exprBuilder,
 });
-const expeditionsStore = expeditionsArrayData.store;
-const ticketExpeditions = computed(() => expeditionsStore.data);
 
 const ticketArrayData = useArrayData('ticketData');
 const ticketStore = ticketArrayData.store;
@@ -73,129 +70,87 @@ watch(
     { immediate: true }
 );
 
-const params = reactive({});
-
-const applyColumnFilter = async (col) => {
-    try {
-        const paramKey = col.columnFilter?.filterParamKey || col.field;
-        params[paramKey] = col.columnFilter.filterValue;
-        await expeditionsArrayData.addFilter({ filter: expeditionsFilter.value, 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 columns = computed(() => [
     {
+        align: 'left',
         label: t('expedition.id'),
         name: 'id',
-        field: 'id',
-        align: 'left',
-        sortable: true,
+        chip: {
+            condition: () => true,
+        },
+        isId: true,
         columnFilter: {
-            component: VnInput,
-            type: 'text',
-            filterParamKey: 'expeditionFk',
-            filterValue: null,
-            event: getInputEvents,
-            attrs: {
-                dense: true,
-            },
+            inWhere: true,
         },
     },
     {
         label: t('expedition.item'),
-        name: 'item',
+        name: 'packagingItemFk',
         align: 'left',
+        cardVisible: true,
         columnFilter: {
-            component: VnInput,
-            type: 'text',
-            filterParamKey: 'packageItemName',
-            filterValue: null,
-            event: getInputEvents,
-            attrs: {
-                dense: true,
-            },
+            inWhere: true,
         },
     },
     {
         label: t('expedition.name'),
-        name: 'name',
-        field: 'packageItemName',
+        name: 'packageItemName',
         align: 'left',
+        isTitle: true,
         columnFilter: {
-            component: VnSelect,
-            type: 'select',
-            filterValue: null,
-            event: getInputEvents,
-            attrs: {
-                url: 'Items',
-                fields: ['id', 'name'],
-                'sort-by': 'name ASC',
-                'option-value': 'id',
-                'option-label': 'name',
-                dense: true,
-            },
+            inWhere: true,
         },
     },
     {
         label: t('expedition.packageType'),
-        name: 'packageType',
-        field: 'freightItemName',
+        name: 'freightItemName',
         align: 'left',
         columnFilter: {
-            component: VnInput,
-            type: 'text',
-            // filterParamKey: 'expeditionFk',
-            filterValue: null,
-            event: getInputEvents,
-            attrs: {
-                dense: true,
-            },
+            inWhere: true,
         },
     },
     {
         label: t('expedition.counter'),
         name: 'counter',
-        field: 'counter',
         align: 'left',
-        columnFilter: null,
+        columnFilter: {
+            inWhere: true,
+        },
     },
     {
         label: t('expedition.externalId'),
         name: 'externalId',
-        field: 'externalId',
         align: 'left',
-        columnFilter: null,
+        cardVisible: true,
+        columnFilter: {
+            inWhere: true,
+        },
     },
     {
         label: t('expedition.created'),
         name: 'created',
-        field: 'created',
         align: 'left',
-        columnFilter: null,
-        format: (value) => toDateTimeFormat(value),
+        cardVisible: true,
+        format: (row) => toDateTimeFormat(row.created),
     },
     {
         label: t('expedition.state'),
         name: 'state',
-        field: 'state',
         align: 'left',
-        columnFilter: null,
+        cardVisible: true,
+        columnFilter: { inWhere: true },
     },
     {
-        label: '',
-        name: 'history',
-        align: 'left',
-        columnFilter: null,
+        align: 'right',
+        name: 'tableActions',
+        actions: [
+            {
+                title: t('expedition.historyAction'),
+                icon: 'history',
+                isPrimary: true,
+                action: (row) => showLog(row),
+            },
+        ],
     },
 ]);
 
@@ -204,23 +159,29 @@ const logTableColumns = computed(() => [
         label: t('expedition.state'),
         name: 'state',
         field: 'state',
-        align: 'left',
+        align: 'center',
         sortable: true,
     },
     {
         label: t('expedition.name'),
         name: 'name',
-        align: 'name',
+        field: 'name',
+        align: 'center',
         columnFilter: null,
     },
     {
         label: t('expedition.created'),
         name: 'created',
         field: 'created',
-        align: 'left',
-        columnFilter: null,
+        align: 'center',
         format: (value) => toDateTimeFormat(value),
     },
+    {
+        label: t('expedition.isScanned'),
+        name: 'isScanned',
+        field: 'isScanned',
+        align: 'center',
+    },
 ]);
 
 const showNewTicketDialog = (withRoute = false) => {
@@ -255,10 +216,20 @@ const getExpeditionState = async (expedition) => {
             order: ['created DESC'],
         };
 
-        const { data } = await axios.get(`ExpeditionStates/filter`, {
+        const { data: expeditionStates } = await axios.get(`ExpeditionStates/filter`, {
             params: { filter: JSON.stringify(filter) },
         });
-        expeditionsLogsData.value = data;
+        const { data: scannedStates } = await axios.get(`ExpeditionStates`, {
+            params: { filter: JSON.stringify(filter), fields: ['id', 'isScanned'] },
+        });
+
+        expeditionsLogsData.value = expeditionStates.map((state) => {
+            const scannedState = scannedStates.find((s) => s.id === state.id);
+            return {
+                ...state,
+                isScanned: scannedState ? scannedState.isScanned : false,
+            };
+        });
     } catch (error) {
         console.error(error);
     }
@@ -274,22 +245,39 @@ onUnmounted(() => (stateStore.rightDrawer = false));
 </script>
 
 <template>
+    <FetchData
+        url="expeditionStateTypes"
+        @on-fetch="(data) => (expeditionStateTypes = data)"
+        auto-load
+    />
     <VnSubToolbar>
-        <template #st-data>
-            <TableVisibleColumns
-                :all-columns="allColumnNames"
-                table-code="expeditionIndex"
-                labels-traductions-path="expedition"
-                @on-config-saved="visibleColumns = [...$event, 'history']"
-            />
-        </template>
         <template #st-actions>
             <QBtnGroup push class="q-gutter-x-sm" flat>
+                <VnBtnSelect
+                    :disable="!hasSelectedRows"
+                    color="primary"
+                    :label="t('globals.changeState')"
+                    :select-props="{
+                        options: expeditionStateTypes,
+                        optionLabel: 'description',
+                    }"
+                    :promise="
+                        async (stateTypeFk) => {
+                            await vnTableRef.CrudModelRef.saveChanges({
+                                updates: selectedRows.map(({ id }) => ({
+                                    data: { stateTypeFk },
+                                    where: { id },
+                                })),
+                            });
+                            vnTableRef.tableRef.clearSelection();
+                        }
+                    "
+                />
                 <QBtnDropdown
                     ref="btnDropdownRef"
                     color="primary"
                     :label="t('expedition.move')"
-                    :disable="!selectedExpeditions.length"
+                    :disable="!hasSelectedRows"
                 >
                     <template #label>
                         <QTooltip>{{ t('Select lines to see the options') }}</QTooltip>
@@ -322,7 +310,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
                     </QList>
                 </QBtnDropdown>
                 <QBtn
-                    :disable="!selectedExpeditions.length"
+                    :disable="!hasSelectedRows"
                     icon="delete"
                     color="primary"
                     @click="
@@ -332,115 +320,34 @@ onUnmounted(() => (stateStore.rightDrawer = false));
                             deleteExpedition
                         )
                     "
-                />
+                >
+                    <QTooltip>{{ t('expedition.removeExpedition') }}</QTooltip>
+                </QBtn>
             </QBtnGroup>
         </template>
     </VnSubToolbar>
-
-    <QTable
-        :rows="ticketExpeditions"
+    <VnTable
+        ref="vnTableRef"
+        data-key="TicketExpedition"
+        url="Expeditions/filter"
         :columns="columns"
-        row-key="id"
-        :pagination="{ rowsPerPage: 0 }"
-        class="full-width q-mt-md"
-        selection="multiple"
-        v-model:selected="selectedExpeditions"
-        :visible-columns="visibleColumns"
-        :no-data-label="t('globals.noResults')"
+        :filter="expeditionsFilter"
+        v-model:selected="selectedRows"
+        :table="{
+            'row-key': 'id',
+            selection: 'multiple',
+        }"
+        save-url="Expeditions/crud"
+        auto-load
+        order="created DESC"
     >
-        <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 #body-cell-item="{ row }">
-            <QTd auto-width @click.stop>
-                <QBtn flat color="primary">{{ row.packagingItemFk }}</QBtn>
+        <template #column-packagingItemFk="{ row }">
+            <span class="link" @click.stop>
+                {{ row.packagingItemFk }}
                 <ItemDescriptorProxy :id="row.packagingItemFk" />
-            </QTd>
+            </span>
         </template>
-        <template #body-cell-available="{ row }">
-            <QTd @click.stop>
-                <QBadge :color="row.available < 0 ? 'alert' : 'transparent'" dense>
-                    {{ row.available }}
-                </QBadge>
-            </QTd>
-        </template>
-
-        <template #body-cell-price="{ row }">
-            <QTd>
-                <template v-if="isTicketEditable && row.id">
-                    <QBtn flat color="primary" dense @click="onOpenEditPricePopover(row)">
-                        {{ toCurrency(row.price) }}
-                    </QBtn>
-                    <TicketEditManaProxy
-                        ref="editPriceProxyRef"
-                        :mana="mana"
-                        :new-price="getNewPrice"
-                        @save="updatePrice(row)"
-                    >
-                        <VnInput
-                            v-model.number="edit.price"
-                            :label="t('ticketSale.price')"
-                            type="number"
-                        />
-                    </TicketEditManaProxy>
-                </template>
-                <span v-else>{{ toCurrency(row.price) }}</span>
-            </QTd>
-        </template>
-        <template #body-cell-discount="{ row }">
-            <QTd>
-                <template v-if="!isLocked && row.id">
-                    <QBtn
-                        flat
-                        color="primary"
-                        dense
-                        @click="onOpenEditDiscountPopover(row)"
-                    >
-                        {{ toPercentage(row.discount / 100) }}
-                    </QBtn>
-                    <TicketEditManaProxy
-                        :mana="mana"
-                        :new-price="getNewPrice"
-                        @save="changeDiscount(row)"
-                    >
-                        <VnInput
-                            v-model.number="edit.discount"
-                            :label="t('ticketSale.discount')"
-                            type="number"
-                        />
-                    </TicketEditManaProxy>
-                </template>
-                <span v-else>{{ toPercentage(row.discount / 100) }}</span>
-            </QTd>
-        </template>
-        <template #body-cell-history="{ row }">
-            <QTd>
-                <QBtn
-                    @click.stop="showLog(row)"
-                    color="primary"
-                    icon="history"
-                    size="md"
-                    flat
-                >
-                    <QTooltip class="text-no-wrap">
-                        {{ t('expedition.historyAction') }}
-                    </QTooltip>
-                </QBtn>
-            </QTd>
-        </template>
-    </QTable>
+    </VnTable>
     <QDialog ref="newTicketDialogRef" transition-show="scale" transition-hide="scale">
         <ExpeditionNewTicket
             :ticket="ticketData"
@@ -454,12 +361,23 @@ onUnmounted(() => (stateStore.rightDrawer = false));
             data-key="TicketExpeditionLog"
             :rows="expeditionsLogsData"
             :columns="logTableColumns"
-            class="q-pa-sm"
+            class="q-pa-md full-width"
         >
             <template #body-cell-name="{ row }">
-                <QTd auto-width>
-                    <QBtn flat dense color="primary">{{ row.name }}</QBtn>
-                    <WorkerDescriptorProxy :id="row.workerFk" />
+                <QTd style="text-align: center">
+                    <span class="link" @click.stop>
+                        <QBtn flat dense>{{ row.name }}</QBtn>
+                        <WorkerDescriptorProxy :id="row.workerFk" />
+                    </span>
+                </QTd>
+            </template>
+            <template #body-cell-isScanned="{ row }">
+                <QTd style="text-align: center">
+                    <QCheckbox disable v-model="row.isScanned">
+                        {{
+                            row.isScanned === 1 ? t('expedition.yes') : t('expedition.no')
+                        }}
+                    </QCheckbox>
                 </QTd>
             </template>
         </QTable>
diff --git a/src/pages/Ticket/Card/TicketSummary.vue b/src/pages/Ticket/Card/TicketSummary.vue
index 1f2a7ca79..358f74af2 100644
--- a/src/pages/Ticket/Card/TicketSummary.vue
+++ b/src/pages/Ticket/Card/TicketSummary.vue
@@ -105,7 +105,7 @@ async function changeState(value) {
                 ref="stateBtnDropdownRef"
                 color="black"
                 text-color="white"
-                :label="t('ticket.summary.changeState')"
+                :label="t('globals.changeState')"
                 :disable="!isEditable()"
             >
                 <VnSelect
diff --git a/test/cypress/integration/ticket/ticketExpedition.spec.js b/test/cypress/integration/ticket/ticketExpedition.spec.js
new file mode 100644
index 000000000..5eb2c1a2a
--- /dev/null
+++ b/test/cypress/integration/ticket/ticketExpedition.spec.js
@@ -0,0 +1,28 @@
+/// <reference types="cypress" />
+
+describe('Ticket expedtion', () => {
+    const tableContent = '.q-table .q-virtual-scroll__content';
+    const stateTd = 'td:nth-child(9)';
+
+    beforeEach(() => {
+        cy.login('developer');
+        cy.viewport(1920, 1080);
+    });
+
+    it('should change the state', () => {
+        cy.visit('#/ticket/1/expedition');
+        cy.intercept('GET', /\/api\/Expeditions\/filter/).as('expeditions');
+        cy.intercept('POST', /\/api\/Expeditions\/crud/).as('crud');
+
+        cy.wait('@expeditions');
+
+        cy.selectRows([1, 2]);
+        cy.get('#subToolbar [aria-controls]:nth-child(1)').click();
+        cy.get('.q-menu .q-item').contains('Perdida').click();
+        cy.wait('@crud');
+
+        cy.get(`${tableContent} tr:nth-child(-n+2) ${stateTd}`).each(($el) => {
+            cy.wrap($el).contains('Perdida');
+        });
+    });
+});
diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js
index 43788f59f..f895d7bb3 100755
--- a/test/cypress/support/commands.js
+++ b/test/cypress/support/commands.js
@@ -152,6 +152,14 @@ Cypress.Commands.add('notificationHas', (selector, text) => {
     cy.get(selector).should('have.text', text);
 });
 
+Cypress.Commands.add('selectRows', (rows) => {
+    rows.forEach((row) => {
+        cy.get('.q-table .q-virtual-scroll__content tr .q-checkbox__inner')
+            .eq(row - 1)
+            .click();
+    });
+});
+
 Cypress.Commands.add('fillRow', (rowSelector, data) => {
     // Usar el selector proporcionado para obtener la fila deseada
     cy.waitForElement('tbody');

From b1a511ff6f7aee7fb218e78f909d7b73a4e98ac3 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Tue, 22 Oct 2024 14:21:03 +0200
Subject: [PATCH 5/8] fix: refs #8083 delete btn & redirect

---
 src/pages/Ticket/Card/TicketExpedition.vue | 47 +++++++---------------
 1 file changed, 14 insertions(+), 33 deletions(-)

diff --git a/src/pages/Ticket/Card/TicketExpedition.vue b/src/pages/Ticket/Card/TicketExpedition.vue
index 987862c22..61cedc1bf 100644
--- a/src/pages/Ticket/Card/TicketExpedition.vue
+++ b/src/pages/Ticket/Card/TicketExpedition.vue
@@ -1,5 +1,5 @@
 <script setup>
-import { onMounted, ref, computed, onUnmounted, watch } from 'vue';
+import { onMounted, ref, computed, onUnmounted } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useRoute } from 'vue-router';
 
@@ -34,42 +34,15 @@ const selectedRows = ref([]);
 const hasSelectedRows = computed(() => selectedRows.value.length > 0);
 const expeditionStateTypes = ref([]);
 
-const exprBuilder = (param, value) => {
-    switch (param) {
-        case 'expeditionFk':
-            return { id: value };
-        case 'packageItemName':
-            return { packagingItemFk: value };
-    }
-};
-
 const expeditionsFilter = computed(() => ({
     where: { ticketFk: route.params.id },
     order: ['created DESC'],
 }));
 
-const expeditionsArrayData = useArrayData('ticketExpeditions', {
-    url: 'Expeditions/filter',
-    filter: expeditionsFilter.value,
-    exprBuilder: exprBuilder,
-});
-
 const ticketArrayData = useArrayData('ticketData');
 const ticketStore = ticketArrayData.store;
 const ticketData = computed(() => ticketStore.data);
 
-const refetchExpeditions = async () => {
-    await expeditionsArrayData.applyFilter({
-        filter: expeditionsFilter.value,
-    });
-};
-
-watch(
-    () => route.params.id,
-    async () => await refetchExpeditions(),
-    { immediate: true }
-);
-
 const columns = computed(() => [
     {
         align: 'left',
@@ -191,12 +164,10 @@ const showNewTicketDialog = (withRoute = false) => {
 
 const deleteExpedition = async () => {
     try {
-        const expeditionIds = selectedExpeditions.value.map(
-            (expedition) => expedition.id
-        );
+        const expeditionIds = selectedRows.value.map((expedition) => expedition.id);
         const params = { expeditionIds };
         await axios.post('Expeditions/deleteExpeditions', params);
-        await refetchExpeditions();
+        vnTableRef.value.reload();
         selectedExpeditions.value = [];
         notify(t('expedition.expeditionRemoved'), 'positive');
     } catch (error) {
@@ -330,6 +301,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
         ref="vnTableRef"
         data-key="TicketExpedition"
         url="Expeditions/filter"
+        search-url="expeditions"
         :columns="columns"
         :filter="expeditionsFilter"
         v-model:selected="selectedRows"
@@ -339,7 +311,16 @@ onUnmounted(() => (stateStore.rightDrawer = false));
         }"
         save-url="Expeditions/crud"
         auto-load
-        order="created DESC"
+        :expr-builder="
+            (param, value) => {
+                switch (param) {
+                    case 'expeditionFk':
+                        return { id: value };
+                    case 'packageItemName':
+                        return { packagingItemFk: value };
+                }
+            }
+        "
     >
         <template #column-packagingItemFk="{ row }">
             <span class="link" @click.stop>

From da4c1e9c12fab1a686df8866d3b8c2e3d41f75af Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Tue, 22 Oct 2024 14:36:30 +0200
Subject: [PATCH 6/8] fix: refs #8083 add order

---
 src/pages/Ticket/Card/TicketExpedition.vue | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/pages/Ticket/Card/TicketExpedition.vue b/src/pages/Ticket/Card/TicketExpedition.vue
index 61cedc1bf..307c42645 100644
--- a/src/pages/Ticket/Card/TicketExpedition.vue
+++ b/src/pages/Ticket/Card/TicketExpedition.vue
@@ -321,6 +321,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
                 }
             }
         "
+        order="created DESC"
     >
         <template #column-packagingItemFk="{ row }">
             <span class="link" @click.stop>

From 033d6bddbee60806a5311f48ad5719639db6e844 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Tue, 22 Oct 2024 14:43:43 +0200
Subject: [PATCH 7/8] fix: refs #8083 move expeditions

---
 src/pages/Ticket/Card/TicketExpedition.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pages/Ticket/Card/TicketExpedition.vue b/src/pages/Ticket/Card/TicketExpedition.vue
index 307c42645..93749ebec 100644
--- a/src/pages/Ticket/Card/TicketExpedition.vue
+++ b/src/pages/Ticket/Card/TicketExpedition.vue
@@ -334,7 +334,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
         <ExpeditionNewTicket
             :ticket="ticketData"
             :with-route="newTicketWithRoute"
-            :selected-expeditions="selectedExpeditions"
+            :selected-expeditions="selectedRows"
         />
     </QDialog>
     <QDialog ref="logsTableDialogRef" transition-show="scale" transition-hide="scale">

From 81a55a9e7a9960dcb7f66dac86add05f3d3107f2 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Tue, 22 Oct 2024 14:55:47 +0200
Subject: [PATCH 8/8] chore: test gitea

---
 src/components/VnTable/VnTable.vue | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index 9d64591e9..9209eaf7b 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -794,6 +794,7 @@ es:
         top: 0;
     }
 }
+
 .vnTable {
     thead tr th {
         position: sticky;