From 8a3b5751574ac2f8748cbaf2ac3ce0db9171e7ed Mon Sep 17 00:00:00 2001
From: pablone <pablone@verdnatura.es>
Date: Fri, 24 May 2024 00:28:18 +0200
Subject: [PATCH 01/69] fix: refs #6346 fix list and create

---
 src/pages/Wagon/Type/WagonTypeCreate.vue | 102 ++++++++++-------------
 src/pages/Wagon/Type/WagonTypeList.vue   |   7 ++
 2 files changed, 51 insertions(+), 58 deletions(-)

diff --git a/src/pages/Wagon/Type/WagonTypeCreate.vue b/src/pages/Wagon/Type/WagonTypeCreate.vue
index bc9c1a40c..640ca75c6 100644
--- a/src/pages/Wagon/Type/WagonTypeCreate.vue
+++ b/src/pages/Wagon/Type/WagonTypeCreate.vue
@@ -4,6 +4,7 @@ import { useRoute, useRouter } from 'vue-router';
 import { useQuasar } from 'quasar';
 
 import VnInput from 'src/components/common/VnInput.vue';
+import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
 
 import { useI18n } from 'vue-i18n';
 import axios from 'axios';
@@ -12,8 +13,8 @@ onMounted(() => fetch());
 onUpdated(() => fetch());
 
 const { t } = useI18n();
+const { notify } = useQuasar();
 const route = useRoute();
-const quasar = useQuasar();
 const router = useRouter();
 const $props = defineProps({
     id: {
@@ -29,29 +30,19 @@ const divisible = ref(false);
 const name = ref('');
 const colorPickerActive = ref(false);
 let originalData = { trays: [] };
-let wagonConfig;
+let maxTrays, maxWagonHeight, minHeightBetweenTrays;
 let wagonTypeColors;
 let currentTrayColorPicked;
 
 async function fetch() {
     try {
-        await axios.get('WagonConfigs').then(async (res) => {
-            if (res.data) {
-                wagonConfig = res.data[0];
-            }
-        });
+        ({
+            data: [{ maxTrays, maxWagonHeight, minHeightBetweenTrays }],
+        } = await axios.get('WagonConfigs'));
 
         await axios.get(`WagonTypeColors`).then(async (res) => {
-            if (res.data) {
-                wagonTypeColors = res.data;
-                if (!entityId.value)
-                    wagon.value.push({
-                        id: 0,
-                        position: 0,
-                        color: { ...wagonTypeColors[0] },
-                        action: 'add',
-                    });
-                else {
+            if ((wagonTypeColors = res.data)) {
+                if (entityId.value) {
                     await axios
                         .get(`WagonTypeTrays`, {
                             params: { filter: { where: { typeFk: entityId.value } } },
@@ -76,18 +67,23 @@ async function fetch() {
                                 });
                             }
                         });
-                }
+                } else
+                    wagon.value.push({
+                        id: 0,
+                        position: 0,
+                        color: { ...wagonTypeColors[0] },
+                        action: 'add',
+                    });
             }
         });
 
-        if (entityId.value) {
+        if (entityId.value)
             await axios.get(`WagonTypes/${entityId.value}`).then((res) => {
                 if (res.data) {
                     originalData.name = name.value = res.data.name;
                     originalData.divisible = divisible.value = res.data.divisible;
                 }
             });
-        }
     } catch (e) {
         //
     }
@@ -98,27 +94,24 @@ function addTray() {
         wagon.value.find((tray) => {
             return tray.position == null;
         })
-    ) {
-        quasar.notify({
+    )
+        return notify({
             message: t('wagon.warnings.uncompleteTrays'),
             type: 'warning',
         });
-        return;
-    }
 
-    if (wagon.value.length < wagonConfig.maxTrays) {
+    if (wagon.value.length < maxTrays)
         wagon.value.unshift({
             id: wagon.value.length,
             position: null,
             color: { ...wagonTypeColors[0] },
             action: 'delete',
         });
-    } else {
-        quasar.notify({
+    else
+        notify({
             message: t('wagon.warnings.maxTrays'),
             type: 'warning',
         });
-    }
 }
 
 function deleteTray(trayToDelete) {
@@ -127,9 +120,8 @@ function deleteTray(trayToDelete) {
 }
 
 function reorderIds() {
-    for (let index = wagon.value.length - 1; index >= 0; index--) {
+    for (let index = wagon.value.length - 1; index >= 0; index--)
         wagon.value[index].id = index;
-    }
 }
 
 async function onSubmit() {
@@ -153,7 +145,7 @@ async function onSubmit() {
     }
 }
 
-function onReset() {
+async function onReset() {
     name.value = entityId.value ? originalData.name : null;
     divisible.value = entityId.value ? originalData.divisible : false;
     wagon.value = entityId.value
@@ -169,11 +161,8 @@ function onReset() {
 }
 
 function doAction(tray) {
-    if (tray.action == 'add') {
-        addTray();
-    } else {
-        deleteTray(tray);
-    }
+    if (tray.action == 'add') addTray();
+    else deleteTray(tray);
 }
 
 function showColorPicker(tray) {
@@ -193,10 +182,8 @@ function updateColor(newColor) {
 
 function onPositionBlur(tray) {
     if (tray.position) {
-        if (tray.position == '' || tray.position < 0) {
-            tray.position = null;
-            return;
-        }
+        if (tray.position == '' || tray.position < 0) return (tray.position = null);
+
         tray.position = parseInt(tray.position);
         wagon.value.sort((a, b) => b.position - a.position);
         reorderIds();
@@ -204,18 +191,18 @@ function onPositionBlur(tray) {
             if (exceedMaxHeight(index - 1)) continue;
             if (
                 wagon.value[index - 1].position - wagon.value[index].position >=
-                wagonConfig.minHeightBetweenTrays
-            ) {
+                minHeightBetweenTrays
+            )
                 continue;
-            } else {
+            else {
                 wagon.value[index - 1].position +=
-                    wagonConfig.minHeightBetweenTrays -
+                    minHeightBetweenTrays -
                     (wagon.value[index - 1].position - wagon.value[index].position);
 
-                quasar.notify({
+                notify({
                     message:
                         t('wagon.warnings.minHeightBetweenTrays') +
-                        wagonConfig.minHeightBetweenTrays +
+                        minHeightBetweenTrays +
                         ' cm',
                     type: 'warning',
                 });
@@ -227,20 +214,19 @@ function onPositionBlur(tray) {
 }
 
 function exceedMaxHeight(pos) {
-    if (wagon.value[pos].position > wagonConfig.maxWagonHeight) {
-        wagon.value.splice(pos, 1);
-        quasar.notify({
-            message:
-                t('wagon.warnings.maxWagonHeight') + wagonConfig.maxWagonHeight + ' cm',
-            type: 'warning',
-        });
-        return true;
-    }
-    return false;
+    if (wagon.value[pos].position < maxWagonHeight) return false;
+
+    wagon.value.splice(pos, 1);
+    notify({
+        message: t('wagon.warnings.maxWagonHeight') + maxWagonHeight + ' cm',
+        type: 'warning',
+    });
+    return true;
 }
 </script>
 
 <template>
+    <VnSubToolbar />
     <QPage class="q-pa-sm q-mx-xl">
         <QForm @submit="onSubmit()" @reset="onReset()" class="q-pa-sm">
             <QCard class="q-pa-md">
@@ -264,13 +250,13 @@ function exceedMaxHeight(pos) {
                             <QTooltip :delay="2000">
                                 {{
                                     t('wagon.warnings.minHeightBetweenTrays') +
-                                    wagonConfig.minHeightBetweenTrays +
+                                    minHeightBetweenTrays +
                                     ' cm'
                                 }}
                                 <QSpace />
                                 {{
                                     t('wagon.warnings.maxWagonHeight') +
-                                    wagonConfig.maxWagonHeight +
+                                    maxWagonHeight +
                                     ' cm'
                                 }}
                             </QTooltip>
diff --git a/src/pages/Wagon/Type/WagonTypeList.vue b/src/pages/Wagon/Type/WagonTypeList.vue
index 3ecca1ea3..6744dde04 100644
--- a/src/pages/Wagon/Type/WagonTypeList.vue
+++ b/src/pages/Wagon/Type/WagonTypeList.vue
@@ -56,6 +56,13 @@ async function remove(row) {
                         :id="row.id"
                         @click="navigate(row.id)"
                     >
+                        <template #list-items>
+                            <QCheckbox
+                                :label="t('Divisble')"
+                                :model-value="row.divisible"
+                                disable
+                            />
+                        </template>
                         <template #actions>
                             <QBtn
                                 :label="t('components.smartCard.openCard')"

From 12370b6437ea2b28410762d88fbb871f52f1f0dd Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Tue, 23 Jul 2024 13:22:54 +0200
Subject: [PATCH 02/69] feat: refs #6346 new wagon type section

---
 src/i18n/locale/en.yml                        |   1 +
 src/i18n/locale/es.yml                        |   1 +
 .../Customer/Defaulter/CustomerDefaulter.vue  |   3 +-
 src/pages/Wagon/Type/WagonCreateTray.vue      | 148 +++++++
 src/pages/Wagon/Type/WagonTypeCreate.vue      | 419 ------------------
 src/pages/Wagon/Type/WagonTypeEdit.vue        | 301 +++++++++++++
 src/pages/Wagon/Type/WagonTypeList.vue        |  88 +++-
 src/router/modules/wagon.js                   |   6 +-
 .../wagonType/wagonTypeCreate.spec.js         |  46 +-
 .../wagonType/wagonTypeEdit.spec.js           |  27 ++
 .../pages/Wagons/WagonTypeCreate.spec.js      | 271 -----------
 11 files changed, 551 insertions(+), 760 deletions(-)
 create mode 100644 src/pages/Wagon/Type/WagonCreateTray.vue
 delete mode 100644 src/pages/Wagon/Type/WagonTypeCreate.vue
 create mode 100644 src/pages/Wagon/Type/WagonTypeEdit.vue
 create mode 100644 test/cypress/integration/wagonType/wagonTypeEdit.spec.js
 delete mode 100644 test/vitest/__tests__/pages/Wagons/WagonTypeCreate.spec.js

diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index 8ccdf640f..d82a5dbd1 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -919,6 +919,7 @@ wagon:
         typeCreate: Create type
         typeEdit: Edit type
         wagonCounter: Trolley counter
+        wagonTray: Tray List
     type:
         name: Name
         submit: Submit
diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml
index 452421343..274edf491 100644
--- a/src/i18n/locale/es.yml
+++ b/src/i18n/locale/es.yml
@@ -907,6 +907,7 @@ wagon:
         typeCreate: Crear tipo
         typeEdit: Editar tipo
         wagonCounter: Contador de carros
+        wagonTray: Listado bandejas
     type:
         name: Nombre
         submit: Guardar
diff --git a/src/pages/Customer/Defaulter/CustomerDefaulter.vue b/src/pages/Customer/Defaulter/CustomerDefaulter.vue
index af7ce0a26..854db2801 100644
--- a/src/pages/Customer/Defaulter/CustomerDefaulter.vue
+++ b/src/pages/Customer/Defaulter/CustomerDefaulter.vue
@@ -189,7 +189,7 @@ const columns = computed(() => [
     {
         align: 'left',
         field: 'finished',
-        label: t('Has recover'),
+        label: t('Has recovery'),
         name: 'finished',
     },
 ]);
@@ -408,4 +408,5 @@ es:
     Credit I.: Crédito A.
     Credit insurance: Crédito asegurado
     From: Desde
+    Has recovery: Tiene recobro
 </i18n>
diff --git a/src/pages/Wagon/Type/WagonCreateTray.vue b/src/pages/Wagon/Type/WagonCreateTray.vue
new file mode 100644
index 000000000..19f66b0b3
--- /dev/null
+++ b/src/pages/Wagon/Type/WagonCreateTray.vue
@@ -0,0 +1,148 @@
+<script setup>
+import { ref, watch } from 'vue';
+import VnSelect from 'src/components/common/VnSelect.vue';
+import FormPopup from 'src/components/FormPopup.vue';
+import VnRow from 'src/components/ui/VnRow.vue';
+import VnInput from 'src/components/common/VnInput.vue';
+import FetchData from 'src/components/FetchData.vue';
+import { useI18n } from 'vue-i18n';
+import { useQuasar } from 'quasar';
+import axios from 'axios';
+
+const $props = defineProps({
+    entityId: {
+        type: Number,
+        required: true,
+    },
+    height: {
+        type: Number,
+        default: 0,
+    },
+    color: {
+        type: Array,
+        default: () => [],
+    },
+});
+const entityId = ref($props.entityId);
+const selectedTrayColor = ref();
+const trayHeight = ref();
+const wagonColors = ref([]);
+const wagonColorTranslated = ref();
+const heights = ref();
+const existingTrayHeight = ref($props.height);
+const { t } = useI18n();
+const { notify } = useQuasar();
+
+watch(wagonColors, () => {
+    wagonColorTranslated.value = wagonColors.value.map((color) => {
+        return { ...color, name: t(`colors.${color.name}`) };
+    });
+});
+const emit = defineEmits(['onSubmit']);
+
+async function getTrays() {
+    const data = await axios.get('WagonTypeTrays', undefined, {
+        filter: { wagonTypeFk: entityId.value },
+    });
+    existingTrayHeight.value = data.data.filter(
+        (item) => item.wagonTypeFk == entityId.value
+    );
+    heights.value = existingTrayHeight.value.map((item) => item.height);
+}
+
+function onSubmit() {
+    if (heights.value.includes(parseInt(trayHeight.value))) {
+        notify({
+            message: t(
+                'A tray with the same height already exists, try with a different height'
+            ),
+            type: 'negative',
+        });
+        return;
+    }
+    if (trayHeight.value - heights.value[heights.value.length - 1] < 50) {
+        notify({
+            message: t('The minimum height between trays is 50cm'),
+            type: 'negative',
+        });
+        return;
+    }
+
+    if (trayHeight.value > 200) {
+        notify({
+            message: t(
+                'The maximum height of the wagon is 200cm, try with a lower height'
+            ),
+            type: 'negative',
+        });
+        return;
+    }
+
+    const newTray = {
+        wagonTypeFk: entityId.value,
+        wagonTypeColorFk: selectedTrayColor.value,
+        height: trayHeight.value,
+    };
+
+    emit('onSubmit', newTray);
+}
+getTrays();
+</script>
+
+<template>
+    <FetchData
+        url="WagonTypeColors"
+        @on-fetch="(data) => (wagonColors = data)"
+        auto-load
+    />
+    <FormPopup
+        ref="createTrayFormDialogRef"
+        @on-submit="onSubmit()"
+        :title="t('Add new tray')"
+        :entity-id="selectedEntityId"
+    >
+        <template #form-inputs>
+            <VnRow class="row q-gutter-md q-mb-md">
+                <VnSelect
+                    v-model="selectedTrayColor"
+                    :options="wagonColorTranslated"
+                    option-label="name"
+                    option-value="id"
+                    id="id"
+                    :label="t('Select a tray color')"
+                >
+                    <template #option="scope">
+                        <QItem v-bind="scope.itemProps" clickable>
+                            <QItemSection>
+                                <QItemLabel>{{ scope.opt.name }}</QItemLabel>
+                            </QItemSection>
+                        </QItem>
+                    </template>
+                </VnSelect>
+                <VnInput v-model="trayHeight" :label="t('Height')" type="number" />
+            </VnRow>
+        </template>
+    </FormPopup>
+</template>
+
+<i18n>
+    en:
+        Select a tray: Select a tray
+        colors:
+            white: White
+            red: Red
+            green: Green
+            blue: Blue
+    es:
+        Select a tray color: Seleccione un color
+        Add new tray: Añadir nueva bandeja
+        Height: Altura
+        The minimum height between trays is 50cm: La altura mínima entre bandejas es de 50cm
+        The maximum height of the wagon is 200cm: La altura máxima del vagón es de 200cm
+        A tray with the same height already exists, try with a different height: Ya existe una bandeja con la misma altura, prueba con una diferente
+        colors:
+            white: Blanco
+            red: Rojo
+            green: Verde
+            blue: Azul
+</i18n>
diff --git a/src/pages/Wagon/Type/WagonTypeCreate.vue b/src/pages/Wagon/Type/WagonTypeCreate.vue
deleted file mode 100644
index 640ca75c6..000000000
--- a/src/pages/Wagon/Type/WagonTypeCreate.vue
+++ /dev/null
@@ -1,419 +0,0 @@
-<script setup>
-import { computed, ref, onMounted, onUpdated } from 'vue';
-import { useRoute, useRouter } from 'vue-router';
-import { useQuasar } from 'quasar';
-
-import VnInput from 'src/components/common/VnInput.vue';
-import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
-
-import { useI18n } from 'vue-i18n';
-import axios from 'axios';
-
-onMounted(() => fetch());
-onUpdated(() => fetch());
-
-const { t } = useI18n();
-const { notify } = useQuasar();
-const route = useRoute();
-const router = useRouter();
-const $props = defineProps({
-    id: {
-        type: Number,
-        required: false,
-        default: null,
-    },
-});
-const entityId = computed(() => $props.id || route.params.id);
-
-const wagon = ref([]);
-const divisible = ref(false);
-const name = ref('');
-const colorPickerActive = ref(false);
-let originalData = { trays: [] };
-let maxTrays, maxWagonHeight, minHeightBetweenTrays;
-let wagonTypeColors;
-let currentTrayColorPicked;
-
-async function fetch() {
-    try {
-        ({
-            data: [{ maxTrays, maxWagonHeight, minHeightBetweenTrays }],
-        } = await axios.get('WagonConfigs'));
-
-        await axios.get(`WagonTypeColors`).then(async (res) => {
-            if ((wagonTypeColors = res.data)) {
-                if (entityId.value) {
-                    await axios
-                        .get(`WagonTypeTrays`, {
-                            params: { filter: { where: { typeFk: entityId.value } } },
-                        })
-                        .then(async (res) => {
-                            if (res.data) {
-                                for (let i = 0; i < res.data.length; i++) {
-                                    const tray = res.data[i];
-                                    wagon.value.push({
-                                        id: res.data.length - i - 1,
-                                        position: tray.height,
-                                        color: {
-                                            ...wagonTypeColors.find((color) => {
-                                                return color.id === tray.colorFk;
-                                            }),
-                                        },
-                                        action: tray.height == 0 ? 'add' : 'delete',
-                                    });
-                                }
-                                wagon.value.forEach((value) => {
-                                    originalData.trays.push({ ...value });
-                                });
-                            }
-                        });
-                } else
-                    wagon.value.push({
-                        id: 0,
-                        position: 0,
-                        color: { ...wagonTypeColors[0] },
-                        action: 'add',
-                    });
-            }
-        });
-
-        if (entityId.value)
-            await axios.get(`WagonTypes/${entityId.value}`).then((res) => {
-                if (res.data) {
-                    originalData.name = name.value = res.data.name;
-                    originalData.divisible = divisible.value = res.data.divisible;
-                }
-            });
-    } catch (e) {
-        //
-    }
-}
-
-function addTray() {
-    if (
-        wagon.value.find((tray) => {
-            return tray.position == null;
-        })
-    )
-        return notify({
-            message: t('wagon.warnings.uncompleteTrays'),
-            type: 'warning',
-        });
-
-    if (wagon.value.length < maxTrays)
-        wagon.value.unshift({
-            id: wagon.value.length,
-            position: null,
-            color: { ...wagonTypeColors[0] },
-            action: 'delete',
-        });
-    else
-        notify({
-            message: t('wagon.warnings.maxTrays'),
-            type: 'warning',
-        });
-}
-
-function deleteTray(trayToDelete) {
-    wagon.value = wagon.value.filter((tray) => tray.id !== trayToDelete.id);
-    reorderIds();
-}
-
-function reorderIds() {
-    for (let index = wagon.value.length - 1; index >= 0; index--)
-        wagon.value[index].id = index;
-}
-
-async function onSubmit() {
-    try {
-        const path = entityId.value
-            ? 'WagonTypes/editWagonType'
-            : 'WagonTypes/createWagonType';
-
-        const params = {
-            id: entityId.value,
-            name: name.value,
-            divisible: divisible.value,
-            trays: wagon.value,
-        };
-
-        await axios.patch(path, params).then((res) => {
-            if (res.status == 204) router.push({ path: `/wagon/type/list` });
-        });
-    } catch (error) {
-        //
-    }
-}
-
-async function onReset() {
-    name.value = entityId.value ? originalData.name : null;
-    divisible.value = entityId.value ? originalData.divisible : false;
-    wagon.value = entityId.value
-        ? [...originalData.trays]
-        : [
-              {
-                  id: 0,
-                  position: 0,
-                  color: { ...wagonTypeColors[0] },
-                  action: 'add',
-              },
-          ];
-}
-
-function doAction(tray) {
-    if (tray.action == 'add') addTray();
-    else deleteTray(tray);
-}
-
-function showColorPicker(tray) {
-    colorPickerActive.value = true;
-    currentTrayColorPicked = wagon.value.findIndex((val) => {
-        return val.id === tray.id;
-    });
-}
-
-function updateColor(newColor) {
-    wagon.value[currentTrayColorPicked].color = {
-        ...wagonTypeColors.find((color) => {
-            return color.rgb === newColor;
-        }),
-    };
-}
-
-function onPositionBlur(tray) {
-    if (tray.position) {
-        if (tray.position == '' || tray.position < 0) return (tray.position = null);
-
-        tray.position = parseInt(tray.position);
-        wagon.value.sort((a, b) => b.position - a.position);
-        reorderIds();
-        for (let index = wagon.value.length - 1; index > 0; index--) {
-            if (exceedMaxHeight(index - 1)) continue;
-            if (
-                wagon.value[index - 1].position - wagon.value[index].position >=
-                minHeightBetweenTrays
-            )
-                continue;
-            else {
-                wagon.value[index - 1].position +=
-                    minHeightBetweenTrays -
-                    (wagon.value[index - 1].position - wagon.value[index].position);
-
-                notify({
-                    message:
-                        t('wagon.warnings.minHeightBetweenTrays') +
-                        minHeightBetweenTrays +
-                        ' cm',
-                    type: 'warning',
-                });
-
-                exceedMaxHeight(index - 1);
-            }
-        }
-    }
-}
-
-function exceedMaxHeight(pos) {
-    if (wagon.value[pos].position < maxWagonHeight) return false;
-
-    wagon.value.splice(pos, 1);
-    notify({
-        message: t('wagon.warnings.maxWagonHeight') + maxWagonHeight + ' cm',
-        type: 'warning',
-    });
-    return true;
-}
-</script>
-
-<template>
-    <VnSubToolbar />
-    <QPage class="q-pa-sm q-mx-xl">
-        <QForm @submit="onSubmit()" @reset="onReset()" class="q-pa-sm">
-            <QCard class="q-pa-md">
-                <VnInput
-                    filled
-                    v-model="name"
-                    :label="t('wagon.type.name')"
-                    :rules="[(val) => !!val || t('wagon.warnings.nameNotEmpty')]"
-                />
-                <QCheckbox class="q-mb-sm" v-model="divisible" label="Divisible" />
-                <div class="wagon-tray q-mx-lg" v-for="tray in wagon" :key="tray.id">
-                    <div class="position">
-                        <QInput
-                            autofocus
-                            filled
-                            type="number"
-                            :class="{ isVisible: tray.action == 'add' }"
-                            v-model="tray.position"
-                            @blur="onPositionBlur(tray)"
-                        >
-                            <QTooltip :delay="2000">
-                                {{
-                                    t('wagon.warnings.minHeightBetweenTrays') +
-                                    minHeightBetweenTrays +
-                                    ' cm'
-                                }}
-                                <QSpace />
-                                {{
-                                    t('wagon.warnings.maxWagonHeight') +
-                                    maxWagonHeight +
-                                    ' cm'
-                                }}
-                            </QTooltip>
-                        </QInput>
-                    </div>
-                    <div class="shelving">
-                        <div class="shelving-half">
-                            <div class="shelving-up"></div>
-                            <div
-                                class="shelving-down"
-                                :style="{ backgroundColor: tray.color.rgb }"
-                                @click="showColorPicker(tray)"
-                            ></div>
-                        </div>
-                        <div
-                            class="shelving-divisible"
-                            :class="{ isVisible: !divisible }"
-                        ></div>
-                        <div class="shelving-half">
-                            <div class="shelving-up"></div>
-                            <div
-                                class="shelving-down"
-                                :style="{ backgroundColor: tray.color.rgb }"
-                                @click="showColorPicker(tray)"
-                            ></div>
-                        </div>
-                    </div>
-                    <div class="action-button">
-                        <QBtn
-                            flat
-                            round
-                            color="primary"
-                            :icon="tray.action"
-                            @click="doAction(tray)"
-                        />
-                    </div>
-                </div>
-                <div class="q-mb-sm wheels">
-                    <QIcon color="grey-6" name="trip_origin" size="xl" />
-                    <QIcon color="grey-6" name="trip_origin" size="xl" />
-                </div>
-                <QDialog
-                    v-model="colorPickerActive"
-                    position="right"
-                    :no-backdrop-dismiss="false"
-                >
-                    <QCard>
-                        <QCardSection>
-                            <div class="text-h6">{{ t('wagon.type.trayColor') }}</div>
-                        </QCardSection>
-                        <QCardSection class="row items-center no-wrap">
-                            <QColor
-                                flat
-                                v-model="wagon[currentTrayColorPicked].color.rgb"
-                                no-header
-                                no-footer
-                                default-view="palette"
-                                :palette="
-                                    wagonTypeColors.map((color) => {
-                                        return color.rgb;
-                                    })
-                                "
-                                @change="updateColor($event)"
-                            />
-                            <QBtn flat round icon="close" v-close-popup />
-                        </QCardSection>
-                    </QCard>
-                </QDialog>
-            </QCard>
-            <div class="q-mt-md">
-                <QBtn :label="t('wagon.type.submit')" type="submit" color="primary" />
-                <QBtn
-                    :label="t('wagon.type.reset')"
-                    type="reset"
-                    color="primary"
-                    flat
-                    class="q-ml-sm"
-                />
-            </div>
-        </QForm>
-    </QPage>
-</template>
-
-<style lang="scss" scoped>
-.q-page {
-    display: flex;
-    justify-content: center;
-    align-items: flex-start;
-}
-
-.q-form {
-    width: 70%;
-}
-
-.q-dialog {
-    .q-card {
-        width: 100%;
-    }
-}
-
-.wheels {
-    margin-left: 5%;
-    display: flex;
-    justify-content: space-around;
-}
-
-.wagon-tray {
-    display: flex;
-    height: 6rem;
-
-    .position {
-        width: 20%;
-        border-right: 1rem solid gray;
-        display: flex;
-        align-items: flex-end;
-        justify-content: flex-end;
-        padding-right: 1rem;
-    }
-
-    .shelving {
-        display: flex;
-        width: 75%;
-
-        .shelving-half {
-            width: 50%;
-            height: 100%;
-
-            .shelving-up {
-                height: 80%;
-                width: 100%;
-            }
-
-            .shelving-down {
-                height: 20%;
-                width: 100%;
-            }
-        }
-
-        .shelving-divisible {
-            width: 1%;
-            height: 100%;
-            border-left: 0.5rem dashed grey;
-            border-right: 0.5rem dashed grey;
-        }
-    }
-
-    .action-button {
-        width: 10%;
-        border-left: 1rem solid gray;
-        display: flex;
-        align-items: flex-end;
-        justify-content: flex-start;
-        padding-left: 1rem;
-    }
-
-    .isVisible {
-        display: none;
-    }
-}
-</style>
diff --git a/src/pages/Wagon/Type/WagonTypeEdit.vue b/src/pages/Wagon/Type/WagonTypeEdit.vue
new file mode 100644
index 000000000..25ba05714
--- /dev/null
+++ b/src/pages/Wagon/Type/WagonTypeEdit.vue
@@ -0,0 +1,301 @@
+<script setup>
+import { computed, ref, watch } from 'vue';
+import { useRoute } from 'vue-router';
+import { useQuasar } from 'quasar';
+import VnInput from 'src/components/common/VnInput.vue';
+import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
+import FormModel from 'src/components/FormModel.vue';
+import { useI18n } from 'vue-i18n';
+import axios from 'axios';
+import VnPaginate from 'components/ui/VnPaginate.vue';
+import WagonCreateTray from './WagonCreateTray.vue';
+
+const { t } = useI18n();
+const { notify } = useQuasar();
+const route = useRoute();
+const entityId = computed(() => route.params.id);
+const wagonTrays = ref([]);
+const createTrayFormDialogRef = ref();
+const selectedEntityId = ref();
+
+async function loadTrays() {
+    try {
+        const res = await axios.get('WagonTypeTrays');
+        const filteredTrays = res.data.filter(
+            (tray) => tray.wagonTypeFk === entityId.value
+        );
+        wagonTrays.value = filteredTrays;
+        return;
+    } catch (err) {
+        console.error('Error loading trays:', err);
+    }
+}
+
+async function addTray(newTray) {
+    try {
+        const res = await axios.post(`WagonTypeTrays`, newTray);
+        wagonTrays.value.push(res.data);
+        notify({
+            message: t(`Tray added successfully`),
+            type: 'positive',
+        });
+    } catch (err) {
+        console.log('err: ', err);
+    }
+}
+
+async function deleteTray(trayToDelete) {
+    try {
+        await axios.delete(`WagonTypeTrays/${trayToDelete.id}`);
+        const index = wagonTrays.value.findIndex((tray) => tray.id === trayToDelete.id);
+        if (index !== -1) {
+            wagonTrays.value.splice(index, 1);
+        }
+        notify({
+            message: t('Tray deleted successfully'),
+            type: 'positive',
+        });
+    } catch (err) {
+        console.log('err: ', err);
+    }
+}
+
+const filter = {
+    where: { wagonTypeFk: entityId.value },
+    include: {
+        relation: 'color',
+        scope: {
+            fields: ['rgb'],
+        },
+    },
+};
+
+const formFilter = { where: { wagonTypeFk: entityId.value } };
+
+const showCreateTrayForm = (id) => {
+    selectedEntityId.value = id;
+    createTrayFormDialogRef.value.show();
+};
+
+watch(
+    () => wagonTrays.value,
+    async (newVal, oldVal) => {
+        if (newVal.length !== oldVal.length) {
+            await loadTrays();
+        }
+    },
+    { deep: true }
+);
+</script>
+
+<template>
+    <VnSubToolbar />
+    <QPage class="q-pa-sm q-mx-xl">
+        <FormModel
+            :url="`WagonTypes/ ${entityId}`"
+            :url-update="`WagonTypes/ ${entityId}`"
+            :filter="formFilter"
+            model="WagonType"
+            auto-load
+        >
+            <template #form="{ data }">
+                <QCard class="q-pa-md">
+                    <VnInput
+                        filled
+                        v-model="data.name"
+                        :label="t('wagon.type.name')"
+                        :rules="[(val) => !!val || t('wagon.warnings.nameNotEmpty')]"
+                    />
+                    <QCheckbox
+                        class="q-mb-sm"
+                        v-model="data.divisible"
+                        label="Divisible"
+                    />
+
+                    <VnPaginate
+                        data-key="wagonTypeTray"
+                        url="WagonTypeTrays"
+                        order="id DESC"
+                        :filter="filter"
+                        auto-load
+                        ref="vnPaginateRef"
+                        v-bind="$attrs"
+                        :key="wagonTrays.length"
+                    >
+                        <template #body="{ rows }">
+                            <div v-for="row in rows" :key="row.id">
+                                <div class="shelving"></div>
+                                <div class="action-button">
+                                    <div class="wagon-tray q-mx-lg">
+                                        <div class="position">
+                                            <VnInput
+                                                borderless
+                                                type="number"
+                                                disable
+                                                class="input-tray q-mb-sm"
+                                                :label="t('Height') + ': '"
+                                                v-model="row.height"
+                                            />
+                                        </div>
+                                        <div class="shelving">
+                                            <div class="shelving-half">
+                                                <div class="shelving-up"></div>
+                                                <div
+                                                    class="shelving-down"
+                                                    :style="{
+                                                        backgroundColor: row.color.rgb,
+                                                    }"
+                                                ></div>
+                                            </div>
+                                            <div
+                                                class="shelving-divisible"
+                                                :class="{ isVisible: !data.divisible }"
+                                            ></div>
+                                            <div class="shelving-half">
+                                                <div class="shelving-up"></div>
+                                                <div
+                                                    class="shelving-down"
+                                                    :style="{
+                                                        backgroundColor: row.color.rgb,
+                                                    }"
+                                                ></div>
+                                            </div>
+                                        </div>
+                                        <div class="action-button">
+                                            <QBtn
+                                                v-if="row.height === 0"
+                                                flat
+                                                round
+                                                color="primary"
+                                                :icon="'add'"
+                                                class="btn-tray"
+                                                @click="showCreateTrayForm(entityId)"
+                                            />
+                                            <QBtn
+                                                v-else
+                                                flat
+                                                round
+                                                color="primary"
+                                                :icon="'delete'"
+                                                class="btn-tray"
+                                                @click="deleteTray(row)"
+                                            />
+                                        </div>
+                                    </div>
+                                </div>
+                            </div>
+                        </template>
+                    </VnPaginate>
+                    <div class="q-mb-sm wheels">
+                        <QIcon color="grey-6" name="trip_origin" size="xl" />
+                        <QIcon color="grey-6" name="trip_origin" size="xl" />
+                    </div>
+                </QCard>
+                <QDialog ref="createTrayFormDialogRef">
+                    <WagonCreateTray :entity-id="entityId" @on-submit="addTray($event)" />
+                </QDialog>
+            </template>
+        </FormModel>
+    </QPage>
+</template>
+
+<style lang="scss" scoped>
+.q-page {
+    display: flex;
+    justify-content: center;
+    align-items: flex-start;
+}
+
+.q-form {
+    width: 70%;
+}
+
+.q-dialog {
+    .q-card {
+        width: 100%;
+    }
+}
+
+.wheels {
+    margin-left: 5%;
+    display: flex;
+    justify-content: space-around;
+}
+
+.wagon-tray {
+    display: flex;
+    height: 6rem;
+
+    .position {
+        width: 26%;
+        border-right: 1rem solid gray;
+        display: flex;
+        align-items: flex-start;
+        justify-content: flex-start;
+        padding-right: 2rem;
+        padding-left: 3rem;
+    }
+
+    .shelving {
+        display: flex;
+        width: 54%;
+
+        .shelving-half {
+            width: 100%;
+            height: 100%;
+
+            .shelving-up {
+                height: 80%;
+                width: 100%;
+            }
+
+            .shelving-down {
+                height: 20%;
+                width: 100%;
+            }
+        }
+
+        .shelving-divisible {
+            width: 1%;
+            height: 100%;
+            border-left: 0.5rem dashed grey;
+            border-right: 0.5rem dashed grey;
+        }
+    }
+
+    .action-button {
+        width: 20%;
+        border-left: 1rem solid gray;
+        align-items: baseline;
+        padding-left: 3rem;
+    }
+
+    .isVisible {
+        display: none;
+    }
+}
+.btn-tray {
+    margin-right: 100%;
+    margin-top: 100%;
+}
+.input-tray {
+    margin-top: 100%;
+    margin-left: 40%;
+}
+</style>
+
+<i18n>
+    en:
+        tray: Tray
+        wagonColor: Wagon color
+        Select a tray: Select a tray
+    es:
+        tray: Bandeja
+        wagonColor: Color de la bandeja
+        Select a tray: Seleccione una bandeja
+        Create new Wagon type: Crear nuevo tipo de vagón
+        Add new tray: Añadir nueva bandeja
+        Height: Altura
+        Tray added successfully: Bandeja añadida correctamente
+        Tray deleted successfully: Bandeja eliminada correctamente
+</i18n>
diff --git a/src/pages/Wagon/Type/WagonTypeList.vue b/src/pages/Wagon/Type/WagonTypeList.vue
index 6744dde04..acd6f14be 100644
--- a/src/pages/Wagon/Type/WagonTypeList.vue
+++ b/src/pages/Wagon/Type/WagonTypeList.vue
@@ -1,4 +1,5 @@
 <script setup>
+import { ref, computed } from 'vue';
 import axios from 'axios';
 import { useQuasar } from 'quasar';
 import VnPaginate from 'src/components/ui/VnPaginate.vue';
@@ -6,36 +7,40 @@ import { useArrayData } from 'src/composables/useArrayData';
 import { useI18n } from 'vue-i18n';
 import { useRouter } from 'vue-router';
 import CardList from 'components/ui/CardList.vue';
+import FormModelPopup from 'src/components/FormModelPopup.vue';
+import VnInput from 'src/components/common/VnInput.vue';
+import VnRow from 'src/components/ui/VnRow.vue';
 
 const quasar = useQuasar();
 const arrayData = useArrayData('WagonTypeList');
 const store = arrayData.store;
-const router = useRouter();
+const dialog = ref();
+const { push } = useRouter();
 const { t } = useI18n();
+const paginate = ref();
 
-function navigate(id) {
-    router.push({ path: `/wagon/type/${id}/edit` });
+const initialData = computed(() => {
+    return {
+        name: null,
+    };
+});
+
+function reloadData() {
+    initialData.value.name = null;
+    paginate.value.fetch();
 }
 
-function create() {
-    router.push({ path: `/wagon/type/create` });
+function navigate(id, name) {
+    push({ path: `/wagon/type/${id}/edit`, query: { name } });
 }
 
 async function remove(row) {
-    try {
-        const id = row.id;
-        await axios
-            .delete(`WagonTypes/deleteWagonType`, { params: { id } })
-            .then(async () => {
-                quasar.notify({
-                    message: t('wagon.type.removeItem'),
-                    type: 'positive',
-                });
-                store.data.splice(store.data.indexOf(row), 1);
-            });
-    } catch (error) {
-        //
-    }
+    await axios.delete(`WagonTypes/${row.id}`);
+    quasar.notify({
+        message: t('wagon.type.removeItem'),
+        type: 'positive',
+    });
+    store.data.splice(store.data.indexOf(row), 1);
 }
 </script>
 
@@ -43,8 +48,9 @@ async function remove(row) {
     <QPage class="column items-center q-pa-md">
         <div class="vn-card-list">
             <VnPaginate
+                ref="paginate"
                 data-key="WagonTypeList"
-                url="/WagonTypes"
+                url="WagonTypes"
                 order="id DESC"
                 auto-load
             >
@@ -54,7 +60,7 @@ async function remove(row) {
                         :key="row.id"
                         :title="(row.name || '').toString()"
                         :id="row.id"
-                        @click="navigate(row.id)"
+                        @click="navigate(row.id, row.name)"
                     >
                         <template #list-items>
                             <QCheckbox
@@ -66,7 +72,7 @@ async function remove(row) {
                         <template #actions>
                             <QBtn
                                 :label="t('components.smartCard.openCard')"
-                                @click.stop="navigate(row.id)"
+                                @click.stop="navigate(row.id, row.name)"
                                 outline
                             />
                             <QBtn
@@ -80,8 +86,42 @@ async function remove(row) {
                 </template>
             </VnPaginate>
         </div>
-        <QPageSticky position="bottom-right" :offset="[18, 18]">
-            <QBtn @click="create" fab icon="add" color="primary" />
+        <QPageSticky :offset="[18, 18]">
+            <QBtn @click.stop="dialog.show()" color="primary" fab icon="add">
+                <QDialog ref="dialog">
+                    <FormModelPopup
+                        :title="t('Create new Wagon type')"
+                        url-create="WagonTypes"
+                        model="WagonType"
+                        :form-initial-data="initialData"
+                        @on-data-saved="reloadData()"
+                        auto-load
+                    >
+                        <template #form-inputs="{ data }">
+                            <VnRow class="row q-gutter-md q-mb-md">
+                                <VnInput
+                                    filled
+                                    v-model="data.name"
+                                    :label="t('Name')"
+                                    :rules="[(val) => !!val || t('nameNotEmpty')]"
+                                />
+                            </VnRow>
+                        </template>
+                    </FormModelPopup>
+                </QDialog>
+            </QBtn>
+            <QTooltip>
+                {{ t('globals.new') }}
+            </QTooltip>
         </QPageSticky>
     </QPage>
 </template>
+
+<i18n>
+en:
+    nameNotEmpty: The name cannot be empty
+es:
+    Create new Wagon type: Crear nuevo tipo de vagón
+    Name: Nombre
+    nameNotEmpty: El nombre no puede estar vacío
+</i18n>
diff --git a/src/router/modules/wagon.js b/src/router/modules/wagon.js
index 6f9a4c819..ecdf7dff0 100644
--- a/src/router/modules/wagon.js
+++ b/src/router/modules/wagon.js
@@ -11,7 +11,7 @@ export default {
     component: RouterView,
     redirect: { name: 'WagonMain' },
     menus: {
-        main: ['WagonList', 'WagonTypeList', 'WagonCounter'],
+        main: ['WagonList', 'WagonTypeList', 'WagonCounter', 'WagonTray'],
         card: [],
     },
     children: [
@@ -81,7 +81,7 @@ export default {
                         title: 'typeCreate',
                         icon: 'create',
                     },
-                    component: () => import('src/pages/Wagon/Type/WagonTypeCreate.vue'),
+                    component: () => import('src/pages/Wagon/Type/WagonTypeList.vue'),
                 },
                 {
                     path: ':id/edit',
@@ -90,7 +90,7 @@ export default {
                         title: 'typeEdit',
                         icon: 'edit',
                     },
-                    component: () => import('src/pages/Wagon/Type/WagonTypeCreate.vue'),
+                    component: () => import('src/pages/Wagon/Type/WagonTypeEdit.vue'),
                 },
             ],
         },
diff --git a/test/cypress/integration/wagonType/wagonTypeCreate.spec.js b/test/cypress/integration/wagonType/wagonTypeCreate.spec.js
index bcf7fe841..05d61c9c5 100644
--- a/test/cypress/integration/wagonType/wagonTypeCreate.spec.js
+++ b/test/cypress/integration/wagonType/wagonTypeCreate.spec.js
@@ -5,50 +5,12 @@ describe('WagonTypeCreate', () => {
         cy.visit('/#/wagon/type/create');
     });
 
-    function chooseColor(color) {
-        cy.get('div.shelving-down').eq(1).click();
-        cy.get('div.q-color-picker__cube').eq(color).click();
-        cy.get('div.q-card__section').find('button').click();
-    }
-
-    function addTray(position) {
-        cy.get('div.action-button').last().find('button').click();
-        cy.focused().type(position);
-        cy.focused().blur();
-    }
-
-    it('should create and delete a new wagon type', () => {
+    it('should create a new wagon type', () => {
+        cy.get('.q-page-sticky > div > .q-btn').click();
         cy.get('input').first().type('Example for testing');
-        cy.get('div.q-checkbox__bg').click();
-        chooseColor(1);
-
-        // Insert invalid position (not minimal height)
-        addTray(20);
-        cy.get('div[role="alert"]').should('exist');
-        chooseColor(2);
-        addTray(150);
-        chooseColor(3);
-        addTray(100);
-
-        // Insert invalid position (max height reached)
-        addTray(210);
-        cy.get('div[role="alert"]').should('exist');
-
-        // Save
         cy.get('button[type="submit"]').click();
-
-        // Check data has been saved successfully
-        cy.get(':nth-child(1) > :nth-child(1) > .justify-between > .flex > .title')
-            .contains('Example for testing')
-            .click();
-        cy.get('input').first().should('have.value', 'Example for testing');
-        cy.get('div.wagon-tray').should('have.length', 4);
-        cy.get('div.position').eq(0).find('input').should('have.value', '150');
-        cy.get('div.position').eq(1).find('input').should('have.value', '100');
-        cy.get('div.position').eq(2).find('input').should('have.value', '50');
-
-        // Delete wagon type created
-        cy.go('back');
+    });
+    it('delete a wagon type', () => {
         cy.get(
             ':nth-child(2) > :nth-child(1) > .card-list-body > .actions > .q-btn--standard'
         ).click();
diff --git a/test/cypress/integration/wagonType/wagonTypeEdit.spec.js b/test/cypress/integration/wagonType/wagonTypeEdit.spec.js
new file mode 100644
index 000000000..6e5816e51
--- /dev/null
+++ b/test/cypress/integration/wagonType/wagonTypeEdit.spec.js
@@ -0,0 +1,27 @@
+describe('WagonTypeEdit', () => {
+    const trayColorRow =
+        '.q-select > .q-field__inner > .q-field__control > .q-field__control-container';
+    beforeEach(() => {
+        cy.viewport(1920, 1080);
+        cy.login('developer');
+        cy.visit('/#/wagon/type/2/edit');
+    });
+
+    it('should edit the name and the divisible field of the wagon type', () => {
+        cy.get('.q-card');
+        cy.get('input').first().type(' changed');
+        cy.get('div.q-checkbox__bg').first().click();
+        cy.get('.q-btn--standard').click();
+    });
+
+    it('should create a tray', () => {
+        cy.get('.action-button > .q-btn > .q-btn__content > .q-icon').click();
+        cy.get('input').last().type('150');
+        cy.get(trayColorRow).type('{downArrow}{downArrow}{enter}');
+    });
+
+    it('should delete a tray', () => {
+        cy.get('.action-button > .q-btn > .q-btn__content > .q-icon').first().click();
+        cy.reload();
+    });
+});
diff --git a/test/vitest/__tests__/pages/Wagons/WagonTypeCreate.spec.js b/test/vitest/__tests__/pages/Wagons/WagonTypeCreate.spec.js
deleted file mode 100644
index 60c199b73..000000000
--- a/test/vitest/__tests__/pages/Wagons/WagonTypeCreate.spec.js
+++ /dev/null
@@ -1,271 +0,0 @@
-import { axios, createWrapper } from 'app/test/vitest/helper';
-import WagonTypeCreate from 'pages/Wagon/Type/WagonTypeCreate.vue';
-import { afterEach, beforeAll, describe, expect, it, vi } from 'vitest';
-
-describe('WagonTypeCreate', () => {
-    let vmCreate, vmEdit;
-    const entityId = 1;
-
-    beforeAll(() => {
-        vmEdit = createWrapper(WagonTypeCreate, {propsData: {
-                id: entityId,
-            }}).vm;
-        vmCreate = createWrapper(WagonTypeCreate).vm;
-        vmEdit.wagonConfig = vmCreate.wagonConfig = {maxTrays: 2 ,minHeightBetweenTrays: 50, maxWagonHeight: 200 };
-        vmEdit.wagonTypeColors = vmCreate.wagonTypeColors = [{id: 1, color:'white', rgb:'#000000'}];
-    });
-
-    afterEach(() => {
-        vi.clearAllMocks();
-    });
-
-    describe('addTray()', () => {
-        it('should throw message if there are uncomplete trays', async () => {
-            vi.spyOn(vmEdit.quasar, 'notify');
-            vmEdit.wagon = [{
-                id: 1,
-                position: null,
-                color: vmEdit.wagonTypeColors[0]
-            }];
-
-            await vmEdit.addTray();
-
-            expect(vmEdit.quasar.notify).toHaveBeenCalledWith(
-                expect.objectContaining({
-                    type: 'warning',
-                })
-            );
-        });
-
-        it('should create a new tray if the limit has not been reached', async () => {
-            vmEdit.wagon = [{
-                id: 1,
-                position: 0,
-                color: vmEdit.wagonTypeColors[0]
-            }];
-
-            await vmEdit.addTray();
-            expect(vmEdit.wagon.length).toEqual(2);
-        });
-
-        it('should throw message if there are uncomplete trays', async () => {
-            vi.spyOn(vmEdit.quasar, 'notify');
-            vmEdit.wagon = [{
-                id: 1,
-                position: 0,
-                color: vmEdit.wagonTypeColors[0]
-            },{
-                id: 2,
-                position: 50,
-                color: vmEdit.wagonTypeColors[0]
-            }];
-
-            await vmEdit.addTray();
-
-            expect(vmEdit.quasar.notify).toHaveBeenCalledWith(
-                expect.objectContaining({
-                    type: 'warning',
-                })
-            );
-        });
-    });
-
-    describe('deleteTray() reorderIds()', () => {
-        it('should delete a tray and reorder the ids', async () => {
-            const trayToDelete = {
-                id: 1,
-                position: 0,
-                color: vmEdit.wagonTypeColors[0]
-            };
-            const trayMaintained = {
-                id: 2,
-                position: 50,
-                color: vmEdit.wagonTypeColors[0]
-            };
-            vmEdit.wagon = [trayToDelete,trayMaintained];
-
-            await vmEdit.deleteTray(trayToDelete);
-
-            expect(vmEdit.wagon.length).toEqual(1);
-            expect(vmEdit.wagon[0].id).toEqual(0);
-            expect(vmEdit.wagon[0].position).toEqual(50);
-
-        });
-    });
-
-    describe('onSubmit()', () => {
-        it('should make a patch to editWagonType if have id', async () => {
-            vi.spyOn(axios, 'patch').mockResolvedValue({ data: true });
-            const wagon = {
-                id: entityId,
-                name: "Mock name",
-                divisible: true,
-                trays: [{
-                    id: 1,
-                    position: 0,
-                    color: vmEdit.wagonTypeColors[0]
-                }]
-            }
-            vmEdit.name = wagon.name;
-            vmEdit.divisible = wagon.divisible;
-            vmEdit.wagon = wagon.trays;
-
-            await vmEdit.onSubmit();
-
-            expect(axios.patch).toHaveBeenCalledWith(
-                `WagonTypes/editWagonType`, wagon
-            );
-        });
-
-        it('should make a patch to createtWagonType if not have id', async () => {
-            vi.spyOn(axios, 'patch').mockResolvedValue({ data: true });
-            const wagon = {
-                name: "Mock name",
-                divisible: true,
-                trays: [{
-                    id: 1,
-                    position: 0,
-                    color: vmCreate.wagonTypeColors[0]
-                }]
-            }
-            vmCreate.name = wagon.name;
-            vmCreate.divisible = wagon.divisible;
-            vmCreate.wagon = wagon.trays;
-
-            await vmCreate.onSubmit();
-
-            expect(axios.patch).toHaveBeenCalledWith(
-                `WagonTypes/createWagonType`, wagon
-            );
-        });
-    });
-
-    describe('onReset()', () => {
-        it('should reset if have id', async () => {
-            vmEdit.name = 'Changed name';
-            vmEdit.divisible = false;
-            vmEdit.wagon = [];
-            vmEdit.originalData = {
-                name: 'Original name',
-                divisible: true,
-                trays: [{
-                    id: 1,
-                    position: 0,
-                    color: vmEdit.wagonTypeColors[0]
-                },{
-                    id: 2,
-                    position: 50,
-                    color: vmEdit.wagonTypeColors[0]
-                }]
-            };
-
-            vmEdit.onReset();
-
-            expect(vmEdit.name).toEqual(vmEdit.originalData.name);
-            expect(vmEdit.divisible).toEqual(vmEdit.originalData.divisible);
-            expect(vmEdit.wagon).toEqual(vmEdit.originalData.trays);
-        });
-
-        it('should reset if not have id', async () => {
-            vmCreate.name = 'Changed name';
-            vmCreate.divisible = false;
-            vmCreate.wagon = [];
-
-            vmCreate.onReset();
-
-            expect(vmCreate.name).toEqual(null);
-            expect(vmCreate.divisible).toEqual(false);
-            expect(vmCreate.wagon.length).toEqual(1);
-        });
-    });
-
-    describe('onPositionBlur()', () => {
-        it('should set position null if position is negative', async () => {
-            const negativeTray = {
-                id: 1,
-                position: -1,
-                color: vmCreate.wagonTypeColors[0]
-            };
-
-            vmCreate.onPositionBlur(negativeTray);
-
-            expect(negativeTray.position).toEqual(null);
-        });
-
-        it('should set position and reorder array', async () => {
-            const trays = [{
-                id: 0,
-                position: 100,
-                color: vmCreate.wagonTypeColors[0]
-            },{
-                id: 1,
-                position: 0,
-                color: vmCreate.wagonTypeColors[0]
-            }];
-            const newTray = {
-                id: 2,
-                position: 50,
-                color: vmCreate.wagonTypeColors[0]
-            };
-            trays.push(newTray);
-            vmCreate.wagon = trays;
-
-            vmCreate.onPositionBlur(newTray);
-
-            expect(vmCreate.wagon[0].position).toEqual(100);
-            expect(vmCreate.wagon[1].position).toEqual(50);
-            expect(vmCreate.wagon[2].position).toEqual(0);
-        });
-
-        it('should throw message if not have min height between trays and should set new adequate positions', async () => {
-            vi.spyOn(vmCreate.quasar, 'notify');
-            const trays = [{
-                id: 0,
-                position: 0,
-                color: vmCreate.wagonTypeColors[0]
-            }];
-            const newTray = {
-                id: 1,
-                position: 20,
-                color: vmCreate.wagonTypeColors[0]
-            };
-            trays.push(newTray);
-            vmCreate.wagon = trays;
-
-            vmCreate.onPositionBlur(newTray);
-
-            expect(vmCreate.wagon[0].position).toEqual(50);
-            expect(vmCreate.wagon[1].position).toEqual(0);
-            expect(vmCreate.quasar.notify).toHaveBeenCalledWith(
-                expect.objectContaining({
-                    type: 'warning',
-                })
-            );
-        });
-
-        it('should throw message if max height has been exceed', async () => {
-            vi.spyOn(vmCreate.quasar, 'notify');
-            const trays = [{
-                id: 0,
-                position: 0,
-                color: vmCreate.wagonTypeColors[0]
-            }];
-            const newTray = {
-                id: 1,
-                position: 210,
-                color: vmCreate.wagonTypeColors[0]
-            };
-            trays.push(newTray);
-            vmCreate.wagon = trays;
-
-            vmCreate.onPositionBlur(newTray);
-
-            expect(vmCreate.wagon.length).toEqual(1);
-            expect(vmCreate.quasar.notify).toHaveBeenCalledWith(
-                expect.objectContaining({
-                    type: 'warning',
-                })
-            );
-        });
-    });
-});

From 3a0204d27bde863207db6d8a621611cd7085dc18 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Wed, 24 Jul 2024 14:58:51 +0200
Subject: [PATCH 03/69] feat: vnLocation changes

---
 src/components/common/VnLocation.vue          | 149 +++++++++++-------
 .../Customer/Card/CustomerFiscalData.vue      |   6 +-
 .../Supplier/Card/SupplierFiscalData.vue      |  10 +-
 .../vnComponent/vnLocation.spec.js            |   2 +-
 4 files changed, 98 insertions(+), 69 deletions(-)

diff --git a/src/components/common/VnLocation.vue b/src/components/common/VnLocation.vue
index 9ed48ca15..fc19e509f 100644
--- a/src/components/common/VnLocation.vue
+++ b/src/components/common/VnLocation.vue
@@ -1,18 +1,23 @@
 <script setup>
-import { ref, toRefs, computed, watch, onMounted } from 'vue';
+import { ref, toRefs, onMounted, nextTick } from 'vue';
 import CreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
 import VnSelectDialog from 'components/common/VnSelectDialog.vue';
-import FetchData from 'components/FetchData.vue';
-const emit = defineEmits(['update:modelValue', 'update:options']);
+const emit = defineEmits(['update:selected', 'update:options']);
 import { useI18n } from 'vue-i18n';
+import { useArrayData } from 'src/composables/useArrayData';
 
 const { t } = useI18n();
 const postcodesOptions = ref([]);
-const postcodesRef = ref(null);
+const postcodesOptionsOriginal = ref([]);
+// const postcodesRef = ref(null);
 
 const $props = defineProps({
-    modelValue: {
-        type: [String, Number, Object],
+    postalCodeKey: {
+        type: String,
+        default: 'postalCode',
+    },
+    location: {
+        type: Object,
         default: null,
     },
     options: {
@@ -41,89 +46,115 @@ const $props = defineProps({
     },
 });
 
-const { options } = toRefs($props);
-const myOptions = ref([]);
-const myOptionsOriginal = ref([]);
-
-const value = computed({
-    get() {
-        return $props.modelValue;
-    },
-    set(value) {
-        emit(
-            'update:modelValue',
-            postcodesOptions.value.find((p) => p.code === value)
-        );
-    },
+const selectedId = ref(null);
+const mySelect = ref();
+const modelValue = ref($props.location[$props.postalCodeKey]);
+const arrayData = useArrayData('postcodes', {
+    url: 'Postcodes/filter',
+    limit: 1,
 });
 
-onMounted(() => {
-    locationFilter($props.modelValue);
-});
+function sanitizePostcode(data) {
+    return data.map((postcode) => ({
+        ...postcode,
+        original: postcode,
+        id: sanitizeId(postcode),
+        label: sanitizeLabel(postcode),
+    }));
+}
 
+function sanitizeId(postcode) {
+    return `${postcode.code ?? postcode[$props.postalCodeKey]}_${postcode.provinceFk}_${
+        postcode.countryFk
+    }`;
+}
+onMounted(async () => {
+    await retriveOptions();
+    nextTick(() => mySelect.value.showPopup());
+});
+async function retriveOptions() {
+    let options = [];
+    if (modelValue.value) {
+        await locationFilter(modelValue.value, () => {});
+        options = arrayData.store.data;
+    } else {
+        const { data } = await arrayData.fetch({ updateRouter: false, append: true });
+        options = data;
+    }
+    setOptions(options);
+}
+function findOptionById(postcode) {
+    return postcodesOptions.value.find((p) => p.id === postcode);
+}
 function setOptions(data) {
-    myOptions.value = JSON.parse(JSON.stringify(data));
-    myOptionsOriginal.value = JSON.parse(JSON.stringify(data));
-}
-setOptions(options.value);
-
-watch(options, (newValue) => {
-    setOptions(newValue);
-});
-
-function showLabel(data) {
-    return `${data.code} - ${data.town}(${data.province}), ${data.country}`;
+    postcodesOptions.value = sanitizePostcode(data);
+    if (modelValue.value) {
+        const { [$props.postalCodeKey]: code } = $props.location;
+        selectedId.value =
+            findOptionById(sanitizeId({ ...$props.location, code })) ??
+            sanitizePostcode([$props.location])[0];
+    }
+    postcodesOptionsOriginal.value = JSON.parse(JSON.stringify(postcodesOptions.value));
 }
 
-function locationFilter(search = '') {
-    if (
-        search &&
-        (search.includes('undefined') || search.startsWith(`${$props.modelValue} - `))
-    )
+async function handleInput(value) {
+    if (value) emit('update:selected', findOptionById(value));
+    if (modelValue.value) {
+        modelValue.value = value;
+        arrayData.store.userFilter = {};
+        await retriveOptions();
+        mySelect.value.showPopup();
+    } else postcodesOptions.value = postcodesOptionsOriginal.value;
+}
+function sanitizeLabel(postcode) {
+    return `${postcode.code ?? postcode[$props.postalCodeKey]} - ${postcode.town}(${
+        postcode.province
+    }), ${postcode.country}`;
+}
+
+async function locationFilter(search, update) {
+    if (search.length === 0) {
         return;
+    }
     let where = { search };
-    postcodesRef.value.fetch({ filter: { where }, limit: 30 });
+    arrayData.store.userFilter = { filter: { where } };
+    await arrayData.fetch({ append: false, updateRouter: false });
+
+    update(() => {
+        postcodesOptions.value = sanitizePostcode(arrayData.store.data);
+    });
 }
 
-function handleFetch(data) {
-    postcodesOptions.value = data;
-}
 function onDataSaved(newPostcode) {
     postcodesOptions.value.push(newPostcode);
-    value.value = newPostcode.code;
+    selectedId.value = newPostcode.code ?? newPostcode[$props.postalCodeKey];
 }
 </script>
 <template>
-    <FetchData
-        ref="postcodesRef"
-        url="Postcodes/filter"
-        @on-fetch="(data) => handleFetch(data)"
-    />
     <VnSelectDialog
-        v-if="postcodesRef"
-        :option-label="(opt) => showLabel(opt) ?? 'code'"
-        :option-value="(opt) => opt.code"
-        v-model="value"
+        ref="mySelect"
+        option-label="label"
+        option-value="id"
+        v-model="selectedId"
         :options="postcodesOptions"
         :label="t('Location')"
+        @update:model-value="handleInput"
         :placeholder="t('search_by_postalcode')"
-        @input-value="locationFilter"
-        :default-filter="false"
+        @filter="locationFilter"
         :input-debounce="300"
         :class="{ required: $attrs.required }"
         v-bind="$attrs"
         clearable
+        emit-value
     >
         <template #form>
-            <CreateNewPostcode
-                @on-data-saved="onDataSaved"
-            />
+            <CreateNewPostcode @on-data-saved="onDataSaved" />
         </template>
         <template #option="{ itemProps, opt }">
             <QItem v-bind="itemProps">
                 <QItemSection v-if="opt.code">
                     <QItemLabel>{{ opt.code }}</QItemLabel>
-                    <QItemLabel caption>{{ showLabel(opt) }}</QItemLabel>
+                    <QItemLabel caption>{{ opt.label }}</QItemLabel>
                 </QItemSection>
             </QItem>
         </template>
diff --git a/src/pages/Customer/Card/CustomerFiscalData.vue b/src/pages/Customer/Card/CustomerFiscalData.vue
index a7cdfafc5..d05e4fa01 100644
--- a/src/pages/Customer/Card/CustomerFiscalData.vue
+++ b/src/pages/Customer/Card/CustomerFiscalData.vue
@@ -95,9 +95,9 @@ function handleLocation(data, location) {
                 <VnLocation
                     :rules="validate('Worker.postcode')"
                     :roles-allowed-to-create="['deliveryAssistant']"
-                    :options="postcodesOptions"
-                    v-model="data.postcode"
-                    @update:model-value="(location) => handleLocation(data, location)"
+                    :location="data"
+                    postal-code-key="postcode"
+                    @update:selected="(location) => handleLocation(data, location)"
                 >
                 </VnLocation>
             </VnRow>
diff --git a/src/pages/Supplier/Card/SupplierFiscalData.vue b/src/pages/Supplier/Card/SupplierFiscalData.vue
index 179c6f5e6..61077d48e 100644
--- a/src/pages/Supplier/Card/SupplierFiscalData.vue
+++ b/src/pages/Supplier/Card/SupplierFiscalData.vue
@@ -17,11 +17,10 @@ const sageTaxTypesOptions = ref([]);
 const sageWithholdingsOptions = ref([]);
 const sageTransactionTypesOptions = ref([]);
 const supplierActivitiesOptions = ref([]);
-const postcodesOptions = ref([]);
 
 function handleLocation(data, location) {
-    const { town, code, provinceFk, countryFk } = location ?? {};
-    data.postCode = code;
+    const { town, label, provinceFk, countryFk } = location ?? {};
+    data.postCode = label;
     data.city = town;
     data.provinceFk = provinceFk;
     data.countryFk = countryFk;
@@ -131,9 +130,8 @@ function handleLocation(data, location) {
                 <VnLocation
                     :rules="validate('Worker.postcode')"
                     :roles-allowed-to-create="['deliveryAssistant']"
-                    :options="postcodesOptions"
-                    v-model="data.postCode"
-                    @update:model-value="(location) => handleLocation(data, location)"
+                    :location="data"
+                    @update:selected="(location) => handleLocation(data, location)"
                 >
                 </VnLocation>
             </VnRow>
diff --git a/test/cypress/integration/vnComponent/vnLocation.spec.js b/test/cypress/integration/vnComponent/vnLocation.spec.js
index 6719d8391..c465b152c 100644
--- a/test/cypress/integration/vnComponent/vnLocation.spec.js
+++ b/test/cypress/integration/vnComponent/vnLocation.spec.js
@@ -17,7 +17,7 @@ describe('VnLocation', () => {
             cy.get(inputLocation).click();
             cy.get(inputLocation).clear();
             cy.get(inputLocation).type('al');
-            cy.get(locationOptions).should('have.length.at.least', 3);
+            cy.get(locationOptions).should('have.length.at.least', 4);
         });
         it('input filter location as "ecuador"', function () {
             cy.get(inputLocation).click();

From 4188569908e57aee9b938e976b670779dd7fd733 Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Thu, 25 Jul 2024 10:03:20 +0200
Subject: [PATCH 04/69] feat: refs #7500 added VnImg to show files

---
 src/components/common/VnDmsList.vue | 33 +++++++++++++++++++++--------
 src/components/ui/VnImg.vue         | 11 +++++++++-
 2 files changed, 34 insertions(+), 10 deletions(-)

diff --git a/src/components/common/VnDmsList.vue b/src/components/common/VnDmsList.vue
index c42de6690..08c4e4aa4 100644
--- a/src/components/common/VnDmsList.vue
+++ b/src/components/common/VnDmsList.vue
@@ -5,12 +5,14 @@ import { useRoute } from 'vue-router';
 import { useQuasar, QCheckbox, QBtn, QInput } from 'quasar';
 import axios from 'axios';
 
+import VnUserLink from '../ui/VnUserLink.vue';
+import { downloadFile } from 'src/composables/downloadFile';
+import VnImg from 'components/ui/VnImg.vue';
 import VnPaginate from 'components/ui/VnPaginate.vue';
 import VnDms from 'src/components/common/VnDms.vue';
 import VnConfirm from 'components/ui/VnConfirm.vue';
 import VnInputDate from 'components/common/VnInputDate.vue';
-import VnUserLink from '../ui/VnUserLink.vue';
-import { downloadFile } from 'src/composables/downloadFile';
+import { useSession } from 'src/composables/useSession';
 
 const route = useRoute();
 const quasar = useQuasar();
@@ -18,6 +20,7 @@ const { t } = useI18n();
 const rows = ref();
 const dmsRef = ref();
 const formDialog = ref({});
+const token = useSession().getTokenMultimedia();
 
 const $props = defineProps({
     model: {
@@ -89,6 +92,25 @@ const dmsFilter = {
 };
 
 const columns = computed(() => [
+    {
+        align: 'left',
+        field: 'file',
+        label: t('globals.file'),
+        name: 'file',
+        component: VnImg,
+        props: (prop) => ({
+            storage: 'dms',
+            collection: null,
+            size: null,
+            token: prop.token,
+            url:
+                'api/dms/' +
+                prop.row.file.split('.')[0] +
+                '/downloadFile?access_token=' +
+                token,
+            class: 'rounded',
+        }),
+    },
     {
         align: 'left',
         field: 'id',
@@ -141,13 +163,6 @@ const columns = computed(() => [
             'model-value': Boolean(prop.value),
         }),
     },
-    {
-        align: 'left',
-        field: 'file',
-        label: t('globals.file'),
-        name: 'file',
-        component: 'span',
-    },
     {
         align: 'left',
         field: 'worker',
diff --git a/src/components/ui/VnImg.vue b/src/components/ui/VnImg.vue
index 985c9cc53..fc54bddf5 100644
--- a/src/components/ui/VnImg.vue
+++ b/src/components/ui/VnImg.vue
@@ -24,6 +24,11 @@ const $props = defineProps({
         type: Number,
         required: true,
     },
+    url: {
+        type: String,
+        required: false,
+        default: null,
+    },
 });
 const show = ref(false);
 const token = useSession().getTokenMultimedia();
@@ -31,9 +36,13 @@ const timeStamp = ref(`timestamp=${Date.now()}`);
 import noImage from '/no-user.png';
 import { useRole } from 'src/composables/useRole';
 const url = computed(() => {
+    if ($props.url) return $props.url;
     const isEmployee = useRole().isEmployee();
+    const _url = [$props.storage, $props.collection, $props.size, $props.id]
+        .filter((prop) => prop)
+        .join('/');
     return isEmployee
-        ? `/api/${$props.storage}/${$props.collection}/${$props.size}/${$props.id}/download?access_token=${token}&${timeStamp.value}`
+        ? `/api/${_url}/download?access_token=${token}&${timeStamp.value}`
         : noImage;
 });
 const reload = () => {

From 2a4180a1c364cdd88fadbb7ef0a9a597fe8c7cc4 Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Fri, 2 Aug 2024 13:20:46 +0200
Subject: [PATCH 05/69] refactor: refs #6346 deleted front error checking

---
 src/pages/Wagon/Type/WagonCreateTray.vue | 30 +-----------------------
 1 file changed, 1 insertion(+), 29 deletions(-)

diff --git a/src/pages/Wagon/Type/WagonCreateTray.vue b/src/pages/Wagon/Type/WagonCreateTray.vue
index 19f66b0b3..c3f8b3847 100644
--- a/src/pages/Wagon/Type/WagonCreateTray.vue
+++ b/src/pages/Wagon/Type/WagonCreateTray.vue
@@ -6,7 +6,6 @@ import VnRow from 'src/components/ui/VnRow.vue';
 import VnInput from 'src/components/common/VnInput.vue';
 import FetchData from 'src/components/FetchData.vue';
 import { useI18n } from 'vue-i18n';
-import { useQuasar } from 'quasar';
 import axios from 'axios';
 
 const $props = defineProps({
@@ -31,7 +30,6 @@ const wagonColorTranslated = ref();
 const heights = ref();
 const existingTrayHeight = ref($props.height);
 const { t } = useI18n();
-const { notify } = useQuasar();
 
 watch(wagonColors, () => {
     wagonColorTranslated.value = wagonColors.value.map((color) => {
@@ -51,33 +49,6 @@ async function getTrays() {
 }
 
 function onSubmit() {
-    if (heights.value.includes(parseInt(trayHeight.value))) {
-        notify({
-            message: t(
-                'A tray with the same height already exists, try with a different height'
-            ),
-            type: 'negative',
-        });
-        return;
-    }
-    if (trayHeight.value - heights.value[heights.value.length - 1] < 50) {
-        notify({
-            message: t('The minimum height between trays is 50cm'),
-            type: 'negative',
-        });
-        return;
-    }
-
-    if (trayHeight.value > 200) {
-        notify({
-            message: t(
-                'The maximum height of the wagon is 200cm, try with a lower height'
-            ),
-            type: 'negative',
-        });
-        return;
-    }
-
     const newTray = {
         wagonTypeFk: entityId.value,
         wagonTypeColorFk: selectedTrayColor.value,
@@ -110,6 +81,7 @@ getTrays();
                     option-value="id"
                     id="id"
                     :label="t('Select a tray color')"
+                    :required="true"
                 >
                     <template #option="scope">
                         <QItem v-bind="scope.itemProps" clickable>

From f2be2b430010d647f2547a8a6d8a2b7604b894dd Mon Sep 17 00:00:00 2001
From: pablone <pablone@verdnatura.es>
Date: Mon, 5 Aug 2024 15:42:57 +0200
Subject: [PATCH 06/69] fea: stockBought add form and formDetail

---
 src/components/VnTable/VnTable.vue            |  14 +-
 src/components/common/VnInputDate.vue         |   2 +-
 src/components/common/VnInputTime.vue         |   2 +-
 src/i18n/locale/en.yml                        |   1 +
 src/pages/Entry/EntryStockBought.vue          | 142 ++++++++++++++++++
 src/pages/Entry/EntryStockBoughtDetail.vue    | 102 +++++++++++++
 src/pages/Route/RouteList.vue                 |   3 +-
 src/router/modules/entry.js                   |  11 +-
 .../integration/entry/stockBought.spec.js     |  36 +++++
 9 files changed, 302 insertions(+), 11 deletions(-)
 create mode 100644 src/pages/Entry/EntryStockBought.vue
 create mode 100644 src/pages/Entry/EntryStockBoughtDetail.vue
 create mode 100644 test/cypress/integration/entry/stockBought.spec.js

diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index 4e40c0ba6..8a20023f2 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -107,7 +107,7 @@ const orders = ref(parseOrder(routeQuery.filter?.order));
 const CrudModelRef = ref({});
 const showForm = ref(false);
 const splittedColumns = ref({ columns: [] });
-const columnsVisibilitySkiped = ref();
+const columnsVisibilitySkipped = ref();
 const tableModes = [
     {
         icon: 'view_column',
@@ -133,7 +133,7 @@ onMounted(() => {
             ? CARD_MODE
             : $props.defaultMode;
     stateStore.rightDrawer = true;
-    columnsVisibilitySkiped.value = [
+    columnsVisibilitySkipped.value = [
         ...splittedColumns.value.columns
             .filter((c) => c.visible == false)
             .map((c) => c.name),
@@ -325,11 +325,11 @@ defineExpose({
     <div class="q-px-md">
         <CrudModel
             v-bind="$attrs"
-            :limit="20"
+            :limit="$attrs.limit ?? 20"
             ref="CrudModelRef"
             @on-fetch="(...args) => emit('onFetch', ...args)"
             :search-url="searchUrl"
-            :disable-infinite-scroll="isTableMode"
+            :disable-infinite-scroll="$attrs['disable-infinite-scroll'] ?? isTableMode"
             @save-changes="reload"
             :has-sub-toolbar="$attrs['hasSubToolbar'] ?? isEditable"
             :auto-load="hasParams || $attrs['auto-load']"
@@ -351,9 +351,8 @@ defineExpose({
                     :grid="!isTableMode"
                     table-header-class="bg-header"
                     card-container-class="grid-three"
-                    flat
                     :style="isTableMode && `max-height: ${tableHeight}`"
-                    virtual-scroll
+                    :virtual-scroll="!isTableMode"
                     @virtual-scroll="
                         (event) =>
                             event.index > rows.length - 2 &&
@@ -361,6 +360,7 @@ defineExpose({
                     "
                     @row-click="(_, row) => rowClickFunction && rowClickFunction(row)"
                     @update:selected="emit('update:selected', $event)"
+                    flat
                 >
                     <template #top-left v-if="!$props.withoutHeader">
                         <slot name="top-left"></slot>
@@ -370,7 +370,7 @@ defineExpose({
                             v-if="isTableMode"
                             v-model="splittedColumns.columns"
                             :table-code="tableCode ?? route.name"
-                            :skip="columnsVisibilitySkiped"
+                            :skip="columnsVisibilitySkipped"
                         />
                         <QBtnToggle
                             v-model="mode"
diff --git a/src/components/common/VnInputDate.vue b/src/components/common/VnInputDate.vue
index 6e57a8a53..76eee86fc 100644
--- a/src/components/common/VnInputDate.vue
+++ b/src/components/common/VnInputDate.vue
@@ -45,7 +45,7 @@ const formattedDate = computed({
         if (value) {
             // parse input
             if (value.includes('/')) {
-                if (value.length == 6) value = value + new Date().getFullYear();
+                if (value.length == 6) value = value + Date.vnNew().getFullYear();
                 if (value.length >= 10) {
                     if (value.at(2) == '/') value = value.split('/').reverse().join('/');
                     value = date.formatDate(
diff --git a/src/components/common/VnInputTime.vue b/src/components/common/VnInputTime.vue
index a59f0e9e8..2c387bf0b 100644
--- a/src/components/common/VnInputTime.vue
+++ b/src/components/common/VnInputTime.vue
@@ -62,7 +62,7 @@ const formattedTime = computed({
 function dateToTime(newDate) {
     return date.formatDate(new Date(newDate), dateFormat);
 }
-const timeField = ref();
+
 watch(
     () => model.value,
     (val) => (formattedTime.value = val),
diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index 78c1fa18d..867f338e4 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -142,6 +142,7 @@ globals:
         dms: File management
         entryCreate: New entry
         latestBuys: Latest buys
+        reserves: Reserves
         tickets: Tickets
         ticketCreate: New Tickets
         boxing: Boxing
diff --git a/src/pages/Entry/EntryStockBought.vue b/src/pages/Entry/EntryStockBought.vue
new file mode 100644
index 000000000..5277cc5e1
--- /dev/null
+++ b/src/pages/Entry/EntryStockBought.vue
@@ -0,0 +1,142 @@
+<script setup>
+import { ref } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { useState } from 'src/composables/useState';
+import { useQuasar } from 'quasar';
+
+import VnTable from 'components/VnTable/VnTable.vue';
+import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
+import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
+import EntryStockBoughtDetail from 'src/pages/Entry/EntryStockBoughtDetail.vue';
+
+const { t } = useI18n();
+const quasar = useQuasar();
+const tableRef = ref();
+const state = useState();
+const user = state.getUser();
+const columns = [
+    {
+        align: 'left',
+        label: 'Id',
+        name: 'id',
+        isTitle: true,
+        isId: true,
+        columnFilter: false,
+        visible: false,
+    },
+    {
+        align: 'left',
+        name: 'workerFk',
+        label: t('Buyer'),
+        columnFilter: false,
+    },
+    {
+        align: 'left',
+        label: t('Reserve'),
+        name: 'reserve',
+        columnFilter: false,
+        create: true,
+        component: 'number',
+    },
+    {
+        align: 'left',
+        name: 'workerFk',
+        label: t('Buyer'),
+        component: 'select',
+        create: true,
+        visible: false,
+        columnFilter: false,
+        attrs: {
+            url: 'Workers/activeWithInheritedRole',
+            fields: ['id', 'name'],
+            where: { role: 'buyer' },
+            optionFilter: 'firstName',
+            optionLabel: 'name',
+            optionValue: 'id',
+            useLike: false,
+        },
+    },
+    {
+        align: 'left',
+        label: t('Bought'),
+        name: 'bought',
+        columnFilter: false,
+    },
+    {
+        align: 'left',
+        label: t('Date'),
+        name: 'dated',
+        component: 'date',
+        visible: false,
+        create: true,
+    },
+    {
+        align: 'left',
+        name: 'tableActions',
+        actions: [
+            {
+                title: t('More'),
+                icon: 'search',
+                isPrimary: true,
+                action: (row) => {
+                    console.log('workerFk: ', row.workerFk);
+                    quasar.dialog({
+                        component: EntryStockBoughtDetail,
+                        componentProps: {
+                            workerFk: row.workerFk,
+                            dated: row.dated,
+                        },
+                        maximized: true,
+                    });
+                },
+            },
+        ],
+    },
+];
+</script>
+<template>
+    <VnSubToolbar />
+    <VnTable
+        ref="tableRef"
+        data-key="StockBoughts"
+        url="StockBoughts/getStockBought"
+        save-url="StockBoughts/crud"
+        order="reserve DESC"
+        :is-editable="true"
+        :disable-option="{ card: true }"
+        :create="{
+            urlCreate: 'StockBoughts',
+            title: t('Reserve some space'),
+            onDataSaved: () => tableRef.reload(),
+            formInitialData: {
+                workerFk: user.id,
+                dated: Date.now(),
+            },
+        }"
+        :columns="columns"
+    >
+        <template #column-workerFk="{ row }">
+            <span class="link" @click.stop>
+                {{ row?.worker?.user?.name }}
+                <WorkerDescriptorProxy :id="row?.workerFk" />
+            </span>
+        </template>
+    </VnTable>
+</template>
+<i18n>
+    en:
+        Buyer: Buyer
+        Reserve: Reserve
+        Bought: Bought
+        More: More
+        Date: Date
+        This buyer has already made a reservation for this date: This buyer has already made a reservation for this date
+    es:
+        Buyer: Comprador
+        Reserve: Reservado
+        Bought: Comprado
+        More: Más
+        Date: Fecha
+        Reserve some space: Reservar espacio
+        This buyer has already made a reservation for this date: Este comprador ya ha hecho una reserva para esta fecha
+</i18n>
diff --git a/src/pages/Entry/EntryStockBoughtDetail.vue b/src/pages/Entry/EntryStockBoughtDetail.vue
new file mode 100644
index 000000000..908f5dd20
--- /dev/null
+++ b/src/pages/Entry/EntryStockBoughtDetail.vue
@@ -0,0 +1,102 @@
+<script setup>
+import { ref } from 'vue';
+import { useI18n } from 'vue-i18n';
+
+import VnTable from 'components/VnTable/VnTable.vue';
+import EntryDescriptorProxy from 'src/pages/Entry/Card/EntryDescriptorProxy.vue';
+import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
+
+const { t } = useI18n();
+const tableRef = ref();
+const $props = defineProps({
+    workerFk: {
+        type: Number,
+        required: true,
+    },
+    dated: {
+        type: String,
+        required: true,
+    },
+});
+console.log('$props: ', $props.workerFk, $props.dated);
+const customUrl = `StockBoughts/getStockBoughtDetail?workerFk=${$props.workerFk}`;
+const columns = [
+    {
+        align: 'left',
+        label: 'Entry',
+        name: 'entryFk',
+        isTitle: true,
+        isId: true,
+        columnFilter: false,
+    },
+    {
+        align: 'left',
+        name: 'itemFk',
+        label: t('Item'),
+        columnFilter: false,
+    },
+    {
+        align: 'left',
+        label: t('Name'),
+        name: 'itemName',
+        columnFilter: false,
+        create: true,
+        columnClass: 'expand',
+    },
+    {
+        align: 'left',
+        name: 'volume',
+        label: t('Volume'),
+        columnFilter: false,
+    },
+    {
+        align: 'left',
+        label: t('Packaging'),
+        name: 'packagingFk',
+        columnFilter: false,
+    },
+    {
+        align: 'left',
+        label: 'Packing',
+        name: 'packing',
+        columnFilter: false,
+    },
+];
+</script>
+<template>
+    <QDialog>
+        <VnTable
+            ref="tableRef"
+            data-key="StockBoughtsDetail"
+            :url="customUrl"
+            order="itemName DESC"
+            :columns="columns"
+            :right-search="false"
+            :disable-infinite-scroll="true"
+            :disable-option="{ card: true }"
+            :limit="0"
+            auto-load
+        >
+            <template #column-entryFk="{ row }">
+                <span class="link" @click.stop>
+                    {{ row?.entryFk }}
+                    <EntryDescriptorProxy :id="row.entryFk" />
+                </span>
+            </template>
+            <template #column-itemName="{ row }">
+                <span class="link" @click.stop>
+                    {{ row?.itemName }}
+                    <ItemDescriptorProxy :id="row.itemFk" />
+                </span>
+            </template>
+        </VnTable>
+    </QDialog>
+</template>
+<i18n>
+    es:
+        Buyer: Comprador
+        Reserve: Reservado
+        Bought: Comprado
+        More: Más
+        Date: Fecha
+</i18n>
diff --git a/src/pages/Route/RouteList.vue b/src/pages/Route/RouteList.vue
index b13e8cacd..7d5dd5c68 100644
--- a/src/pages/Route/RouteList.vue
+++ b/src/pages/Route/RouteList.vue
@@ -139,7 +139,7 @@ const columns = computed(() => [
     {
         align: 'center',
         name: 'm3',
-        label: 'volume',
+        label: t('Volume'),
         cardVisible: true,
     },
     {
@@ -379,4 +379,5 @@ es:
     Route is not served: La ruta no está servida
     hourStarted: Hora de inicio
     hourFinished: Hora de fin
+    Volume: Volumen
 </i18n>
diff --git a/src/router/modules/entry.js b/src/router/modules/entry.js
index 0d38ed626..fd5490fbd 100644
--- a/src/router/modules/entry.js
+++ b/src/router/modules/entry.js
@@ -11,7 +11,7 @@ export default {
     component: RouterView,
     redirect: { name: 'EntryMain' },
     menus: {
-        main: ['EntryList', 'MyEntries', 'EntryLatestBuys'],
+        main: ['EntryList', 'MyEntries', 'EntryLatestBuys', 'EntryStockBought'],
         card: ['EntryBasicData', 'EntryBuys', 'EntryNotes', 'EntryDms', 'EntryLog'],
     },
     children: [
@@ -57,6 +57,15 @@ export default {
                     },
                     component: () => import('src/pages/Entry/EntryLatestBuys.vue'),
                 },
+                {
+                    path: 'stock-Bought',
+                    name: 'EntryStockBought',
+                    meta: {
+                        title: 'reserves',
+                        icon: 'deployed_code_history',
+                    },
+                    component: () => import('src/pages/Entry/EntryStockBought.vue'),
+                },
             ],
         },
         {
diff --git a/test/cypress/integration/entry/stockBought.spec.js b/test/cypress/integration/entry/stockBought.spec.js
new file mode 100644
index 000000000..8e4fe225c
--- /dev/null
+++ b/test/cypress/integration/entry/stockBought.spec.js
@@ -0,0 +1,36 @@
+describe('EntryStockBought', () => {
+    const reserveField = 'input[name="reserve"]';
+    beforeEach(() => {
+        cy.viewport(1920, 1080);
+        cy.login('buyer');
+        cy.visit(
+            `/#/entry/stock-Bought?table={"filter":"{}","dated":"2000-12-31T23:00:00.000Z"}`
+        );
+    });
+    it('Should edit the reserved space', () => {
+        cy.get('tBody > tr').its('length').should('eq', 1);
+        cy.get(reserveField).type('10{enter}');
+        cy.get('button[title="Save"]').click();
+        cy.get('.q-notification__message').should('have.text', 'Data saved');
+    });
+    it('Should add a new reserved space for buyerBoss', () => {
+        cy.get('.q-page-sticky > div > .q-btn > .q-btn__content > .q-icon').click();
+        cy.get('input[aria-label="Reserve"]').type('1');
+        cy.get('input[aria-label="Date"]').eq(1).clear();
+        cy.get('input[aria-label="Date"]').eq(1).type('01-01');
+        cy.get('input[aria-label="Buyer"]').type('buyerboss{downarrow}{enter}');
+        cy.get('.q-notification__message').should('have.text', 'Data created');
+        cy.get('tBody > tr').its('length').should('eq', 2);
+    });
+    it('Should check detail for the buyer', () => {
+        cy.get(':nth-child(1) > .sticky > .q-btn > .q-btn__content > .q-icon').click();
+        cy.get('tBody > tr').eq(1).its('length').should('eq', 1);
+    });
+    it('Should check detail for the buyerBoss and had no content', () => {
+        cy.get(':nth-child(2) > .sticky > .q-btn > .q-btn__content > .q-icon').click();
+        cy.get('.q-table__bottom.row.items-center.q-table__bottom--nodata').should(
+            'have.text',
+            'warningNo data available'
+        );
+    });
+});

From de94b3629e6470ba0f8ba9f6b3fad807c6ec01ab Mon Sep 17 00:00:00 2001
From: pablone <pablone@verdnatura.es>
Date: Mon, 5 Aug 2024 15:44:37 +0200
Subject: [PATCH 07/69] fix: remove console.log

---
 src/pages/Entry/EntryStockBought.vue       | 1 -
 src/pages/Entry/EntryStockBoughtDetail.vue | 1 -
 2 files changed, 2 deletions(-)

diff --git a/src/pages/Entry/EntryStockBought.vue b/src/pages/Entry/EntryStockBought.vue
index 5277cc5e1..e0063bb62 100644
--- a/src/pages/Entry/EntryStockBought.vue
+++ b/src/pages/Entry/EntryStockBought.vue
@@ -79,7 +79,6 @@ const columns = [
                 icon: 'search',
                 isPrimary: true,
                 action: (row) => {
-                    console.log('workerFk: ', row.workerFk);
                     quasar.dialog({
                         component: EntryStockBoughtDetail,
                         componentProps: {
diff --git a/src/pages/Entry/EntryStockBoughtDetail.vue b/src/pages/Entry/EntryStockBoughtDetail.vue
index 908f5dd20..f7e89c691 100644
--- a/src/pages/Entry/EntryStockBoughtDetail.vue
+++ b/src/pages/Entry/EntryStockBoughtDetail.vue
@@ -18,7 +18,6 @@ const $props = defineProps({
         required: true,
     },
 });
-console.log('$props: ', $props.workerFk, $props.dated);
 const customUrl = `StockBoughts/getStockBoughtDetail?workerFk=${$props.workerFk}`;
 const columns = [
     {

From ffd7d98e9cc25cf218b2c577d0c7ef98df7cf59b Mon Sep 17 00:00:00 2001
From: pablone <pablone@verdnatura.es>
Date: Tue, 13 Aug 2024 17:56:07 +0200
Subject: [PATCH 08/69] feat: travel m3

---
 src/components/VnTable/VnTable.vue         | 20 +++++++++++
 src/pages/Entry/EntryStockBought.vue       | 39 +++++++++++++++++++++-
 src/pages/Entry/EntryStockBoughtDetail.vue |  2 +-
 src/pages/Travel/Card/TravelBasicData.vue  |  3 ++
 4 files changed, 62 insertions(+), 2 deletions(-)

diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index 8a20023f2..dbe5d1622 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -15,6 +15,7 @@ import VnTableChip from 'components/VnTable/VnChip.vue';
 import VnVisibleColumn from 'src/components/VnTable/VnVisibleColumn.vue';
 import VnLv from 'components/ui/VnLv.vue';
 import VnTableOrder from 'src/components/VnTable/VnOrder.vue';
+import item from 'src/router/modules/item';
 
 const $props = defineProps({
     columns: {
@@ -609,6 +610,25 @@ defineExpose({
                             </QCard>
                         </component>
                     </template>
+                    <template #bottom-row="{ cols }">
+                        <QTr>
+                            <QTh
+                                v-for="col of cols.filter((cols) => cols.visible ?? true)"
+                                :key="col.id"
+                                class="text-center"
+                            >
+                                <span v-if="col.summation">
+                                    {{
+                                        rows.reduce(
+                                            (sum, currentRow) =>
+                                                sum + currentRow[col.name],
+                                            0
+                                        )
+                                    }}
+                                </span>
+                            </QTh>
+                        </QTr>
+                    </template>
                 </QTable>
             </template>
         </CrudModel>
diff --git a/src/pages/Entry/EntryStockBought.vue b/src/pages/Entry/EntryStockBought.vue
index e0063bb62..12fc96214 100644
--- a/src/pages/Entry/EntryStockBought.vue
+++ b/src/pages/Entry/EntryStockBought.vue
@@ -5,6 +5,10 @@ import { useState } from 'src/composables/useState';
 import { useQuasar } from 'quasar';
 
 import VnTable from 'components/VnTable/VnTable.vue';
+import VnRow from 'src/components/ui/VnRow.vue';
+import VnInput from 'src/components/common/VnInput.vue';
+import VnLv from 'src/components/ui/VnLv.vue';
+import FetchData from 'src/components/FetchData.vue';
 import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
 import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
 import EntryStockBoughtDetail from 'src/pages/Entry/EntryStockBoughtDetail.vue';
@@ -37,6 +41,7 @@ const columns = [
         columnFilter: false,
         create: true,
         component: 'number',
+        summation: true,
     },
     {
         align: 'left',
@@ -61,6 +66,7 @@ const columns = [
         label: t('Bought'),
         name: 'bought',
         columnFilter: false,
+        summation: true,
     },
     {
         align: 'left',
@@ -92,6 +98,21 @@ const columns = [
         ],
     },
 ];
+function getFilter(dated = Date.vnNow()) {
+    console.log('dated: ', new Date(dated * 1000));
+    return {
+        fields: ['id', 'm3'],
+        where: { dated },
+        include: [
+            {
+                relation: 'warehouseIn',
+                where: { code: 'vnh' },
+            },
+        ],
+    };
+}
+const travel = ref();
+const fetchDataRef = ref();
 </script>
 <template>
     <VnSubToolbar />
@@ -109,11 +130,27 @@ const columns = [
             onDataSaved: () => tableRef.reload(),
             formInitialData: {
                 workerFk: user.id,
-                dated: Date.now(),
+                dated: Date.vnNow(),
             },
         }"
         :columns="columns"
+        auto-load
+        @on-fetch="() => fetchDataRef.fetch()"
     >
+        <template #moreFilterPanel="{ params }">
+            <FetchData
+                ref="fetchDataRef"
+                url="Travels"
+                limit="1"
+                auto-load
+                :filter="getFilter(params?.date)"
+                @on-fetch="(data) => (travel = data)"
+            />
+            <VnRow class="q-pa-md" style="align-items: center" v-if="travel?.length">
+                <span>{{ t('Booked trucks: ') + travel[0]?.m3 }}</span>
+                <QBtn style="max-width: 20%" flat icon="edit" />
+            </VnRow>
+        </template>
         <template #column-workerFk="{ row }">
             <span class="link" @click.stop>
                 {{ row?.worker?.user?.name }}
diff --git a/src/pages/Entry/EntryStockBoughtDetail.vue b/src/pages/Entry/EntryStockBoughtDetail.vue
index f7e89c691..9212f1115 100644
--- a/src/pages/Entry/EntryStockBoughtDetail.vue
+++ b/src/pages/Entry/EntryStockBoughtDetail.vue
@@ -38,9 +38,9 @@ const columns = [
         align: 'left',
         label: t('Name'),
         name: 'itemName',
-        columnFilter: false,
         create: true,
         columnClass: 'expand',
+        columnFilter: false,
     },
     {
         align: 'left',
diff --git a/src/pages/Travel/Card/TravelBasicData.vue b/src/pages/Travel/Card/TravelBasicData.vue
index 8a369a0dd..a3620a6ba 100644
--- a/src/pages/Travel/Card/TravelBasicData.vue
+++ b/src/pages/Travel/Card/TravelBasicData.vue
@@ -70,6 +70,9 @@ const agenciesOptions = ref([]);
                     hide-selected
                 />
             </VnRow>
+            <VnRow>
+                <VnInput v-model="data.m3" label="m3" />
+            </VnRow>
             <VnRow>
                 <QCheckbox
                     :label="t('travel.basicData.delivered')"

From 92130b4c9c770c6cc49e1253885f7837bcba29cf Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Wed, 14 Aug 2024 08:09:27 +0200
Subject: [PATCH 09/69] WIP: 59262019 Merge pull request '#7283 finish item
 card sections' (!588) from 7283-itemSectionsMigration into dev

---
 src/components/common/VnLocation.vue | 103 +++------------------------
 1 file changed, 9 insertions(+), 94 deletions(-)

diff --git a/src/components/common/VnLocation.vue b/src/components/common/VnLocation.vue
index 4d429a995..8256ec5b0 100644
--- a/src/components/common/VnLocation.vue
+++ b/src/components/common/VnLocation.vue
@@ -1,124 +1,39 @@
 <script setup>
-import { ref, toRefs, computed, watch, onMounted } from 'vue';
 import CreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
 import VnSelectDialog from 'components/common/VnSelectDialog.vue';
-import FetchData from 'components/FetchData.vue';
-const emit = defineEmits(['update:modelValue', 'update:options']);
 import { useI18n } from 'vue-i18n';
-import { useArrayData } from 'src/composables/useArrayData';
 
 const { t } = useI18n();
-const postcodesOptions = ref([]);
-const postcodesRef = ref(null);
-
-const $props = defineProps({
-    modelValue: {
-        type: [String, Number, Object],
-        default: null,
-    },
-    options: {
-        type: Array,
-        default: () => [],
-    },
-    optionLabel: {
-        type: String,
-        default: '',
-    },
-    optionValue: {
-        type: String,
-        default: '',
-    },
-    filterOptions: {
-        type: Array,
-        default: () => [],
-    },
-    isClearable: {
-        type: Boolean,
-        default: true,
-    },
-    defaultFilter: {
-        type: Boolean,
-        default: true,
-    },
-});
-
-const { options } = toRefs($props);
-const myOptions = ref([]);
-const myOptionsOriginal = ref([]);
-
-const value = computed({
-    get() {
-        return $props.modelValue;
-    },
-    set(value) {
-        emit(
-            'update:modelValue',
-            postcodesOptions.value.find((p) => p.code === value)
-        );
-    },
-});
-
-onMounted(() => {
-    locationFilter($props.modelValue);
-});
-
-function setOptions(data) {
-    myOptions.value = JSON.parse(JSON.stringify(data));
-    myOptionsOriginal.value = JSON.parse(JSON.stringify(data));
-}
-setOptions(options.value);
-
-watch(options, (newValue) => {
-    setOptions(newValue);
-});
+const value = defineModel({ type: [String, Number, Object] });
 
 function showLabel(data) {
     return `${data.code} - ${data.town}(${data.province}), ${data.country}`;
 }
-
-function locationFilter(search = '') {
-    if (
-        search &&
-        (search.includes('undefined') || search.startsWith(`${$props.modelValue} - `))
-    )
-        return;
-    let where = { search };
-    postcodesRef.value.fetch({ filter: { where }, limit: 30 });
-}
-
-function handleFetch(data) {
-    postcodesOptions.value = data;
-}
-function onDataSaved(newPostcode) {
-    postcodesOptions.value.push(newPostcode);
-    value.value = newPostcode.code;
-}
 </script>
 <template>
     <VnSelectDialog
-        v-if="postcodesRef"
-        :option-label="(opt) => showLabel(opt) ?? 'code'"
-        :option-value="(opt) => opt.code"
         v-model="value"
-        :options="postcodesOptions"
+        option-value="code"
+        option-filter-value="search"
+        :option-label="(opt) => showLabel(opt)"
+        url="Postcodes/filter"
+        :use-like="false"
         :label="t('Location')"
-        @update:model-value="handleInput"
         :placeholder="t('search_by_postalcode')"
-        @input-value="locationFilter"
-        :default-filter="false"
         :input-debounce="300"
         :class="{ required: $attrs.required }"
         v-bind="$attrs"
         clearable
+        :emit-value="false"
     >
         <template #form>
-            <CreateNewPostcode @on-data-saved="onDataSaved" />
+            <CreateNewPostcode @on-data-saved="(newValue) => (value = newValue)" />
         </template>
         <template #option="{ itemProps, opt }">
             <QItem v-bind="itemProps">
                 <QItemSection v-if="opt.code">
                     <QItemLabel>{{ opt.code }}</QItemLabel>
-                    <QItemLabel caption>{{ opt.label }}</QItemLabel>
+                    <QItemLabel caption>{{ showLabel(opt) }}</QItemLabel>
                 </QItemSection>
             </QItem>
         </template>

From 55b9cd9ff44f29fb95da4958c29d18ea1ae4207d Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Wed, 14 Aug 2024 09:18:24 +0200
Subject: [PATCH 10/69] feat: handle same multiple CP

---
 src/components/common/VnLocation.vue          | 24 ++++++++++++-----
 src/i18n/locale/en.yml                        |  1 +
 src/i18n/locale/es.yml                        |  1 +
 src/pages/Customer/Card/CustomerAddress.vue   | 26 ++++++-------------
 .../Customer/Card/CustomerFiscalData.vue      |  2 +-
 src/pages/Customer/CustomerCreate.vue         |  1 -
 src/pages/Customer/CustomerList.vue           |  1 -
 .../components/CustomerAddressCreate.vue      |  1 -
 .../components/CustomerAddressEdit.vue        |  7 ++++-
 src/pages/Supplier/Card/SupplierAddresses.vue |  8 +++++-
 .../Supplier/Card/SupplierAddressesCreate.vue | 12 ++++++++-
 .../Supplier/Card/SupplierFiscalData.vue      | 24 ++++++++++++++++-
 src/pages/Worker/WorkerCreate.vue             |  1 -
 src/pages/Worker/WorkerList.vue               |  1 -
 src/router/modules/customer.js                |  2 +-
 15 files changed, 77 insertions(+), 35 deletions(-)

diff --git a/src/components/common/VnLocation.vue b/src/components/common/VnLocation.vue
index 8256ec5b0..778932be3 100644
--- a/src/components/common/VnLocation.vue
+++ b/src/components/common/VnLocation.vue
@@ -2,20 +2,32 @@
 import CreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
 import VnSelectDialog from 'components/common/VnSelectDialog.vue';
 import { useI18n } from 'vue-i18n';
-
+import { ref } from 'vue';
 const { t } = useI18n();
-const value = defineModel({ type: [String, Number, Object] });
 
+const props = defineProps({
+    location: {
+        type: Object,
+        default: null,
+    },
+});
+const modelValue = ref(
+    props.location
+        ? `${props.location?.postcode} - ${props.location?.city}(${props.location?.province?.name}), ${props.location?.country?.name}`
+        : null
+);
 function showLabel(data) {
     return `${data.code} - ${data.town}(${data.province}), ${data.country}`;
 }
 </script>
 <template>
+    {{ modelValue }}
     <VnSelectDialog
-        v-model="value"
-        option-value="code"
+        v-model="modelValue"
         option-filter-value="search"
-        :option-label="(opt) => showLabel(opt)"
+        :option-label="
+            (opt) => (typeof modelValue === 'string' ? modelValue : showLabel(opt))
+        "
         url="Postcodes/filter"
         :use-like="false"
         :label="t('Location')"
@@ -27,7 +39,7 @@ function showLabel(data) {
         :emit-value="false"
     >
         <template #form>
-            <CreateNewPostcode @on-data-saved="(newValue) => (value = newValue)" />
+            <CreateNewPostcode @on-data-saved="(newValue) => (modelValue = newValue)" />
         </template>
         <template #option="{ itemProps, opt }">
             <QItem v-bind="itemProps">
diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index ee53da80c..f61dda8a4 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -95,6 +95,7 @@ globals:
     to: To
     pageTitles:
         logIn: Login
+        addressEdit: Update address
         summary: Summary
         basicData: Basic data
         log: Logs
diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml
index 86963f3dc..4e71a69fb 100644
--- a/src/i18n/locale/es.yml
+++ b/src/i18n/locale/es.yml
@@ -95,6 +95,7 @@ globals:
     to: Hasta
     pageTitles:
         logIn: Inicio de sesión
+        addressEdit: Modificar consignatario
         summary: Resumen
         basicData: Datos básicos
         log: Historial
diff --git a/src/pages/Customer/Card/CustomerAddress.vue b/src/pages/Customer/Card/CustomerAddress.vue
index 6c8279d84..a97b45a50 100644
--- a/src/pages/Customer/Card/CustomerAddress.vue
+++ b/src/pages/Customer/Card/CustomerAddress.vue
@@ -5,15 +5,12 @@ import { useRoute, useRouter } from 'vue-router';
 
 import axios from 'axios';
 
-import FetchData from 'components/FetchData.vue';
-
 const { t } = useI18n();
 const route = useRoute();
 const router = useRouter();
 
 const addresses = ref([]);
 const client = ref(null);
-const provincesLocation = ref([]);
 
 const addressFilter = {
     fields: [
@@ -41,7 +38,13 @@ const addressFilter = {
         {
             relation: 'province',
             scope: {
-                fields: ['id', 'name'],
+                fields: ['id', 'name', 'countryFk'],
+                include: [
+                    {
+                        relation: 'country',
+                        scope: { fields: ['id', 'name'] },
+                    },
+                ],
             },
         },
     ],
@@ -83,13 +86,6 @@ const getClientData = async (id) => {
     }
 };
 
-const setProvince = (provinceFk) => {
-    const result = provincesLocation.value.filter(
-        (province) => province.id === provinceFk
-    );
-    return result[0]?.name || '';
-};
-
 const isDefaultAddress = (address) => {
     return client?.value?.defaultAddressFk === address.id ? 1 : 0;
 };
@@ -128,12 +124,6 @@ const toCustomerAddressEdit = (addressId) => {
 </script>
 
 <template>
-    <FetchData
-        @on-fetch="(data) => (provincesLocation = data)"
-        auto-load
-        url="Provinces/location"
-    />
-
     <div class="full-width flex justify-center">
         <QCard class="card-width q-pa-lg" v-if="addresses.length">
             <QCardSection>
@@ -177,7 +167,7 @@ const toCustomerAddressEdit = (addressId) => {
                         <div>{{ item.street }}</div>
                         <div>
                             {{ item.postalCode }} - {{ item.city }},
-                            {{ setProvince(item.provinceFk) }}
+                            {{ item.province.name }}
                         </div>
                         <div class="flex">
                             <QCheckbox
diff --git a/src/pages/Customer/Card/CustomerFiscalData.vue b/src/pages/Customer/Card/CustomerFiscalData.vue
index 5cc656bb3..54b8adb70 100644
--- a/src/pages/Customer/Card/CustomerFiscalData.vue
+++ b/src/pages/Customer/Card/CustomerFiscalData.vue
@@ -94,7 +94,7 @@ function handleLocation(data, location) {
                 <VnLocation
                     :rules="validate('Worker.postcode')"
                     :roles-allowed-to-create="['deliveryAssistant']"
-                    v-model="data.postcode"
+                    :location="data"
                     @update:model-value="(location) => handleLocation(data, location)"
                 />
             </VnRow>
diff --git a/src/pages/Customer/CustomerCreate.vue b/src/pages/Customer/CustomerCreate.vue
index 041c92d17..193ed59c9 100644
--- a/src/pages/Customer/CustomerCreate.vue
+++ b/src/pages/Customer/CustomerCreate.vue
@@ -87,7 +87,6 @@ function handleLocation(data, location) {
                     <VnLocation
                         :rules="validate('Worker.postcode')"
                         :roles-allowed-to-create="['deliveryAssistant']"
-                        v-model="data.location"
                         @update:model-value="(location) => handleLocation(data, location)"
                     >
                     </VnLocation>
diff --git a/src/pages/Customer/CustomerList.vue b/src/pages/Customer/CustomerList.vue
index 82ad559ad..1dc0dab43 100644
--- a/src/pages/Customer/CustomerList.vue
+++ b/src/pages/Customer/CustomerList.vue
@@ -413,7 +413,6 @@ function handleLocation(data, location) {
         <template #more-create-dialog="{ data }">
             <VnLocation
                 :roles-allowed-to-create="['deliveryAssistant']"
-                v-model="data.location"
                 @update:model-value="(location) => handleLocation(data, location)"
             />
             <QInput v-model="data.userName" :label="t('Web user')" />
diff --git a/src/pages/Customer/components/CustomerAddressCreate.vue b/src/pages/Customer/components/CustomerAddressCreate.vue
index 30e4b21d0..e37306a94 100644
--- a/src/pages/Customer/components/CustomerAddressCreate.vue
+++ b/src/pages/Customer/components/CustomerAddressCreate.vue
@@ -93,7 +93,6 @@ function handleLocation(data, location) {
             <VnLocation
                 :rules="validate('Worker.postcode')"
                 :roles-allowed-to-create="['deliveryAssistant']"
-                v-model="data.location"
                 @update:model-value="(location) => handleLocation(data, location)"
             />
 
diff --git a/src/pages/Customer/components/CustomerAddressEdit.vue b/src/pages/Customer/components/CustomerAddressEdit.vue
index 7a4c44014..133d2fabc 100644
--- a/src/pages/Customer/components/CustomerAddressEdit.vue
+++ b/src/pages/Customer/components/CustomerAddressEdit.vue
@@ -177,7 +177,12 @@ function handleLocation(data, location) {
                     <VnLocation
                         :rules="validate('Worker.postcode')"
                         :roles-allowed-to-create="['deliveryAssistant']"
-                        v-model="data.postalCode"
+                        :location="{
+                            postcode: data.postalCode,
+                            city: data.city,
+                            province: data.province,
+                            country: data.province.country,
+                        }"
                         @update:model-value="(location) => handleLocation(data, location)"
                     ></VnLocation>
                 </div>
diff --git a/src/pages/Supplier/Card/SupplierAddresses.vue b/src/pages/Supplier/Card/SupplierAddresses.vue
index 735b50625..4b2174d0b 100644
--- a/src/pages/Supplier/Card/SupplierAddresses.vue
+++ b/src/pages/Supplier/Card/SupplierAddresses.vue
@@ -26,7 +26,13 @@ const addressesFilter = {
         {
             relation: 'province',
             scope: {
-                fields: ['id', 'name'],
+                fields: ['id', 'name', 'countryFk'],
+                include: [
+                    {
+                        relation: 'country',
+                        scope: { fields: ['id', 'name'] },
+                    },
+                ],
             },
         },
     ],
diff --git a/src/pages/Supplier/Card/SupplierAddressesCreate.vue b/src/pages/Supplier/Card/SupplierAddressesCreate.vue
index da6549a24..df98b6091 100644
--- a/src/pages/Supplier/Card/SupplierAddressesCreate.vue
+++ b/src/pages/Supplier/Card/SupplierAddressesCreate.vue
@@ -21,6 +21,7 @@ const newAddressForm = reactive({
     provinceFk: null,
     phone: null,
     mobile: null,
+    province: null,
 });
 
 const onDataSaved = () => {
@@ -84,7 +85,16 @@ function handleLocation(data, location) {
                     <VnLocation
                         :rules="validate('Worker.postcode')"
                         :roles-allowed-to-create="['deliveryAssistant']"
-                        v-model="data.location"
+                        :location="
+                            data.postalCode
+                                ? {
+                                      postcode: data.postalCode,
+                                      city: data.city,
+                                      province: data.province,
+                                      country: data.province.country,
+                                  }
+                                : null
+                        "
                         @update:model-value="(location) => handleLocation(data, location)"
                     >
                     </VnLocation>
diff --git a/src/pages/Supplier/Card/SupplierFiscalData.vue b/src/pages/Supplier/Card/SupplierFiscalData.vue
index c638abbdc..c2cb42c6a 100644
--- a/src/pages/Supplier/Card/SupplierFiscalData.vue
+++ b/src/pages/Supplier/Card/SupplierFiscalData.vue
@@ -51,6 +51,23 @@ function handleLocation(data, location) {
         :url="`Suppliers/${route.params.id}`"
         :url-update="`Suppliers/${route.params.id}/updateFiscalData`"
         model="supplier"
+        :filter="{
+            fields: ['id', 'name', 'city', 'postCode', 'countryFk', 'provinceFk'],
+            include: [
+                {
+                    relation: 'province',
+                    scope: {
+                        fields: ['id', 'name'],
+                    },
+                },
+                {
+                    relation: 'country',
+                    scope: {
+                        fields: ['id', 'name'],
+                    },
+                },
+            ],
+        }"
         auto-load
         :clear-store-on-unmount="false"
     >
@@ -130,7 +147,12 @@ function handleLocation(data, location) {
                 <VnLocation
                     :rules="validate('Worker.postcode')"
                     :roles-allowed-to-create="['deliveryAssistant']"
-                    v-model="data.postCode"
+                    :location="{
+                        postcode: data.postCode,
+                        city: data.city,
+                        province: data.province,
+                        country: data.country,
+                    }"
                     @update:model-value="(location) => handleLocation(data, location)"
                 >
                 </VnLocation>
diff --git a/src/pages/Worker/WorkerCreate.vue b/src/pages/Worker/WorkerCreate.vue
index a1ded2bfd..5f96c136d 100644
--- a/src/pages/Worker/WorkerCreate.vue
+++ b/src/pages/Worker/WorkerCreate.vue
@@ -172,7 +172,6 @@ onBeforeMount(async () => {
                     <VnLocation
                         :rules="validate('Worker.postcode')"
                         :roles-allowed-to-create="['deliveryAssistant']"
-                        v-model="data.location"
                         @update:model-value="(location) => handleLocation(data, location)"
                         :disable="formData.isFreelance"
                     >
diff --git a/src/pages/Worker/WorkerList.vue b/src/pages/Worker/WorkerList.vue
index 0c2656597..9d4c3acbf 100644
--- a/src/pages/Worker/WorkerList.vue
+++ b/src/pages/Worker/WorkerList.vue
@@ -223,7 +223,6 @@ function uppercaseStreetModel(data) {
                     <VnLocation
                         :roles-allowed-to-create="['deliveryAssistant']"
                         :options="postcodesOptions"
-                        v-model="data.location"
                         @update:model-value="(location) => handleLocation(data, location)"
                         :disable="data.isFreelance"
                     >
diff --git a/src/router/modules/customer.js b/src/router/modules/customer.js
index 684b83b0f..f364bd862 100644
--- a/src/router/modules/customer.js
+++ b/src/router/modules/customer.js
@@ -174,7 +174,7 @@ export default {
                                     path: 'edit',
                                     name: 'CustomerAddressEdit',
                                     meta: {
-                                        title: 'address-edit',
+                                        title: 'addressEdit',
                                     },
                                     component: () =>
                                         import(

From 8acd008f42e02a80f0c491394f41335705dc3b85 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Wed, 14 Aug 2024 09:47:27 +0200
Subject: [PATCH 11/69] feat: handle newValue

---
 src/components/common/VnLocation.vue | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/components/common/VnLocation.vue b/src/components/common/VnLocation.vue
index 778932be3..2fbe357ba 100644
--- a/src/components/common/VnLocation.vue
+++ b/src/components/common/VnLocation.vue
@@ -4,6 +4,7 @@ import VnSelectDialog from 'components/common/VnSelectDialog.vue';
 import { useI18n } from 'vue-i18n';
 import { ref } from 'vue';
 const { t } = useI18n();
+const emit = defineEmits(['update:model-value', 'update:options']);
 
 const props = defineProps({
     location: {
@@ -39,7 +40,14 @@ function showLabel(data) {
         :emit-value="false"
     >
         <template #form>
-            <CreateNewPostcode @on-data-saved="(newValue) => (modelValue = newValue)" />
+            <CreateNewPostcode
+                @on-data-saved="
+                    (newValue) => {
+                        modelValue = newValue;
+                        emit('update:model-value', newValue);
+                    }
+                "
+            />
         </template>
         <template #option="{ itemProps, opt }">
             <QItem v-bind="itemProps">

From 9eff6b56ea4f78fbc8938dbbf5d22f89a4cd843a Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Wed, 14 Aug 2024 09:48:59 +0200
Subject: [PATCH 12/69] fix: remove print variable

---
 src/components/common/VnLocation.vue | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/components/common/VnLocation.vue b/src/components/common/VnLocation.vue
index 2fbe357ba..e95d5a043 100644
--- a/src/components/common/VnLocation.vue
+++ b/src/components/common/VnLocation.vue
@@ -22,7 +22,6 @@ function showLabel(data) {
 }
 </script>
 <template>
-    {{ modelValue }}
     <VnSelectDialog
         v-model="modelValue"
         option-filter-value="search"

From a131cb559dbbc8f012f650bd6cbdd0130042a6d4 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Wed, 21 Aug 2024 13:34:40 +0200
Subject: [PATCH 13/69] feat: add max rule

---
 src/components/common/VnInput.vue | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/src/components/common/VnInput.vue b/src/components/common/VnInput.vue
index 75d4b8a28..e79a235d5 100644
--- a/src/components/common/VnInput.vue
+++ b/src/components/common/VnInput.vue
@@ -67,9 +67,13 @@ const mixinRules = [
     requiredFieldRule,
     ...($attrs.rules ?? []),
     (val) => {
-        const { min } = vnInputRef.value.$attrs;
+        const { min, max } = vnInputRef.value.$attrs;
         if (!min) return null;
         if (min >= 0) if (Math.floor(val) < min) return t('inputMin', { value: min });
+        if (!max) return null;
+        if (max > 0) {
+            if (Math.floor(val) > max) return t('inputMax', { value: max });
+        }
     },
 ];
 </script>
@@ -116,6 +120,8 @@ const mixinRules = [
 <i18n>
     en:
         inputMin: Must be more than {value}
+        inputMax: Must be less than {value}
     es:
         inputMin: Debe ser mayor a {value}
+        inputMax: Debe ser menor a {value}
 </i18n>

From 76a7f8a1b3366d5f5c7f61e7283073c8c6069a65 Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Mon, 2 Sep 2024 12:09:48 +0200
Subject: [PATCH 14/69] refactor: refs #7500 refactor vnimg when storage is dms

---
 src/components/common/VnDmsList.vue |  8 ++------
 src/components/ui/VnImg.vue         | 15 +++++----------
 2 files changed, 7 insertions(+), 16 deletions(-)

diff --git a/src/components/common/VnDmsList.vue b/src/components/common/VnDmsList.vue
index 08c4e4aa4..c0e8ac43b 100644
--- a/src/components/common/VnDmsList.vue
+++ b/src/components/common/VnDmsList.vue
@@ -102,12 +102,8 @@ const columns = computed(() => [
             storage: 'dms',
             collection: null,
             size: null,
-            token: prop.token,
-            url:
-                'api/dms/' +
-                prop.row.file.split('.')[0] +
-                '/downloadFile?access_token=' +
-                token,
+            id: prop.row.file.split('.')[0],
+            token: token,
             class: 'rounded',
         }),
     },
diff --git a/src/components/ui/VnImg.vue b/src/components/ui/VnImg.vue
index 2e690ffe1..19ac8ce7f 100644
--- a/src/components/ui/VnImg.vue
+++ b/src/components/ui/VnImg.vue
@@ -12,10 +12,12 @@ const $props = defineProps({
     collection: {
         type: String,
         default: 'catalog',
+        required: false,
     },
     resolution: {
         type: String,
         default: '200x200',
+        required: false,
     },
     zoomResolution: {
         type: String,
@@ -29,27 +31,20 @@ const $props = defineProps({
         type: Number,
         required: true,
     },
-    url: {
-        type: String,
-        required: false,
-        default: null,
-    },
 });
 const show = ref(false);
 const token = useSession().getTokenMultimedia();
 const timeStamp = ref(`timestamp=${Date.now()}`);
-if ($props.url) return $props.url;
 const isEmployee = useRole().isEmployee();
-const _url = [$props.storage, $props.collection, $props.size, $props.id]
-    .filter((prop) => prop)
-    .join('/');
 
 const getUrl = (zoom = false) => {
     const curResolution = zoom
         ? $props.zoomResolution || $props.resolution
         : $props.resolution;
+    if ($props.storage === 'dms')
+        return `/api/${$props.storage}/${$props.id}/downloadFile?access_token=${token}`;
     return isEmployee
-        ? `/api/${_url}/download?access_token=${token}&${timeStamp.value}`
+        ? `/api/${$props.storage}/${$props.collection}/${curResolution}/${$props.id}/download?access_token=${token}&${timeStamp.value}`
         : noImage;
 };
 const reload = () => {

From 3dc720e2d0db55c61b7b28df6087d997af241b54 Mon Sep 17 00:00:00 2001
From: pablone <pablone@verdnatura.es>
Date: Wed, 4 Sep 2024 14:40:36 +0200
Subject: [PATCH 15/69] feat: refs #7404 add m3 and fix detail

---
 src/components/VnTable/VnTable.vue            |  36 ++++-
 src/pages/Entry/EntryList.vue                 |   3 -
 src/pages/Entry/EntryStockBought.vue          | 136 +++++++++++-------
 src/pages/Entry/EntryStockBoughtDetail.vue    |   2 +-
 .../integration/entry/stockBought.spec.js     |   2 +-
 5 files changed, 118 insertions(+), 61 deletions(-)

diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index 26b5c424a..0fd6d1dd1 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -90,6 +90,10 @@ const $props = defineProps({
         type: String,
         default: '90vh',
     },
+    footer: {
+        type: Boolean,
+        default: false,
+    },
 });
 const { t } = useI18n();
 const stateStore = useStateStore();
@@ -108,7 +112,7 @@ const orders = ref(parseOrder(routeQuery.filter?.order));
 const CrudModelRef = ref({});
 const showForm = ref(false);
 const splittedColumns = ref({ columns: [] });
-const columnsVisibilitySkippped = ref();
+const columnsVisibilitySkipped = ref();
 const createForm = ref();
 
 const tableModes = [
@@ -347,11 +351,11 @@ defineExpose({
     <CrudModel
         v-bind="$attrs"
         :class="$attrs['class'] ?? 'q-px-md'"
-        :limit="20"
+        :limit="$attrs.limit ?? 20"
         ref="CrudModelRef"
         @on-fetch="(...args) => emit('onFetch', ...args)"
         :search-url="searchUrl"
-        :disable-infinite-scroll="isTableMode"
+        :disable-infinite-scroll="$attrs['disable-infinite-scroll'] ?? isTableMode"
         @save-changes="reload"
         :has-sub-toolbar="$attrs['hasSubToolbar'] ?? isEditable"
         :auto-load="hasParams || $attrs['auto-load']"
@@ -371,7 +375,7 @@ defineExpose({
                 card-container-class="grid-three"
                 flat
                 :style="isTableMode && `max-height: ${tableHeight}`"
-                virtual-scroll
+                :virtual-scroll="!isTableMode"
                 @virtual-scroll="
                     (event) =>
                         event.index > rows.length - 2 &&
@@ -614,6 +618,28 @@ defineExpose({
                         </QCard>
                     </component>
                 </template>
+                <template #bottom-row="{ cols }" v-if="footer">
+                    <QTr v-if="rows.length" class="bg-header" style="height: 30px">
+                        <QTh
+                            v-for="col of cols.filter((cols) => cols.visible ?? true)"
+                            :key="col?.id"
+                            class="text-center"
+                        >
+                            <div
+                                v-if="col?.summation"
+                                :class="`text-${col?.align ?? 'left'}`"
+                                class="text-bold q-pa-sm"
+                            >
+                                {{
+                                    rows.reduce(
+                                        (sum, currentRow) => sum + currentRow[col.name],
+                                        0
+                                    )
+                                }}
+                            </div>
+                        </QTh>
+                    </QTr>
+                </template>
             </QTable>
         </template>
     </CrudModel>
@@ -672,7 +698,7 @@ es:
 }
 
 .bg-header {
-    background-color: var(--vn-header-color);
+    background-color: var(--vn-accent-color);
     color: var(--vn-text-color);
 }
 
diff --git a/src/pages/Entry/EntryList.vue b/src/pages/Entry/EntryList.vue
index c03c67edb..6f7ff1935 100644
--- a/src/pages/Entry/EntryList.vue
+++ b/src/pages/Entry/EntryList.vue
@@ -9,9 +9,6 @@ import RightMenu from 'src/components/common/RightMenu.vue';
 import { toDate } from 'src/filters';
 import { useSummaryDialog } from 'src/composables/useSummaryDialog';
 import EntrySummary from './Card/EntrySummary.vue';
-import VnUserLink from 'components/ui/VnUserLink.vue';
-import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
-import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue';
 import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue';
 import TravelDescriptorProxy from 'src/pages/Travel/Card/TravelDescriptorProxy.vue';
 
diff --git a/src/pages/Entry/EntryStockBought.vue b/src/pages/Entry/EntryStockBought.vue
index 12fc96214..75d356bd2 100644
--- a/src/pages/Entry/EntryStockBought.vue
+++ b/src/pages/Entry/EntryStockBought.vue
@@ -6,24 +6,23 @@ import { useQuasar } from 'quasar';
 
 import VnTable from 'components/VnTable/VnTable.vue';
 import VnRow from 'src/components/ui/VnRow.vue';
-import VnInput from 'src/components/common/VnInput.vue';
-import VnLv from 'src/components/ui/VnLv.vue';
 import FetchData from 'src/components/FetchData.vue';
 import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
 import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
 import EntryStockBoughtDetail from 'src/pages/Entry/EntryStockBoughtDetail.vue';
+import { useRouter } from 'vue-router';
 
 const { t } = useI18n();
 const quasar = useQuasar();
 const tableRef = ref();
 const state = useState();
 const user = state.getUser();
+const router = useRouter();
 const columns = [
     {
         align: 'left',
         label: 'Id',
         name: 'id',
-        isTitle: true,
         isId: true,
         columnFilter: false,
         visible: false,
@@ -32,6 +31,7 @@ const columns = [
         align: 'left',
         name: 'workerFk',
         label: t('Buyer'),
+        isTitle: true,
         columnFilter: false,
     },
     {
@@ -42,6 +42,7 @@ const columns = [
         create: true,
         component: 'number',
         summation: true,
+        cardVisible: true,
     },
     {
         align: 'left',
@@ -67,6 +68,7 @@ const columns = [
         name: 'bought',
         columnFilter: false,
         summation: true,
+        cardVisible: true,
     },
     {
         align: 'left',
@@ -89,7 +91,6 @@ const columns = [
                         component: EntryStockBoughtDetail,
                         componentProps: {
                             workerFk: row.workerFk,
-                            dated: row.dated,
                         },
                         maximized: true,
                     });
@@ -98,15 +99,22 @@ const columns = [
         ],
     },
 ];
-function getFilter(dated = Date.vnNow()) {
-    console.log('dated: ', new Date(dated * 1000));
+
+function navigate(id) {
+    router.push({ path: `/travel/${id}/basic-data` });
+}
+
+function getFilter(dated) {
+    const shipped = dated ? new Date(dated) : Date.vnNew();
+    shipped.setHours(0, 0, 0, 0);
     return {
-        fields: ['id', 'm3'],
-        where: { dated },
+        where: {
+            shipped,
+            m3: { neq: null },
+        },
         include: [
             {
                 relation: 'warehouseIn',
-                where: { code: 'vnh' },
             },
         ],
     };
@@ -116,49 +124,74 @@ const fetchDataRef = ref();
 </script>
 <template>
     <VnSubToolbar />
-    <VnTable
-        ref="tableRef"
-        data-key="StockBoughts"
-        url="StockBoughts/getStockBought"
-        save-url="StockBoughts/crud"
-        order="reserve DESC"
-        :is-editable="true"
-        :disable-option="{ card: true }"
-        :create="{
-            urlCreate: 'StockBoughts',
-            title: t('Reserve some space'),
-            onDataSaved: () => tableRef.reload(),
-            formInitialData: {
-                workerFk: user.id,
-                dated: Date.vnNow(),
-            },
-        }"
-        :columns="columns"
-        auto-load
-        @on-fetch="() => fetchDataRef.fetch()"
-    >
-        <template #moreFilterPanel="{ params }">
-            <FetchData
-                ref="fetchDataRef"
-                url="Travels"
-                limit="1"
-                auto-load
-                :filter="getFilter(params?.date)"
-                @on-fetch="(data) => (travel = data)"
-            />
-            <VnRow class="q-pa-md" style="align-items: center" v-if="travel?.length">
-                <span>{{ t('Booked trucks: ') + travel[0]?.m3 }}</span>
-                <QBtn style="max-width: 20%" flat icon="edit" />
-            </VnRow>
-        </template>
-        <template #column-workerFk="{ row }">
-            <span class="link" @click.stop>
-                {{ row?.worker?.user?.name }}
-                <WorkerDescriptorProxy :id="row?.workerFk" />
-            </span>
-        </template>
-    </VnTable>
+    <QPage class="column items-center q-pa-md">
+        <VnTable
+            ref="tableRef"
+            data-key="StockBoughts"
+            url="StockBoughts/getStockBought"
+            save-url="StockBoughts/crud"
+            order="reserve DESC"
+            :is-editable="true"
+            :create="{
+                urlCreate: 'StockBoughts',
+                title: t('Reserve some space'),
+                onDataSaved: () => tableRef.reload(),
+                formInitialData: {
+                    workerFk: user.id,
+                    dated: Date.vnNow(),
+                },
+            }"
+            :columns="columns"
+            auto-load
+            @on-fetch="() => console.log('fetching...', fetchDataRef.fetch())"
+            style="max-width: 40%"
+            :footer="true"
+        >
+            <template #moreFilterPanel="{ params }">
+                <FetchData
+                    ref="fetchDataRef"
+                    url="Travels"
+                    limit="1"
+                    auto-load
+                    :filter="getFilter(params?.dated)"
+                    @on-fetch="(data) => (travel = data)"
+                />
+                <div class="trucks">
+                    <div>
+                        <span style="color: var(--vn-label-color)">
+                            {{ t('Booked trucks') + ': ' }}
+                        </span>
+                    </div>
+                    <div v-if="travel">
+                        <span>
+                            {{ travel[0]?.m3 }}
+                        </span>
+                        <QBtn
+                            style="max-width: 20%"
+                            flat
+                            icon="edit"
+                            @click="navigate(travel[0]?.id)"
+                        />
+                    </div>
+                </div>
+            </template>
+            <template #column-workerFk="{ row }">
+                <span class="link" @click.stop>
+                    {{ row?.worker?.user?.name }}
+                    <WorkerDescriptorProxy :id="row?.workerFk" />
+                </span>
+            </template>
+        </VnTable>
+    </QPage>
 </template>
+<style lang="scss">
+.trucks {
+    text-align: center;
+    margin: 3%;
+    border: 3px solid var(--vn-header-color);
+    padding: 1%;
+}
+</style>
 <i18n>
     en:
         Buyer: Buyer
@@ -168,6 +201,7 @@ const fetchDataRef = ref();
         Date: Date
         This buyer has already made a reservation for this date: This buyer has already made a reservation for this date
     es:
+        Booked trucks: Camiones reservados
         Buyer: Comprador
         Reserve: Reservado
         Bought: Comprado
diff --git a/src/pages/Entry/EntryStockBoughtDetail.vue b/src/pages/Entry/EntryStockBoughtDetail.vue
index 9212f1115..b9420af71 100644
--- a/src/pages/Entry/EntryStockBoughtDetail.vue
+++ b/src/pages/Entry/EntryStockBoughtDetail.vue
@@ -18,7 +18,7 @@ const $props = defineProps({
         required: true,
     },
 });
-const customUrl = `StockBoughts/getStockBoughtDetail?workerFk=${$props.workerFk}`;
+const customUrl = `StockBoughts/getStockBoughtDetail?workerFk=${$props.workerFk}&date=${$props.dated}`;
 const columns = [
     {
         align: 'left',
diff --git a/test/cypress/integration/entry/stockBought.spec.js b/test/cypress/integration/entry/stockBought.spec.js
index 8e4fe225c..10af3ef42 100644
--- a/test/cypress/integration/entry/stockBought.spec.js
+++ b/test/cypress/integration/entry/stockBought.spec.js
@@ -8,7 +8,7 @@ describe('EntryStockBought', () => {
         );
     });
     it('Should edit the reserved space', () => {
-        cy.get('tBody > tr').its('length').should('eq', 1);
+        cy.get('tBody > tr').its('length').should('eq', 2);
         cy.get(reserveField).type('10{enter}');
         cy.get('button[title="Save"]').click();
         cy.get('.q-notification__message').should('have.text', 'Data saved');

From cd12343302c9fea5bc5419ac04731d43b75baf2d Mon Sep 17 00:00:00 2001
From: pablone <pablone@verdnatura.es>
Date: Fri, 6 Sep 2024 07:26:04 +0200
Subject: [PATCH 16/69] feat: refs #7404 add some style to the form and
 reorganize fields

---
 src/components/VnTable/VnColumn.vue        |   3 +
 src/components/VnTable/VnTable.vue         |   8 +-
 src/components/common/VnComponent.vue      |   6 +-
 src/pages/Entry/EntryStockBought.vue       | 119 ++++++++++-----------
 src/pages/Entry/EntryStockBoughtFilter.vue |  57 ++++++++++
 src/pages/Route/Card/RouteSearchbar.vue    |   1 -
 src/pages/Route/RouteList.vue              |   1 -
 7 files changed, 120 insertions(+), 75 deletions(-)
 create mode 100644 src/pages/Entry/EntryStockBoughtFilter.vue

diff --git a/src/components/VnTable/VnColumn.vue b/src/components/VnTable/VnColumn.vue
index ed34e9eee..ad120f02e 100644
--- a/src/components/VnTable/VnColumn.vue
+++ b/src/components/VnTable/VnColumn.vue
@@ -171,6 +171,8 @@ const components = computed(() => $props.components ?? defaultComponents);
             :value="{ row, model }"
             v-model="model"
         />
+
+        {{ console.log('model: ', col) }}
         <VnComponent
             v-if="col.component"
             :prop="col"
@@ -178,6 +180,7 @@ const components = computed(() => $props.components ?? defaultComponents);
             :value="{ row, model }"
             v-model="model"
         />
+
         <span :title="value" v-else>{{ value }}</span>
         <VnComponent
             v-if="col.after"
diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index 0fd6d1dd1..b5d6e8d10 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -15,7 +15,6 @@ import VnTableChip from 'components/VnTable/VnChip.vue';
 import VnVisibleColumn from 'src/components/VnTable/VnVisibleColumn.vue';
 import VnLv from 'components/ui/VnLv.vue';
 import VnTableOrder from 'src/components/VnTable/VnOrder.vue';
-import item from 'src/router/modules/item';
 
 const $props = defineProps({
     columns: {
@@ -410,7 +409,7 @@ defineExpose({
                     />
                 </template>
                 <template #header-cell="{ col }">
-                    <QTh v-if="col.visible ?? true">
+                    <QTh table-header-style="max-width:50%" v-if="col.visible ?? true">
                         <div
                             class="column self-start q-ml-xs ellipsis"
                             :class="`text-${col?.align ?? 'left'}`"
@@ -665,6 +664,7 @@ defineExpose({
                         :column-name="column.name"
                         :label="column.label"
                     >
+                        {{ console.log('data: ', data) }}
                         <VnTableColumn
                             :column="column"
                             :row="{}"
@@ -708,9 +708,7 @@ es:
 
 .q-table--dark .q-table__bottom,
 .q-table--dark thead,
-.q-table--dark tr,
-.q-table--dark th,
-.q-table--dark td {
+.q-table--dark tr {
     border-color: var(--vn-section-color);
 }
 
diff --git a/src/components/common/VnComponent.vue b/src/components/common/VnComponent.vue
index fa99f9892..1ba1506d5 100644
--- a/src/components/common/VnComponent.vue
+++ b/src/components/common/VnComponent.vue
@@ -17,17 +17,16 @@ const $props = defineProps({
     },
 });
 
-let mixed;
 const componentArray = computed(() => {
     if (typeof $props.prop === 'object') return [$props.prop];
     return $props.prop;
 });
 
 function mix(toComponent) {
-    if (mixed) return mixed;
     const { component, attrs, event } = toComponent;
+    console.log('attrs: ', attrs);
     const customComponent = $props.components[component];
-    mixed = {
+    return {
         component: customComponent?.component ?? component,
         attrs: {
             ...toValueAttrs(attrs),
@@ -37,7 +36,6 @@ function mix(toComponent) {
         },
         event: { ...customComponent?.event, ...event },
     };
-    return mixed;
 }
 
 function toValueAttrs(attrs) {
diff --git a/src/pages/Entry/EntryStockBought.vue b/src/pages/Entry/EntryStockBought.vue
index 75d356bd2..8f197be92 100644
--- a/src/pages/Entry/EntryStockBought.vue
+++ b/src/pages/Entry/EntryStockBought.vue
@@ -4,20 +4,18 @@ import { useI18n } from 'vue-i18n';
 import { useState } from 'src/composables/useState';
 import { useQuasar } from 'quasar';
 
-import VnTable from 'components/VnTable/VnTable.vue';
-import VnRow from 'src/components/ui/VnRow.vue';
-import FetchData from 'src/components/FetchData.vue';
 import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
+import RightMenu from 'src/components/common/RightMenu.vue';
+import EntryStockBoughtFilter from './EntryStockBoughtFilter.vue';
+import VnTable from 'components/VnTable/VnTable.vue';
 import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
 import EntryStockBoughtDetail from 'src/pages/Entry/EntryStockBoughtDetail.vue';
-import { useRouter } from 'vue-router';
 
 const { t } = useI18n();
 const quasar = useQuasar();
 const tableRef = ref();
 const state = useState();
 const user = state.getUser();
-const router = useRouter();
 const columns = [
     {
         align: 'left',
@@ -31,8 +29,17 @@ const columns = [
         align: 'left',
         name: 'workerFk',
         label: t('Buyer'),
-        isTitle: true,
-        columnFilter: false,
+        component: 'select',
+        create: true,
+        attrs: {
+            url: 'Workers/activeWithInheritedRole',
+            fields: ['id', 'name'],
+            where: { role: 'buyer' },
+            optionFilter: 'firstName',
+            optionLabel: 'name',
+            optionValue: 'id',
+            useLike: false,
+        },
     },
     {
         align: 'left',
@@ -44,31 +51,13 @@ const columns = [
         summation: true,
         cardVisible: true,
     },
-    {
-        align: 'left',
-        name: 'workerFk',
-        label: t('Buyer'),
-        component: 'select',
-        create: true,
-        visible: false,
-        columnFilter: false,
-        attrs: {
-            url: 'Workers/activeWithInheritedRole',
-            fields: ['id', 'name'],
-            where: { role: 'buyer' },
-            optionFilter: 'firstName',
-            optionLabel: 'name',
-            optionValue: 'id',
-            useLike: false,
-        },
-    },
     {
         align: 'left',
         label: t('Bought'),
         name: 'bought',
-        columnFilter: false,
         summation: true,
         cardVisible: true,
+        columnFilter: false,
     },
     {
         align: 'left',
@@ -92,7 +81,6 @@ const columns = [
                         componentProps: {
                             workerFk: row.workerFk,
                         },
-                        maximized: true,
                     });
                 },
             },
@@ -100,10 +88,6 @@ const columns = [
     },
 ];
 
-function navigate(id) {
-    router.push({ path: `/travel/${id}/basic-data` });
-}
-
 function getFilter(dated) {
     const shipped = dated ? new Date(dated) : Date.vnNew();
     shipped.setHours(0, 0, 0, 0);
@@ -119,11 +103,46 @@ function getFilter(dated) {
         ],
     };
 }
-const travel = ref();
-const fetchDataRef = ref();
+const userParams = ref({
+    dated: Date.vnNew(),
+});
 </script>
 <template>
-    <VnSubToolbar />
+    <VnSubToolbar>
+        <template #st-data="{ params }">
+            <FetchData
+                ref="fetchDataRef"
+                url="Travels"
+                limit="1"
+                auto-load
+                :filter="getFilter(params?.dated)"
+                @on-fetch="(data) => (travel = data)"
+            />
+            <div>
+                <div>
+                    <span style="color: var(--vn-label-color)">
+                        {{ t('Booked trucks') + ': ' }}
+                    </span>
+                </div>
+                <div v-if="travel">
+                    <span>
+                        {{ travel[0]?.m3 }}
+                    </span>
+                    <QBtn
+                        style="max-width: 20%"
+                        flat
+                        icon="edit"
+                        @click="navigate(travel[0]?.id)"
+                    />
+                </div>
+            </div>
+        </template>
+    </VnSubToolbar>
+    <RightMenu>
+        <template #right-panel>
+            <EntryStockBoughtFilter data-key="StockBoughts" />
+        </template>
+    </RightMenu>
     <QPage class="column items-center q-pa-md">
         <VnTable
             ref="tableRef"
@@ -131,6 +150,7 @@ const fetchDataRef = ref();
             url="StockBoughts/getStockBought"
             save-url="StockBoughts/crud"
             order="reserve DESC"
+            :right-search="false"
             :is-editable="true"
             :create="{
                 urlCreate: 'StockBoughts',
@@ -142,39 +162,10 @@ const fetchDataRef = ref();
                 },
             }"
             :columns="columns"
+            :user-params="userParams"
             auto-load
-            @on-fetch="() => console.log('fetching...', fetchDataRef.fetch())"
-            style="max-width: 40%"
             :footer="true"
         >
-            <template #moreFilterPanel="{ params }">
-                <FetchData
-                    ref="fetchDataRef"
-                    url="Travels"
-                    limit="1"
-                    auto-load
-                    :filter="getFilter(params?.dated)"
-                    @on-fetch="(data) => (travel = data)"
-                />
-                <div class="trucks">
-                    <div>
-                        <span style="color: var(--vn-label-color)">
-                            {{ t('Booked trucks') + ': ' }}
-                        </span>
-                    </div>
-                    <div v-if="travel">
-                        <span>
-                            {{ travel[0]?.m3 }}
-                        </span>
-                        <QBtn
-                            style="max-width: 20%"
-                            flat
-                            icon="edit"
-                            @click="navigate(travel[0]?.id)"
-                        />
-                    </div>
-                </div>
-            </template>
             <template #column-workerFk="{ row }">
                 <span class="link" @click.stop>
                     {{ row?.worker?.user?.name }}
diff --git a/src/pages/Entry/EntryStockBoughtFilter.vue b/src/pages/Entry/EntryStockBoughtFilter.vue
new file mode 100644
index 000000000..3393113f2
--- /dev/null
+++ b/src/pages/Entry/EntryStockBoughtFilter.vue
@@ -0,0 +1,57 @@
+<script setup>
+import { ref } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { onMounted } from 'vue';
+import { useStateStore } from 'stores/useStateStore';
+
+import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
+import FetchData from 'components/FetchData.vue';
+import VnInputDate from 'src/components/common/VnInputDate.vue';
+
+const { t } = useI18n();
+const props = defineProps({
+    dataKey: {
+        type: String,
+        required: true,
+    },
+});
+
+const companiesOptions = ref([]);
+const stateStore = useStateStore();
+onMounted(async () => {
+    stateStore.rightDrawer = true;
+});
+</script>
+
+<template>
+    <FetchData
+        ref="buyer"
+        url="Workers/activeWithInheritedRole"
+        :filter="{
+            fields: ['id', 'name'],
+            where: { role: 'buyer' },
+        }"
+        order="name"
+        @on-fetch="(data) => (companiesOptions = data)"
+        auto-load
+    />
+    <VnFilterPanel :data-key="props.dataKey" :search-button="true">
+        <template #tags="{ tag, formatFn }">
+            <div class="q-gutter-x-xs">
+                <strong>{{ t(`params.${tag.label}`) }}: </strong>
+                <span>{{ formatFn(tag.value) }}</span>
+            </div>
+        </template>
+        <template #body="{ params }">
+            <QItem class="q-my-sm">
+                <QItemSection>
+                    <VnInputDate v-model="params.dated" :label="t('Date')" is-outlined />
+                </QItemSection>
+            </QItem>
+        </template>
+    </VnFilterPanel>
+</template>
+<i18n>
+    es:
+        Date: Fecha
+</i18n>
diff --git a/src/pages/Route/Card/RouteSearchbar.vue b/src/pages/Route/Card/RouteSearchbar.vue
index 48ad09151..a8e11cef6 100644
--- a/src/pages/Route/Card/RouteSearchbar.vue
+++ b/src/pages/Route/Card/RouteSearchbar.vue
@@ -3,7 +3,6 @@ import VnSearchbar from 'components/ui/VnSearchbar.vue';
 import { useI18n } from 'vue-i18n';
 const { t } = useI18n();
 </script>
-
 <template>
     <VnSearchbar
         data-key="RouteList"
diff --git a/src/pages/Route/RouteList.vue b/src/pages/Route/RouteList.vue
index ef544a68d..7e2358236 100644
--- a/src/pages/Route/RouteList.vue
+++ b/src/pages/Route/RouteList.vue
@@ -409,5 +409,4 @@ es:
     Route is not served: La ruta no está servida
     hourStarted: Hora de inicio
     hourFinished: Hora de fin
-    Volume: Volumen
 </i18n>

From e1adb1a8dd7c30f7cc764b5f00e2ca0846565ac3 Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Tue, 10 Sep 2024 08:01:28 +0200
Subject: [PATCH 17/69] refactor: refs #6346 requested changes

---
 src/pages/Wagon/Type/WagonCreateTray.vue | 8 +++-----
 src/pages/Wagon/Type/WagonTypeEdit.vue   | 4 ++++
 2 files changed, 7 insertions(+), 5 deletions(-)

diff --git a/src/pages/Wagon/Type/WagonCreateTray.vue b/src/pages/Wagon/Type/WagonCreateTray.vue
index c3f8b3847..bb52e747b 100644
--- a/src/pages/Wagon/Type/WagonCreateTray.vue
+++ b/src/pages/Wagon/Type/WagonCreateTray.vue
@@ -39,7 +39,7 @@ watch(wagonColors, () => {
 const emit = defineEmits(['onSubmit']);
 
 async function getTrays() {
-    const data = await axios.get('WagonTypeTrays', undefined, {
+    const { data } = await axios.get('WagonTypeTrays', undefined, {
         filter: { wagonTypeFk: entityId.value },
     });
     existingTrayHeight.value = data.data.filter(
@@ -49,13 +49,11 @@ async function getTrays() {
 }
 
 function onSubmit() {
-    const newTray = {
+    emit('onSubmit', {
         wagonTypeFk: entityId.value,
         wagonTypeColorFk: selectedTrayColor.value,
         height: trayHeight.value,
-    };
-
-    emit('onSubmit', newTray);
+    });
 }
 getTrays();
 </script>
diff --git a/src/pages/Wagon/Type/WagonTypeEdit.vue b/src/pages/Wagon/Type/WagonTypeEdit.vue
index 25ba05714..dc261990f 100644
--- a/src/pages/Wagon/Type/WagonTypeEdit.vue
+++ b/src/pages/Wagon/Type/WagonTypeEdit.vue
@@ -56,6 +56,10 @@ async function deleteTray(trayToDelete) {
             type: 'positive',
         });
     } catch (err) {
+        notify({
+            message: t('Error deleting tray'),
+            type: 'negative',
+        });
         console.log('err: ', err);
     }
 }

From 30ffb2428ea7e549681a3d2d360dfdb7aee1b92e Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Tue, 10 Sep 2024 08:28:48 +0200
Subject: [PATCH 18/69] perf: refs #6346 previous changes

---
 src/pages/Wagon/Type/WagonCreateTray.vue |  4 +--
 src/pages/Wagon/Type/WagonTypeEdit.vue   | 40 +++++++++---------------
 2 files changed, 15 insertions(+), 29 deletions(-)

diff --git a/src/pages/Wagon/Type/WagonCreateTray.vue b/src/pages/Wagon/Type/WagonCreateTray.vue
index bb52e747b..304fa83ac 100644
--- a/src/pages/Wagon/Type/WagonCreateTray.vue
+++ b/src/pages/Wagon/Type/WagonCreateTray.vue
@@ -42,9 +42,7 @@ async function getTrays() {
     const { data } = await axios.get('WagonTypeTrays', undefined, {
         filter: { wagonTypeFk: entityId.value },
     });
-    existingTrayHeight.value = data.data.filter(
-        (item) => item.wagonTypeFk == entityId.value
-    );
+    existingTrayHeight.value = data.filter((item) => item.wagonTypeFk == entityId.value);
     heights.value = existingTrayHeight.value.map((item) => item.height);
 }
 
diff --git a/src/pages/Wagon/Type/WagonTypeEdit.vue b/src/pages/Wagon/Type/WagonTypeEdit.vue
index dc261990f..eb8205d72 100644
--- a/src/pages/Wagon/Type/WagonTypeEdit.vue
+++ b/src/pages/Wagon/Type/WagonTypeEdit.vue
@@ -32,36 +32,24 @@ async function loadTrays() {
 }
 
 async function addTray(newTray) {
-    try {
-        const res = await axios.post(`WagonTypeTrays`, newTray);
-        wagonTrays.value.push(res.data);
-        notify({
-            message: t(`Tray added successfully`),
-            type: 'positive',
-        });
-    } catch (err) {
-        console.log('err: ', err);
-    }
+    const res = await axios.post(`WagonTypeTrays`, newTray);
+    wagonTrays.value.push(res.data);
+    notify({
+        message: t(`Tray added successfully`),
+        type: 'positive',
+    });
 }
 
 async function deleteTray(trayToDelete) {
-    try {
-        await axios.delete(`WagonTypeTrays/${trayToDelete.id}`);
-        const index = wagonTrays.value.findIndex((tray) => tray.id === trayToDelete.id);
-        if (index !== -1) {
-            wagonTrays.value.splice(index, 1);
-        }
-        notify({
-            message: t('Tray deleted successfully'),
-            type: 'positive',
-        });
-    } catch (err) {
-        notify({
-            message: t('Error deleting tray'),
-            type: 'negative',
-        });
-        console.log('err: ', err);
+    await axios.delete(`WagonTypeTrays/${trayToDelete.id}`);
+    const index = wagonTrays.value.findIndex((tray) => tray.id === trayToDelete.id);
+    if (index !== -1) {
+        wagonTrays.value.splice(index, 1);
     }
+    notify({
+        message: t('Tray deleted successfully'),
+        type: 'positive',
+    });
 }
 
 const filter = {

From c7e717c61108c59e29d35a2f65796aa5492f05bf Mon Sep 17 00:00:00 2001
From: pablone <pablone@verdnatura.es>
Date: Wed, 11 Sep 2024 07:55:12 +0200
Subject: [PATCH 19/69] feat: refs #7404 add travel m3 to reserves form

---
 src/components/VnTable/VnColumn.vue           |  3 -
 src/components/common/VnComponent.vue         |  1 -
 src/components/ui/VnFilterPanel.vue           |  1 +
 src/pages/Entry/EntryStockBought.vue          | 84 ++++++++++++++-----
 src/pages/Entry/EntryStockBoughtFilter.vue    | 11 ++-
 .../integration/entry/stockBought.spec.js     |  2 +-
 6 files changed, 76 insertions(+), 26 deletions(-)

diff --git a/src/components/VnTable/VnColumn.vue b/src/components/VnTable/VnColumn.vue
index ad120f02e..ed34e9eee 100644
--- a/src/components/VnTable/VnColumn.vue
+++ b/src/components/VnTable/VnColumn.vue
@@ -171,8 +171,6 @@ const components = computed(() => $props.components ?? defaultComponents);
             :value="{ row, model }"
             v-model="model"
         />
-
-        {{ console.log('model: ', col) }}
         <VnComponent
             v-if="col.component"
             :prop="col"
@@ -180,7 +178,6 @@ const components = computed(() => $props.components ?? defaultComponents);
             :value="{ row, model }"
             v-model="model"
         />
-
         <span :title="value" v-else>{{ value }}</span>
         <VnComponent
             v-if="col.after"
diff --git a/src/components/common/VnComponent.vue b/src/components/common/VnComponent.vue
index 1ba1506d5..bd25c787c 100644
--- a/src/components/common/VnComponent.vue
+++ b/src/components/common/VnComponent.vue
@@ -24,7 +24,6 @@ const componentArray = computed(() => {
 
 function mix(toComponent) {
     const { component, attrs, event } = toComponent;
-    console.log('attrs: ', attrs);
     const customComponent = $props.components[component];
     return {
         component: customComponent?.component ?? component,
diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue
index cebdc4bbf..b3bdec0a5 100644
--- a/src/components/ui/VnFilterPanel.vue
+++ b/src/components/ui/VnFilterPanel.vue
@@ -58,6 +58,7 @@ const $props = defineProps({
 });
 
 defineExpose({ search, sanitizer });
+
 const emit = defineEmits([
     'update:modelValue',
     'refresh',
diff --git a/src/pages/Entry/EntryStockBought.vue b/src/pages/Entry/EntryStockBought.vue
index 8f197be92..ad338acb1 100644
--- a/src/pages/Entry/EntryStockBought.vue
+++ b/src/pages/Entry/EntryStockBought.vue
@@ -1,10 +1,13 @@
 <script setup>
-import { ref } from 'vue';
+import { onMounted, ref, watch } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useState } from 'src/composables/useState';
 import { useQuasar } from 'quasar';
 
 import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
+import axios from 'axios';
+import FetchData from 'components/FetchData.vue';
+import VnRow from 'components/ui/VnRow.vue';
 import RightMenu from 'src/components/common/RightMenu.vue';
 import EntryStockBoughtFilter from './EntryStockBoughtFilter.vue';
 import VnTable from 'components/VnTable/VnTable.vue';
@@ -13,7 +16,6 @@ import EntryStockBoughtDetail from 'src/pages/Entry/EntryStockBoughtDetail.vue';
 
 const { t } = useI18n();
 const quasar = useQuasar();
-const tableRef = ref();
 const state = useState();
 const user = state.getUser();
 const columns = [
@@ -88,7 +90,32 @@ const columns = [
     },
 ];
 
+const fetchDataRef = ref();
+const tableRef = ref();
+const travel = ref(null);
+const userParams = ref({
+    dated: Date.vnNew(),
+});
+const filter = ref({
+    where: {
+        shipped: (userParams.value.dated
+            ? new Date(userParams.value.dated)
+            : Date.vnNew()
+        ).setHours(0, 0, 0, 0),
+        m3: { neq: null },
+    },
+    include: [
+        {
+            relation: 'warehouseIn',
+            scope: {
+                where: { code: 'vnh' },
+            },
+        },
+    ],
+});
+
 function getFilter(dated) {
+    console.log('dated: ', dated);
     const shipped = dated ? new Date(dated) : Date.vnNew();
     shipped.setHours(0, 0, 0, 0);
     return {
@@ -99,48 +126,62 @@ function getFilter(dated) {
         include: [
             {
                 relation: 'warehouseIn',
+                scope: {
+                    where: { code: 'vnh' },
+                },
             },
         ],
     };
 }
-const userParams = ref({
-    dated: Date.vnNew(),
-});
+
+const setUserParams = async ({ dated }) => {
+    const shipped = (dated ? new Date(dated) : Date.vnNew()).setHours(0, 0, 0, 0);
+
+    filter.value.where.shipped = shipped;
+    fetchDataRef.value?.fetch();
+};
 </script>
 <template>
     <VnSubToolbar>
-        <template #st-data="{ params }">
+        <template #st-data>
             <FetchData
                 ref="fetchDataRef"
                 url="Travels"
                 limit="1"
                 auto-load
-                :filter="getFilter(params?.dated)"
+                :filter="filter"
                 @on-fetch="(data) => (travel = data)"
             />
-            <div>
-                <div>
-                    <span style="color: var(--vn-label-color)">
-                        {{ t('Booked trucks') + ': ' }}
-                    </span>
-                </div>
-                <div v-if="travel">
+
+            <VnRow>
+                <div v-if="travel" class="q-pa-md">
+                    <QIcon
+                        name="local_airport"
+                        class="fill-icon q-mr-sm"
+                        size="md"
+                        :title="t('Travel')"
+                        color="primary"
+                    />
                     <span>
-                        {{ travel[0]?.m3 }}
+                        {{ t('Booked trucks') + ': ' + travel[0]?.m3 }}
                     </span>
                     <QBtn
                         style="max-width: 20%"
                         flat
                         icon="edit"
                         @click="navigate(travel[0]?.id)"
+                        :title="t('Edit travel')"
                     />
                 </div>
-            </div>
+            </VnRow>
         </template>
     </VnSubToolbar>
     <RightMenu>
         <template #right-panel>
-            <EntryStockBoughtFilter data-key="StockBoughts" />
+            <EntryStockBoughtFilter
+                data-key="StockBoughts"
+                @set-user-params="setUserParams"
+            />
         </template>
     </RightMenu>
     <QPage class="column items-center q-pa-md">
@@ -155,7 +196,10 @@ const userParams = ref({
             :create="{
                 urlCreate: 'StockBoughts',
                 title: t('Reserve some space'),
-                onDataSaved: () => tableRef.reload(),
+                onDataSaved: () => {
+                    tableRef.reload();
+                    fetchDataRef.reload();
+                },
                 formInitialData: {
                     workerFk: user.id,
                     dated: Date.vnNow(),
@@ -163,8 +207,8 @@ const userParams = ref({
             }"
             :columns="columns"
             :user-params="userParams"
-            auto-load
             :footer="true"
+            auto-load
         >
             <template #column-workerFk="{ row }">
                 <span class="link" @click.stop>
@@ -192,6 +236,8 @@ const userParams = ref({
         Date: Date
         This buyer has already made a reservation for this date: This buyer has already made a reservation for this date
     es:
+        Edit travel: Editar envío
+        Travel: Envíos
         Booked trucks: Camiones reservados
         Buyer: Comprador
         Reserve: Reservado
diff --git a/src/pages/Entry/EntryStockBoughtFilter.vue b/src/pages/Entry/EntryStockBoughtFilter.vue
index 3393113f2..aaf9e7a5b 100644
--- a/src/pages/Entry/EntryStockBoughtFilter.vue
+++ b/src/pages/Entry/EntryStockBoughtFilter.vue
@@ -15,9 +15,12 @@ const props = defineProps({
         required: true,
     },
 });
-
 const companiesOptions = ref([]);
 const stateStore = useStateStore();
+const emit = defineEmits(['set-user-params']);
+const setUserParams = (params) => {
+    emit('set-user-params', params);
+};
 onMounted(async () => {
     stateStore.rightDrawer = true;
 });
@@ -35,7 +38,11 @@ onMounted(async () => {
         @on-fetch="(data) => (companiesOptions = data)"
         auto-load
     />
-    <VnFilterPanel :data-key="props.dataKey" :search-button="true">
+    <VnFilterPanel
+        :data-key="props.dataKey"
+        :search-button="true"
+        @set-user-params="setUserParams"
+    >
         <template #tags="{ tag, formatFn }">
             <div class="q-gutter-x-xs">
                 <strong>{{ t(`params.${tag.label}`) }}: </strong>
diff --git a/test/cypress/integration/entry/stockBought.spec.js b/test/cypress/integration/entry/stockBought.spec.js
index 10af3ef42..a18e2bd2e 100644
--- a/test/cypress/integration/entry/stockBought.spec.js
+++ b/test/cypress/integration/entry/stockBought.spec.js
@@ -20,7 +20,7 @@ describe('EntryStockBought', () => {
         cy.get('input[aria-label="Date"]').eq(1).type('01-01');
         cy.get('input[aria-label="Buyer"]').type('buyerboss{downarrow}{enter}');
         cy.get('.q-notification__message').should('have.text', 'Data created');
-        cy.get('tBody > tr').its('length').should('eq', 2);
+        cy.get('tBody > tr').its('length').should('eq', 3);
     });
     it('Should check detail for the buyer', () => {
         cy.get(':nth-child(1) > .sticky > .q-btn > .q-btn__content > .q-icon').click();

From 2b012d2de4ce72f253b9d8d7e2a99f4ceb03f34f Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Wed, 11 Sep 2024 10:04:40 +0200
Subject: [PATCH 20/69] fix: refs #7353 use same datakey

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

diff --git a/src/pages/Monitor/Ticket/MonitorTicketSearchbar.vue b/src/pages/Monitor/Ticket/MonitorTicketSearchbar.vue
index 4950ab381..f1c347588 100644
--- a/src/pages/Monitor/Ticket/MonitorTicketSearchbar.vue
+++ b/src/pages/Monitor/Ticket/MonitorTicketSearchbar.vue
@@ -3,7 +3,7 @@ import VnSearchbar from 'components/ui/VnSearchbar.vue';
 </script>
 <template>
     <VnSearchbar
-        data-key="SalesMonitorTickets"
+        data-key="saleMonitorTickets"
         url="SalesMonitors/salesFilter"
         :redirect="false"
         :label="$t('searchBar.label')"

From b559f2f18c4a0fd370ac85e3f2e7254de8c9f8b4 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Wed, 11 Sep 2024 10:18:11 +0200
Subject: [PATCH 21/69] refactor: refs #7353 use global locales

---
 src/i18n/locale/en.yml                        |  9 +++++++
 src/i18n/locale/es.yml                        |  9 +++++++
 .../Monitor/Ticket/MonitorTicketFilter.vue    | 24 ++++---------------
 3 files changed, 23 insertions(+), 19 deletions(-)

diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index 362c1ec9f..02f04c994 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -278,6 +278,15 @@ globals:
     createInvoiceIn: Create invoice in
     myAccount: My account
     noOne: No one
+    params:
+        search: General search
+        clientFk: Client id
+        salesPersonFk: Sales person
+        warehouseFk: Warehouse
+        provinceFk: Province
+        from: From
+        To: To
+        stateFk: State
 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 34383686c..caa6b6788 100644
--- a/src/i18n/locale/es.yml
+++ b/src/i18n/locale/es.yml
@@ -282,6 +282,15 @@ globals:
     createInvoiceIn: Crear factura recibida
     myAccount: Mi cuenta
     noOne: Nadie
+    params:
+        search: Búsqueda general
+        clientFk: Id cliente
+        salesPersonFk: Comercial
+        warehouseFk: Almacén
+        provinceFk: Provincia
+        from: Desde
+        To: Hasta
+        stateFk: Estado
 errors:
     statusUnauthorized: Acceso denegado
     statusInternalServerError: Ha ocurrido un error interno del servidor
diff --git a/src/pages/Monitor/Ticket/MonitorTicketFilter.vue b/src/pages/Monitor/Ticket/MonitorTicketFilter.vue
index 167a10465..17e26618d 100644
--- a/src/pages/Monitor/Ticket/MonitorTicketFilter.vue
+++ b/src/pages/Monitor/Ticket/MonitorTicketFilter.vue
@@ -64,7 +64,7 @@ const handleScopeDays = (params, days, callback) => {
             <QItem>
                 <QItemSection>
                     <VnInput
-                        :label="t('params.clientFk')"
+                        :label="t('globals.params.clientFk')"
                         v-model="params.clientFk"
                         is-outlined
                     />
@@ -105,7 +105,7 @@ const handleScopeDays = (params, days, callback) => {
                         outlined
                         dense
                         rounded
-                        :label="t('params.salesPersonFk')"
+                        :label="t('globals.params.salesPersonFk')"
                         v-model="params.salesPersonFk"
                         url="Workers/search"
                         :params="{ departmentCodes: ['VT'] }"
@@ -158,7 +158,7 @@ const handleScopeDays = (params, days, callback) => {
                         outlined
                         dense
                         rounded
-                        :label="t('params.stateFk')"
+                        :label="t('globals.params.stateFk')"
                         v-model="params.stateFk"
                         url="States"
                         is-outlined
@@ -184,7 +184,7 @@ const handleScopeDays = (params, days, callback) => {
                         outlined
                         dense
                         rounded
-                        :label="t('params.warehouseFk')"
+                        :label="t('globals.params.warehouseFk')"
                         v-model="params.warehouseFk"
                         :options="warehouses"
                     />
@@ -196,7 +196,7 @@ const handleScopeDays = (params, days, callback) => {
                         outlined
                         dense
                         rounded
-                        :label="t('params.provinceFk')"
+                        :label="t('globals.params.provinceFk')"
                         v-model="params.provinceFk"
                         url="Provinces"
                     />
@@ -235,22 +235,15 @@ const handleScopeDays = (params, days, callback) => {
 <i18n>
 en:
     params:
-        clientFk: Client id
         orderFk: Order id
         scopeDays: Days onward
         nickname: Nickname
-        salesPersonFk: Sales person
         refFk: Invoice
         agencyModeFk: Agency
-        stateFk: State
         groupedStates: Grouped State
-        warehouseFk: Warehouse
-        provinceFk: Province
         myTeam: My team
         problems: With problems
         pending: Pending
-        from: From
-        to: To
         alertLevel: Grouped State
     FREE: Free
     DELIVERED: Delivered
@@ -261,22 +254,15 @@ en:
     
 es:
     params:
-        clientFk: Id cliente
         orderFk: Id cesta
         scopeDays: Días en adelante
         nickname: Nombre mostrado
-        salesPersonFk: Comercial
         refFk: Factura
         agencyModeFk: Agencia
-        stateFk: Estado
         groupedStates: Estado agrupado
-        warehouseFk: Almacén
-        provinceFk: Provincia
         myTeam: Mi equipo
         problems: Con problemas
         pending: Pendiente
-        from: Desde
-        To: Hasta
         alertLevel: Estado agrupado
     FREE: Libre
     DELIVERED: Servido

From 9219fa07f57b4a317de6dd59bc9d037c744c1021 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Wed, 11 Sep 2024 11:23:14 +0200
Subject: [PATCH 22/69] fix: refs #7353 hide search param

---
 src/i18n/locale/en.yml                           | 1 -
 src/i18n/locale/es.yml                           | 1 -
 src/pages/Monitor/Ticket/MonitorTicketFilter.vue | 2 +-
 3 files changed, 1 insertion(+), 3 deletions(-)

diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index 02f04c994..19372f698 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -279,7 +279,6 @@ globals:
     myAccount: My account
     noOne: No one
     params:
-        search: General search
         clientFk: Client id
         salesPersonFk: Sales person
         warehouseFk: Warehouse
diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml
index caa6b6788..60e61d51c 100644
--- a/src/i18n/locale/es.yml
+++ b/src/i18n/locale/es.yml
@@ -283,7 +283,6 @@ globals:
     myAccount: Mi cuenta
     noOne: Nadie
     params:
-        search: Búsqueda general
         clientFk: Id cliente
         salesPersonFk: Comercial
         warehouseFk: Almacén
diff --git a/src/pages/Monitor/Ticket/MonitorTicketFilter.vue b/src/pages/Monitor/Ticket/MonitorTicketFilter.vue
index 17e26618d..3c95f5cdd 100644
--- a/src/pages/Monitor/Ticket/MonitorTicketFilter.vue
+++ b/src/pages/Monitor/Ticket/MonitorTicketFilter.vue
@@ -40,7 +40,7 @@ const handleScopeDays = (params, days, callback) => {
     <VnFilterPanel
         :data-key="dataKey"
         :search-button="true"
-        :hidden-tags="['from', 'to']"
+        :hidden-tags="['from', 'to', 'search']"
         :custom-tags="['scopeDays']"
         :unremovable-params="['from', 'to', 'scopeDays']"
     >

From d822bb6f208bc15812235e43dd0f3cc3e3db2360 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Thu, 12 Sep 2024 09:12:36 +0200
Subject: [PATCH 23/69] fix(ClaimList): attenderFk filter

---
 src/components/VnTable/VnFilter.vue | 2 +-
 src/pages/Claim/ClaimList.vue       | 1 +
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/components/VnTable/VnFilter.vue b/src/components/VnTable/VnFilter.vue
index c6d4d8ef2..fd3c29fa3 100644
--- a/src/components/VnTable/VnFilter.vue
+++ b/src/components/VnTable/VnFilter.vue
@@ -34,7 +34,7 @@ const model = defineModel(undefined, { required: true });
 const arrayData = useArrayData($props.dataKey, { searchUrl: $props.searchUrl });
 const columnFilter = computed(() => $props.column?.columnFilter);
 
-const updateEvent = { 'update:modelValue': addFilter, remove: () => addFilter(null) };
+const updateEvent = { 'update:modelValue': addFilter };
 const enterEvent = {
     'keyup.enter': () => addFilter(model.value),
     remove: () => addFilter(null),
diff --git a/src/pages/Claim/ClaimList.vue b/src/pages/Claim/ClaimList.vue
index b03dfb226..8f265442a 100644
--- a/src/pages/Claim/ClaimList.vue
+++ b/src/pages/Claim/ClaimList.vue
@@ -52,6 +52,7 @@ const columns = computed(() => [
         name: 'attendedBy',
         orderBy: 'workerFk',
         columnFilter: {
+            name: 'attenderFk',
             component: 'select',
             attrs: {
                 url: 'Workers/activeWithInheritedRole',

From 6b79d70551f4bdc981c20816d1b014f6d4162fa7 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Thu, 12 Sep 2024 09:14:55 +0200
Subject: [PATCH 24/69] refactor: refs #7828 wip

---
 src/pages/Worker/Card/WorkerTimeControl.vue | 50 +++++++++------------
 1 file changed, 22 insertions(+), 28 deletions(-)

diff --git a/src/pages/Worker/Card/WorkerTimeControl.vue b/src/pages/Worker/Card/WorkerTimeControl.vue
index abf60a078..2f66fbcc1 100644
--- a/src/pages/Worker/Card/WorkerTimeControl.vue
+++ b/src/pages/Worker/Card/WorkerTimeControl.vue
@@ -271,44 +271,38 @@ const fetchWorkerTimeControlMails = async (filter) => {
 
 const fetchWeekData = async () => {
     try {
-        const filter = {
-            where: {
-                workerFk: route.params.id,
-                year: selectedDate.value ? selectedDate.value?.getFullYear() : null,
-                week: selectedWeekNumber.value,
-            },
-        };
-
-        const data = await fetchWorkerTimeControlMails(filter);
-        if (!data.length) {
-            state.value = null;
-        } else {
+        const data = (
+            await axios.get(`WorkerTimeControlMails/${route.params.id}/getWeeklyMail`, {
+                params: {
+                    workerFk: route.params.id,
+                    year: selectedDate.value.getFullYear(),
+                    week: selectedWeekNumber.value,
+                },
+            })
+        ).data;
+        if (!data.length) state.value = null;
+        else {
             const [mail] = data;
             state.value = mail.state;
             reason.value = mail.reason;
         }
 
-        await canBeResend();
+        canResend.value = !!(
+            await axios.get('WorkerTimeControlMails/count', {
+                filter: {
+                    where: {
+                        year: selectedDate.value.getFullYear(),
+                        week: selectedWeekNumber.value,
+                        limit: 1,
+                    },
+                },
+            })
+        ).data.count;
     } catch (err) {
         console.error('Error fetching week data');
     }
 };
 
-const canBeResend = async () => {
-    canResend.value = false;
-
-    const filter = {
-        where: {
-            year: selectedDate.value.getFullYear(),
-            week: selectedWeekNumber.value,
-        },
-        limit: 1,
-    };
-
-    const data = await fetchWorkerTimeControlMails(filter);
-    if (data.length) canResend.value = true;
-};
-
 const setHours = (data) => {
     for (const weekDay of weekDays.value) {
         if (data) {

From 22bc8f24142d9faeda7a60b20eeb628dcea553f4 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Thu, 12 Sep 2024 09:33:06 +0200
Subject: [PATCH 25/69] fix: refs #7323 show user name

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

diff --git a/src/pages/Worker/Card/WorkerDescriptor.vue b/src/pages/Worker/Card/WorkerDescriptor.vue
index 154db1258..d04b5a3d3 100644
--- a/src/pages/Worker/Card/WorkerDescriptor.vue
+++ b/src/pages/Worker/Card/WorkerDescriptor.vue
@@ -197,7 +197,7 @@ const refetch = async () => await cardDescriptorRef.value.getData();
             </div>
         </template>
         <template #body="{ entity }">
-            <VnLv :label="t('worker.card.name')" :value="entity.user?.nickname" />
+            <VnLv :label="t('worker.card.name')" :value="entity.user?.name" />
             <VnLv :label="t('worker.card.email')" :value="entity.user?.email" copy />
             <VnLv
                 :label="t('worker.list.department')"

From d92659289bbcc8e0faddaea3bc84ea38546554e6 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Thu, 12 Sep 2024 09:41:29 +0200
Subject: [PATCH 26/69] fix: refs #7323 add correct label

---
 src/i18n/locale/en.yml                     | 2 +-
 src/i18n/locale/es.yml                     | 2 +-
 src/pages/Worker/Card/WorkerDescriptor.vue | 2 +-
 3 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index ba188a17a..c1f9a18ef 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -834,7 +834,7 @@ worker:
         newWorker: New worker
     card:
         workerId: Worker ID
-        name: Name
+        user: User
         email: Email
         phone: Phone
         mobile: Mobile
diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml
index d0f14ec47..08fee22d2 100644
--- a/src/i18n/locale/es.yml
+++ b/src/i18n/locale/es.yml
@@ -837,7 +837,7 @@ worker:
         newWorker: Nuevo trabajador
     card:
         workerId: ID Trabajador
-        name: Nombre
+        user: Usuario
         email: Correo personal
         phone: Teléfono
         mobile: Móvil
diff --git a/src/pages/Worker/Card/WorkerDescriptor.vue b/src/pages/Worker/Card/WorkerDescriptor.vue
index d04b5a3d3..0785be059 100644
--- a/src/pages/Worker/Card/WorkerDescriptor.vue
+++ b/src/pages/Worker/Card/WorkerDescriptor.vue
@@ -197,7 +197,7 @@ const refetch = async () => await cardDescriptorRef.value.getData();
             </div>
         </template>
         <template #body="{ entity }">
-            <VnLv :label="t('worker.card.name')" :value="entity.user?.name" />
+            <VnLv :label="t('worker.card.user')" :value="entity.user?.name" />
             <VnLv :label="t('worker.card.email')" :value="entity.user?.email" copy />
             <VnLv
                 :label="t('worker.list.department')"

From 59f6913a74ec636523a53ed42d8eb4b9a506cc54 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Thu, 12 Sep 2024 09:43:08 +0200
Subject: [PATCH 27/69] fix: refs #7323 add correct label

---
 src/i18n/locale/en.yml | 1 +
 src/i18n/locale/es.yml | 1 +
 2 files changed, 2 insertions(+)

diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index c1f9a18ef..79eec7d5a 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -835,6 +835,7 @@ worker:
     card:
         workerId: Worker ID
         user: User
+        name: Name
         email: Email
         phone: Phone
         mobile: Mobile
diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml
index 08fee22d2..23ac93e3d 100644
--- a/src/i18n/locale/es.yml
+++ b/src/i18n/locale/es.yml
@@ -838,6 +838,7 @@ worker:
     card:
         workerId: ID Trabajador
         user: Usuario
+        name: Nombre
         email: Correo personal
         phone: Teléfono
         mobile: Móvil

From 994197568320ff2f98592eae2543dafdd8f9bce0 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 12 Sep 2024 08:07:53 +0000
Subject: [PATCH 28/69] fix: infiniteScroll

---
 src/components/ui/VnPaginate.vue | 21 ++++++++++++++-------
 1 file changed, 14 insertions(+), 7 deletions(-)

diff --git a/src/components/ui/VnPaginate.vue b/src/components/ui/VnPaginate.vue
index 441a8ff48..eb9828bc7 100644
--- a/src/components/ui/VnPaginate.vue
+++ b/src/components/ui/VnPaginate.vue
@@ -224,20 +224,27 @@ defineExpose({ fetch, addFilter, paginate });
         v-bind="$attrs"
     >
         <slot name="body" :rows="store.data"></slot>
-        <div v-if="isLoading" class="info-row q-pa-md text-center">
+        <div v-if="isLoading" class="spinner info-row q-pa-md text-center">
             <QSpinner color="primary" size="md" />
         </div>
     </QInfiniteScroll>
 </template>
 
 <style lang="scss" scoped>
-.info-row {
-    width: 100%;
-
-    h5 {
-        margin: 0;
+    .spinner {
+        z-index: 1;
+        align-content: end;
+        position: absolute;
+        bottom: 0;
+        left: 0;
+    }
+    .info-row {
+        width: 100%;
+
+        h5 {
+            margin: 0;
+        }
     }
-}
 </style>
 
 <i18n>

From 3892df250cc87c166eb6b4dd71d0b05b2bcb1527 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 12 Sep 2024 08:53:46 +0000
Subject: [PATCH 29/69] fix: #5938 grouped filter

---
 src/pages/Ticket/TicketFutureFilter.vue | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/pages/Ticket/TicketFutureFilter.vue b/src/pages/Ticket/TicketFutureFilter.vue
index bb597a525..6345f62b3 100644
--- a/src/pages/Ticket/TicketFutureFilter.vue
+++ b/src/pages/Ticket/TicketFutureFilter.vue
@@ -153,7 +153,7 @@ onMounted(async () => {
                         :label="t('params.state')"
                         v-model="params.state"
                         :options="stateOptions"
-                        option-value="code"
+                        option-value="id"
                         option-label="name"
                         @update:model-value="searchFn()"
                         dense
@@ -169,7 +169,7 @@ onMounted(async () => {
                         :label="t('params.futureState')"
                         v-model="params.futureState"
                         :options="stateOptions"
-                        option-value="code"
+                        option-value="id"
                         option-label="name"
                         @update:model-value="searchFn()"
                         dense

From c7adb919121d785ba8f5b1a25b82eedad4421db7 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 12 Sep 2024 09:56:37 +0000
Subject: [PATCH 30/69] feat: use disableInifiniteScroll property

---
 src/pages/Customer/components/CustomerSummaryTable.vue | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/pages/Customer/components/CustomerSummaryTable.vue b/src/pages/Customer/components/CustomerSummaryTable.vue
index 87e4cffa7..374769a57 100644
--- a/src/pages/Customer/components/CustomerSummaryTable.vue
+++ b/src/pages/Customer/components/CustomerSummaryTable.vue
@@ -151,6 +151,7 @@ const setShippedColor = (date) => {
         :disable-option="{ card: true, table: true }"
         limit="5"
         class="full-width"
+        :disable-infinite-scroll="true"
     >
         <template #column-nickname="{ row }">
             <span class="link">

From c22f8f1069882a17c9ccc1ab85a213549d102396 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 12 Sep 2024 09:57:31 +0000
Subject: [PATCH 31/69] fix: stop call back event hasMoreData

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

diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index 0ed3de261..689be76ee 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -369,7 +369,7 @@ function handleOnDataSaved(_) {
         @on-fetch="(...args) => emit('onFetch', ...args)"
         :search-url="searchUrl"
         :disable-infinite-scroll="
-            $attrs['disableInfiniteScroll'] ? isTableMode : disableInfiniteScroll
+            $attrs['disableInfiniteScroll'] ? isTableMode : !disableInfiniteScroll
         "
         @save-changes="reload"
         :has-sub-toolbar="$props.hasSubToolbar ?? isEditable"

From a3afe790b942e0154cf6b1fc720156d223a62716 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 12 Sep 2024 12:00:07 +0200
Subject: [PATCH 32/69] fix: CustomerGreuges

---
 src/pages/Customer/Card/CustomerGreuges.vue | 17 ++++++++---------
 1 file changed, 8 insertions(+), 9 deletions(-)

diff --git a/src/pages/Customer/Card/CustomerGreuges.vue b/src/pages/Customer/Card/CustomerGreuges.vue
index 1c78392e7..1d8b8585f 100644
--- a/src/pages/Customer/Card/CustomerGreuges.vue
+++ b/src/pages/Customer/Card/CustomerGreuges.vue
@@ -5,10 +5,10 @@ import { useRoute } from 'vue-router';
 import { toCurrency } from 'src/filters';
 import { toDateTimeFormat } from 'src/filters/date';
 import VnTable from 'components/VnTable/VnTable.vue';
-
+import FetchData from 'components/FetchData.vue';
+const entityId = computed(() => route.params.id);
 const { t } = useI18n();
 const route = useRoute();
-const rows = ref([]);
 const totalAmount = ref();
 const tableRef = ref();
 const filter = computed(() => {
@@ -28,7 +28,7 @@ const filter = computed(() => {
             },
         ],
         where: {
-            clientFk: route.params.id,
+            clientFk: entityId,
         },
     };
 });
@@ -84,14 +84,14 @@ const columns = computed(() => [
         create: true,
     },
 ]);
-
-const setRows = (data) => {
-    rows.value = data;
-    totalAmount.value = data.reduce((acc, { amount = 0 }) => acc + amount, 0);
-};
 </script>
 
 <template>
+    <FetchData
+        :url="`Greuges/${entityId}/sumAmount`"
+        auto-load
+        @on-fetch="({ sumAmount }) => (totalAmount = sumAmount)"
+    ></FetchData>
     <VnTable
         ref="tableRef"
         data-key="Greuges"
@@ -104,7 +104,6 @@ const setRows = (data) => {
         :is-editable="false"
         :use-model="true"
         :column-search="false"
-        @on-fetch="(data) => setRows(data)"
         :disable-option="{ card: true }"
         :create="{
             urlCreate: `Greuges`,

From e5056c45c0c15e056e89b773a85cfddd24bce221 Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Thu, 12 Sep 2024 12:31:00 +0200
Subject: [PATCH 33/69] fix: refs #7500 fixed showing images wrongly

---
 src/components/common/VnDmsList.vue | 28 +++++++++++++++-------------
 src/components/ui/VnImg.vue         |  3 +--
 2 files changed, 16 insertions(+), 15 deletions(-)

diff --git a/src/components/common/VnDmsList.vue b/src/components/common/VnDmsList.vue
index c0e8ac43b..ea19bf25d 100644
--- a/src/components/common/VnDmsList.vue
+++ b/src/components/common/VnDmsList.vue
@@ -93,19 +93,21 @@ const dmsFilter = {
 
 const columns = computed(() => [
     {
-        align: 'left',
-        field: 'file',
-        label: t('globals.file'),
+        label: '',
         name: 'file',
+        align: 'left',
         component: VnImg,
-        props: (prop) => ({
-            storage: 'dms',
-            collection: null,
-            size: null,
-            id: prop.row.file.split('.')[0],
-            token: token,
-            class: 'rounded',
-        }),
+        props: (prop) => {
+            return {
+                storage: 'dms',
+                collection: null,
+                resolution: null,
+                id: prop.row.file.split('.')[0],
+                token: token,
+                class: 'rounded',
+                ratio: 1,
+            };
+        },
     },
     {
         align: 'left',
@@ -397,10 +399,10 @@ defineExpose({
     </QPageSticky>
 </template>
 <style scoped>
-.q-gutter-y-ms {
+/* .q-gutter-y-ms {
     display: grid;
     row-gap: 20px;
-}
+} */
 .labelColor {
     color: var(--vn-label-color);
 }
diff --git a/src/components/ui/VnImg.vue b/src/components/ui/VnImg.vue
index 19ac8ce7f..834f9afdc 100644
--- a/src/components/ui/VnImg.vue
+++ b/src/components/ui/VnImg.vue
@@ -12,12 +12,10 @@ const $props = defineProps({
     collection: {
         type: String,
         default: 'catalog',
-        required: false,
     },
     resolution: {
         type: String,
         default: '200x200',
-        required: false,
     },
     zoomResolution: {
         type: String,
@@ -68,6 +66,7 @@ defineExpose({
             v-bind="$attrs"
             spinner-color="primary"
             class="img_zoom"
+            :ratio="0"
         />
     </QDialog>
 </template>

From d00a88a3f44e908ed16c708fd7560ce77cc70474 Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Thu, 12 Sep 2024 12:34:54 +0200
Subject: [PATCH 34/69] refactor: refs #7500 deleted useless code

---
 src/components/common/VnDmsList.vue | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/src/components/common/VnDmsList.vue b/src/components/common/VnDmsList.vue
index ea19bf25d..b7ae449f9 100644
--- a/src/components/common/VnDmsList.vue
+++ b/src/components/common/VnDmsList.vue
@@ -399,10 +399,6 @@ defineExpose({
     </QPageSticky>
 </template>
 <style scoped>
-/* .q-gutter-y-ms {
-    display: grid;
-    row-gap: 20px;
-} */
 .labelColor {
     color: var(--vn-label-color);
 }

From 44fd356d63ee78589a3902ba1891ec41dd5d39ad Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Thu, 12 Sep 2024 13:19:31 +0200
Subject: [PATCH 35/69] feat: refs #7828 useAcl & cherry pick mail data worker

---
 src/pages/Worker/Card/WorkerTimeControl.vue | 63 +++++++--------------
 1 file changed, 22 insertions(+), 41 deletions(-)

diff --git a/src/pages/Worker/Card/WorkerTimeControl.vue b/src/pages/Worker/Card/WorkerTimeControl.vue
index 2f66fbcc1..3893305a5 100644
--- a/src/pages/Worker/Card/WorkerTimeControl.vue
+++ b/src/pages/Worker/Card/WorkerTimeControl.vue
@@ -12,7 +12,6 @@ import WorkerTimeControlCalendar from 'pages/Worker/Card/WorkerTimeControlCalend
 
 import useNotify from 'src/composables/useNotify.js';
 import axios from 'axios';
-import { useRole } from 'src/composables/useRole';
 import { useAcl } from 'src/composables/useAcl';
 import { useWeekdayStore } from 'src/stores/useWeekdayStore';
 import { useStateStore } from 'stores/useStateStore';
@@ -63,13 +62,16 @@ const selectedCalendarDates = ref([]);
 const selectedDateFormatted = ref(toDateString(defaultDate.value));
 
 const arrayData = useArrayData('workerData');
-
+const acl = useAcl();
 const worker = computed(() => arrayData.store?.data);
-
-const isHr = computed(() => useRole().hasAny(['hr']));
-
-const canSend = computed(() => useAcl().hasAny('WorkerTimeControl', 'sendMail', 'WRITE'));
-
+const canSend = computed(() =>
+    acl.hasAny([{ model: 'WorkerTimeControl', props: 'sendMail', accessType: 'WRITE' }])
+);
+const canUpdate = computed(() =>
+    acl.hasAny([
+        { model: 'WorkerTimeControl', props: 'updateMailState', accessType: 'WRITE' },
+    ])
+);
 const isHimself = computed(() => user.value.id === Number(route.params.id));
 
 const columns = computed(() => {
@@ -257,46 +259,25 @@ const fetchHours = async () => {
     }
 };
 
-const fetchWorkerTimeControlMails = async (filter) => {
-    try {
-        const { data } = await axios.get('WorkerTimeControlMails', {
-            params: { filter: JSON.stringify(filter) },
-        });
-
-        return data;
-    } catch (err) {
-        console.error('Error fetching worker time control mails');
-    }
-};
-
 const fetchWeekData = async () => {
+    const where = {
+        year: selectedDate.value.getFullYear(),
+        week: selectedWeekNumber.value,
+    };
     try {
-        const data = (
-            await axios.get(`WorkerTimeControlMails/${route.params.id}/getWeeklyMail`, {
-                params: {
-                    workerFk: route.params.id,
-                    year: selectedDate.value.getFullYear(),
-                    week: selectedWeekNumber.value,
-                },
+        const mail = (
+            await axios.get(`Workers/${route.params.id}/mail`, {
+                params: { filter: { where } },
             })
-        ).data;
-        if (!data.length) state.value = null;
+        ).data[0];
+        if (!mail) state.value = null;
         else {
-            const [mail] = data;
             state.value = mail.state;
             reason.value = mail.reason;
         }
 
         canResend.value = !!(
-            await axios.get('WorkerTimeControlMails/count', {
-                filter: {
-                    where: {
-                        year: selectedDate.value.getFullYear(),
-                        week: selectedWeekNumber.value,
-                        limit: 1,
-                    },
-                },
-            })
+            await axios.get('WorkerTimeControlMails/count', { params: { where } })
         ).data.count;
     } catch (err) {
         console.error('Error fetching week data');
@@ -443,7 +424,7 @@ onMounted(async () => {
         <div>
             <QBtnGroup push class="q-gutter-x-sm" flat>
                 <QBtn
-                    v-if="isHimself && state"
+                    v-if="canUpdate && state"
                     :label="t('Satisfied')"
                     color="primary"
                     type="submit"
@@ -451,7 +432,7 @@ onMounted(async () => {
                     @click="isSatisfied()"
                 />
                 <QBtn
-                    v-if="isHimself && state"
+                    v-if="canUpdate && state"
                     :label="t('Not satisfied')"
                     color="primary"
                     type="submit"
@@ -462,7 +443,7 @@ onMounted(async () => {
             </QBtnGroup>
             <QBtnGroup push class="q-gutter-x-sm q-ml-none" flat>
                 <QBtn
-                    v-if="reason && state && (isHimself || isHr)"
+                    v-if="reason && state && canUpdate"
                     :label="t('Reason')"
                     color="primary"
                     type="submit"

From cfdfc7f53100d2b2698cd3f36c0ab68639389ab1 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 12 Sep 2024 13:19:37 +0200
Subject: [PATCH 36/69] fix: CustomerSamples

---
 src/components/VnTable/VnTable.vue            |  7 +-
 src/components/ui/VnPaginate.vue              |  9 ++-
 src/composables/usePrintService.js            |  5 +-
 src/pages/Customer/Card/CustomerBalance.vue   | 80 +++++++------------
 .../components/CustomerSummaryTable.vue       |  1 +
 .../Card/InvoiceOutDescriptorMenu.vue         |  3 +-
 src/pages/Route/RouteList.vue                 | 19 +++--
 src/pages/Ticket/TicketFutureFilter.vue       |  4 +-
 8 files changed, 54 insertions(+), 74 deletions(-)

diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index 0ed3de261..d6008de0b 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -369,7 +369,7 @@ function handleOnDataSaved(_) {
         @on-fetch="(...args) => emit('onFetch', ...args)"
         :search-url="searchUrl"
         :disable-infinite-scroll="
-            $attrs['disableInfiniteScroll'] ? isTableMode : disableInfiniteScroll
+            $attrs['disableInfiniteScroll'] ? isTableMode : !disableInfiniteScroll
         "
         @save-changes="reload"
         :has-sub-toolbar="$props.hasSubToolbar ?? isEditable"
@@ -507,11 +507,8 @@ function handleOnDataSaved(_) {
                             :key="index"
                             :title="btn.title"
                             :icon="btn.icon"
-                            class="q-px-sm"
+                            class="q-px-sm text-primary-light"
                             flat
-                            :class="
-                                btn.isPrimary ? 'text-primary-light' : 'color-vn-text '
-                            "
                             :style="`visibility: ${
                                 (btn.show && btn.show(row)) ?? true ? 'visible' : 'hidden'
                             }`"
diff --git a/src/components/ui/VnPaginate.vue b/src/components/ui/VnPaginate.vue
index 441a8ff48..79a79c383 100644
--- a/src/components/ui/VnPaginate.vue
+++ b/src/components/ui/VnPaginate.vue
@@ -224,13 +224,20 @@ defineExpose({ fetch, addFilter, paginate });
         v-bind="$attrs"
     >
         <slot name="body" :rows="store.data"></slot>
-        <div v-if="isLoading" class="info-row q-pa-md text-center">
+        <div v-if="isLoading" class="spinner info-row q-pa-md text-center">
             <QSpinner color="primary" size="md" />
         </div>
     </QInfiniteScroll>
 </template>
 
 <style lang="scss" scoped>
+.spinner {
+    z-index: 1;
+    align-content: end;
+    position: absolute;
+    bottom: 0;
+    left: 0;
+}
 .info-row {
     width: 100%;
 
diff --git a/src/composables/usePrintService.js b/src/composables/usePrintService.js
index ff43c65a1..68a009d7b 100644
--- a/src/composables/usePrintService.js
+++ b/src/composables/usePrintService.js
@@ -16,7 +16,7 @@ export function usePrintService() {
         );
     }
 
-    function openReport(path, params) {
+    function openReport(path, params, isNewTab = '_self') {
         if (typeof params === 'string') params = JSON.parse(params);
         params = Object.assign(
             {
@@ -26,8 +26,7 @@ export function usePrintService() {
         );
 
         const query = new URLSearchParams(params).toString();
-
-        window.open(`api/${path}?${query}`);
+        window.open(`api/${path}?${query}`, isNewTab);
     }
 
     return {
diff --git a/src/pages/Customer/Card/CustomerBalance.vue b/src/pages/Customer/Card/CustomerBalance.vue
index 70747bb3f..2a1991bbd 100644
--- a/src/pages/Customer/Card/CustomerBalance.vue
+++ b/src/pages/Customer/Card/CustomerBalance.vue
@@ -5,12 +5,12 @@ import { useRoute } from 'vue-router';
 import { useAcl } from 'src/composables/useAcl';
 import axios from 'axios';
 import { useQuasar } from 'quasar';
+import FetchData from 'components/FetchData.vue';
 
 import { toCurrency, toDate, toDateHourMin } from 'src/filters';
 import { useState } from 'composables/useState';
 import { useStateStore } from 'stores/useStateStore';
 import { usePrintService } from 'composables/usePrintService';
-import { useSession } from 'composables/useSession';
 import { useVnConfirm } from 'composables/useVnConfirm';
 
 import VnTable from 'components/VnTable/VnTable.vue';
@@ -22,12 +22,10 @@ import CustomerNewPayment from 'src/pages/Customer/components/CustomerNewPayment
 import InvoiceOutDescriptorProxy from 'src/pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue';
 
 const { openConfirmationModal } = useVnConfirm();
-const { sendEmail } = usePrintService();
+const { sendEmail, openReport } = usePrintService();
 const { t } = useI18n();
 const { hasAny } = useAcl();
-
-const session = useSession();
-const tokenMultimedia = session.getTokenMultimedia();
+const currentBalance = ref({});
 const quasar = useQuasar();
 const route = useRoute();
 const state = useState();
@@ -36,7 +34,7 @@ const user = state.getUser();
 
 const clientRisk = ref([]);
 const tableRef = ref();
-const companyId = ref();
+const companyId = ref(user.value.companyFk);
 const companyLastId = ref(user.value.companyFk);
 const balances = ref([]);
 const vnFilterRef = ref({});
@@ -76,14 +74,14 @@ const companyFilterColumn = {
 
 const columns = computed(() => [
     {
-        align: 'left',
+        align: 'right',
         name: 'payed',
         label: t('Date'),
         format: ({ payed }) => toDate(payed),
         cardVisible: true,
     },
     {
-        align: 'left',
+        align: 'right',
         name: 'created',
         label: t('Creation date'),
         format: ({ created }) => toDateHourMin(created),
@@ -91,11 +89,10 @@ const columns = computed(() => [
     },
     {
         align: 'left',
-        name: 'workerFk',
         label: t('Employee'),
         columnField: {
             component: 'userLink',
-            attrs: ({ row }) => ({ workerId: row.workerFk, tag: row.userName }),
+            attrs: ({ row }) => ({ workerId: row.workerFk, name: row.userName }),
         },
         cardVisible: true,
     },
@@ -120,14 +117,14 @@ const columns = computed(() => [
         isId: true,
     },
     {
-        align: 'right',
+        align: 'left',
         name: 'credit',
         label: t('Havings'),
         format: ({ credit }) => credit && toCurrency(credit),
         cardVisible: true,
     },
     {
-        align: 'right',
+        align: 'left',
         name: 'balance',
         label: t('Balance'),
         format: ({ balance }) => toCurrency(balance),
@@ -166,41 +163,15 @@ const columns = computed(() => [
 
 onBeforeMount(() => {
     stateStore.rightDrawer = true;
-    companyId.value = user.value.companyFk;
 });
 
-async function getClientRisk() {
-    const { data } = await axios.get(`clientRisks`, {
-        params: {
-            filter: JSON.stringify({
-                include: { relation: 'company', scope: { fields: ['code'] } },
-                where: { clientFk: route.params.id, companyFk: user.value.companyFk },
-            }),
-        },
-    });
-    clientRisk.value = data;
-    return clientRisk.value;
-}
-
-async function getCurrentBalance() {
-    const currentBalance = (await getClientRisk()).find((balance) => {
-        return balance.companyFk === companyId.value;
-    });
-    return currentBalance && currentBalance.amount;
-}
-
-async function onFetch(data) {
-    balances.value = [];
-    for (const [index, balance] of data.entries()) {
-        if (index === 0) {
-            balance.balance = await getCurrentBalance();
-            continue;
-        }
-        const previousBalance = data[index - 1];
-        balance.balance =
-            previousBalance?.balance - (previousBalance?.debit - previousBalance?.credit);
+async function getCurrentBalance(data) {
+    for (const balance of data) {
+        currentBalance.value[balance.companyFk] = {
+            code: balance.company.code,
+            amount: balance.amount,
+        };
     }
-    balances.value = data;
 }
 
 const showNewPaymentDialog = () => {
@@ -215,19 +186,27 @@ const showNewPaymentDialog = () => {
 };
 
 const showBalancePdf = ({ id }) => {
-    const url = `api/InvoiceOuts/${id}/download?access_token=${tokenMultimedia}`;
-    window.open(url, '_blank');
+    openReport(`InvoiceOuts/${id}/download`, {}, '_blank');
 };
 </script>
 
 <template>
+    <FetchData
+        url="clientRisks"
+        :filter="{
+            include: { relation: 'company', scope: { fields: ['code'] } },
+            where: { clientFk: route.params.id, companyFk: companyId },
+        }"
+        auto-load
+        @on-fetch="getCurrentBalance"
+    ></FetchData>
     <VnSubToolbar class="q-mb-md">
         <template #st-data>
             <div class="column justify-center q-px-md q-py-sm">
                 <span class="text-bold">{{ t('Total by company') }}</span>
-                <div class="row justify-center" v-if="clientRisk?.length">
-                    {{ clientRisk[0].company.code }}:
-                    {{ toCurrency(clientRisk[0].amount) }}
+                <div class="row justify-center">
+                    {{ currentBalance[companyId]?.code }}:
+                    {{ toCurrency(currentBalance[companyId]?.amount) }}
                 </div>
             </div>
         </template>
@@ -253,7 +232,6 @@ const showBalancePdf = ({ id }) => {
         :right-search="false"
         :is-editable="false"
         :column-search="false"
-        @on-fetch="onFetch"
         :disable-option="{ card: true }"
         auto-load
     >
@@ -262,7 +240,7 @@ const showBalancePdf = ({ id }) => {
         </template>
         <template #column-description="{ row }">
             <div class="link" v-if="row.isInvoice">
-                {{ row.description }}
+                {{ t('bill', { ref: row.description }) }}
                 <InvoiceOutDescriptorProxy :id="row.description" />
             </div>
             <span v-else class="q-pa-xs dotted rounded-borders" :title="row.description">
diff --git a/src/pages/Customer/components/CustomerSummaryTable.vue b/src/pages/Customer/components/CustomerSummaryTable.vue
index 87e4cffa7..374769a57 100644
--- a/src/pages/Customer/components/CustomerSummaryTable.vue
+++ b/src/pages/Customer/components/CustomerSummaryTable.vue
@@ -151,6 +151,7 @@ const setShippedColor = (date) => {
         :disable-option="{ card: true, table: true }"
         limit="5"
         class="full-width"
+        :disable-infinite-scroll="true"
     >
         <template #column-nickname="{ row }">
             <span class="link">
diff --git a/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue b/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue
index 213e25b4a..08b21fb4a 100644
--- a/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue
+++ b/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue
@@ -41,8 +41,7 @@ const invoiceFormType = ref('pdf');
 const defaultEmailAddress = ref($props.invoiceOutData.client?.email);
 
 const showInvoicePdf = () => {
-    const url = `api/InvoiceOuts/${$props.invoiceOutData.id}/download?access_token=${token}`;
-    window.open(url, '_blank');
+    openReport(`InvoiceOuts/${$props.invoiceOutData.id}/download`, {}, '_blank');
 };
 
 const showInvoiceCsv = () => {
diff --git a/src/pages/Route/RouteList.vue b/src/pages/Route/RouteList.vue
index 7e2358236..1e20df99c 100644
--- a/src/pages/Route/RouteList.vue
+++ b/src/pages/Route/RouteList.vue
@@ -17,7 +17,9 @@ import RouteFilter from 'pages/Route/Card/RouteFilter.vue';
 import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
 import VnInputDate from 'components/common/VnInputDate.vue';
 import VnTable from 'components/VnTable/VnTable.vue';
+import { usePrintService } from 'src/composables/usePrintService';
 
+const { openReport } = usePrintService();
 const { t } = useI18n();
 const { viewSummary } = useSummaryDialog();
 const quasar = useQuasar();
@@ -235,18 +237,15 @@ const cloneRoutes = () => {
 const showRouteReport = () => {
     const ids = selectedRows.value.map((row) => row?.id);
     const idString = ids.join(',');
-    let url;
-
-    if (selectedRows.value.length <= 1) {
-        url = `api/Routes/${idString}/driver-route-pdf?access_token=${session.getTokenMultimedia()}`;
-    } else {
-        const params = new URLSearchParams({
-            access_token: session.getTokenMultimedia(),
+    let url = `Routes/${idString}/driver-route-pdf`;
+    let params = {};
+    if (selectedRows.value.length >= 1) {
+        params = {
             id: idString,
-        });
-        url = `api/Routes/downloadZip?${params.toString()}`;
+        };
+        url = `Routes/downloadZip`;
     }
-    window.open(url, '_blank');
+    openReport(url, params, '_blank');
 };
 
 function markAsServed() {
diff --git a/src/pages/Ticket/TicketFutureFilter.vue b/src/pages/Ticket/TicketFutureFilter.vue
index bb597a525..6345f62b3 100644
--- a/src/pages/Ticket/TicketFutureFilter.vue
+++ b/src/pages/Ticket/TicketFutureFilter.vue
@@ -153,7 +153,7 @@ onMounted(async () => {
                         :label="t('params.state')"
                         v-model="params.state"
                         :options="stateOptions"
-                        option-value="code"
+                        option-value="id"
                         option-label="name"
                         @update:model-value="searchFn()"
                         dense
@@ -169,7 +169,7 @@ onMounted(async () => {
                         :label="t('params.futureState')"
                         v-model="params.futureState"
                         :options="stateOptions"
-                        option-value="code"
+                        option-value="id"
                         option-label="name"
                         @update:model-value="searchFn()"
                         dense

From dcb8c5daad7821cc786aa69d5eb0bfdd77db3546 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Thu, 12 Sep 2024 13:27:28 +0200
Subject: [PATCH 37/69] fix: workerDms filter workerFk

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

diff --git a/src/pages/Worker/Card/WorkerDms.vue b/src/pages/Worker/Card/WorkerDms.vue
index a4f7ef5a9..e2b62bc4f 100644
--- a/src/pages/Worker/Card/WorkerDms.vue
+++ b/src/pages/Worker/Card/WorkerDms.vue
@@ -10,6 +10,6 @@ const route = useRoute();
         delete-model="WorkerDms"
         download-model="WorkerDms"
         default-dms-code="hhrrData"
-        filter="worker"
+        filter="wd.workerFk"
     />
 </template>

From 6adcaff5d8330420195f3119706148a420c6dab9 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Thu, 12 Sep 2024 16:48:39 +0200
Subject: [PATCH 38/69] fix: refs #7524 dynamic load

---
 src/pages/Claim/Card/ClaimDevelopment.vue | 15 ++++++---------
 1 file changed, 6 insertions(+), 9 deletions(-)

diff --git a/src/pages/Claim/Card/ClaimDevelopment.vue b/src/pages/Claim/Card/ClaimDevelopment.vue
index f7e8131c6..61d9f5858 100644
--- a/src/pages/Claim/Card/ClaimDevelopment.vue
+++ b/src/pages/Claim/Card/ClaimDevelopment.vue
@@ -16,7 +16,6 @@ const claimReasons = ref([]);
 const claimResults = ref([]);
 const claimResponsibles = ref([]);
 const claimRedeliveries = ref([]);
-const workers = ref([]);
 const selected = ref([]);
 const saveButtonRef = ref();
 
@@ -82,7 +81,9 @@ const columns = computed(() => [
         label: t('Worker'),
         field: (row) => row.workerFk,
         sortable: true,
-        options: workers.value,
+        url: 'Workers/search',
+        where: { active: 1 },
+        sortBy: 'name ASC',
         model: 'workerFk',
         optionValue: 'id',
         optionLabel: 'nickname',
@@ -129,13 +130,6 @@ const columns = computed(() => [
         @on-fetch="(data) => (claimRedeliveries = data)"
         auto-load
     />
-    <FetchData
-        url="Workers/search"
-        :where="{ active: 1 }"
-        order="name ASC"
-        @on-fetch="(data) => (workers = data)"
-        auto-load
-    />
     <CrudModel
         data-key="ClaimDevelopments"
         url="ClaimDevelopments"
@@ -165,6 +159,9 @@ const columns = computed(() => [
                     >
                         <VnSelect
                             v-model="row[col.model]"
+                            :url="col.url"
+                            :where="col.where"
+                            :sort-by="col.sortBy"
                             :options="col.options"
                             :option-value="col.optionValue"
                             :option-label="col.optionLabel"

From 5907fedcd68fdbe58c8c45b3a7f64bd2599f3660 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Thu, 12 Sep 2024 17:49:59 +0200
Subject: [PATCH 39/69] fix: refs #7524 test

---
 .../integration/claim/claimDevelopment.spec.js      | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/test/cypress/integration/claim/claimDevelopment.spec.js b/test/cypress/integration/claim/claimDevelopment.spec.js
index 903f58d4b..3b73a24d9 100755
--- a/test/cypress/integration/claim/claimDevelopment.spec.js
+++ b/test/cypress/integration/claim/claimDevelopment.spec.js
@@ -8,6 +8,8 @@ describe('ClaimDevelopment', () => {
         cy.viewport(1920, 1080);
         cy.login('developer');
         cy.visit(`/#/claim/${claimId}/development`);
+        cy.intercept('GET', /\/api\/Workers\/search/).as('workers');
+        cy.intercept('GET', /\/api\/Workers\/search/).as('workers');
         cy.waitForElement('tbody');
     });
 
@@ -32,10 +34,19 @@ describe('ClaimDevelopment', () => {
     });
 
     it('should add and remove new line', () => {
+        cy.wait(['@workers', '@workers']);
         cy.addCard();
+
         cy.get(thirdRow).should('exist');
 
-        const rowData = [false, 'Novato', 'Roces', 'Compradores', 'employeeNick', 'Tour'];
+        const rowData = [
+            false,
+            'Novato',
+            'Roces',
+            'Compradores',
+            'administrativeNick',
+            'Tour',
+        ];
         cy.fillRow(thirdRow, rowData);
 
         cy.saveCard();

From 8fc7e3cfb45291a6b0d2ed0427b371413b6ade2e Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 12 Sep 2024 20:43:50 +0200
Subject: [PATCH 40/69] fix: CustomerSamples

---
 src/pages/Customer/Card/CustomerSamples.vue | 16 +++++++++++++---
 1 file changed, 13 insertions(+), 3 deletions(-)

diff --git a/src/pages/Customer/Card/CustomerSamples.vue b/src/pages/Customer/Card/CustomerSamples.vue
index 49697aab7..8e2ab92a0 100644
--- a/src/pages/Customer/Card/CustomerSamples.vue
+++ b/src/pages/Customer/Card/CustomerSamples.vue
@@ -1,7 +1,7 @@
 <script setup>
 import { ref, computed } from 'vue';
 import { useI18n } from 'vue-i18n';
-import { useRoute, useRouter } from 'vue-router';
+import { useRoute } from 'vue-router';
 
 import { QBtn, useQuasar } from 'quasar';
 
@@ -13,7 +13,6 @@ import CustomerSamplesCreate from '../components/CustomerSamplesCreate.vue';
 
 const { t } = useI18n();
 const route = useRoute();
-const router = useRouter();
 
 const filter = {
     include: [
@@ -42,7 +41,16 @@ const columns = computed(() => [
     {
         align: 'left',
         label: t('Worker'),
-        name: 'worker',
+        columnField: {
+            component: 'userLink',
+            attrs: ({ row }) => {
+                return {
+                    defaultName: true,
+                    workerId: row?.user?.id,
+                    name: row?.user?.name,
+                };
+            },
+        },
     },
     {
         align: 'left',
@@ -73,7 +81,9 @@ const tableRef = ref();
         :columns="columns"
         :pagination="{ rowsPerPage: 12 }"
         :disable-option="{ card: true }"
+        :right-search="false"
         :rows="rows"
+        :order="['created DESC']"
         class="full-width q-mt-md"
         row-key="id"
         :create="false"

From 24f4af712ec3ab8e680a32b58619f58d649ef33a Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 12 Sep 2024 20:59:35 +0200
Subject: [PATCH 41/69] fix: CustomerMandates

---
 src/pages/Customer/Card/CustomerMandates.vue | 108 +++++--------------
 1 file changed, 27 insertions(+), 81 deletions(-)

diff --git a/src/pages/Customer/Card/CustomerMandates.vue b/src/pages/Customer/Card/CustomerMandates.vue
index 7af3e5828..248515b4a 100644
--- a/src/pages/Customer/Card/CustomerMandates.vue
+++ b/src/pages/Customer/Card/CustomerMandates.vue
@@ -1,20 +1,19 @@
 <script setup>
-import { computed, ref } from 'vue';
+import { computed } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useRoute } from 'vue-router';
 
 import { toDateTimeFormat } from 'src/filters/date';
 
-import FetchData from 'components/FetchData.vue';
+import VnTable from 'src/components/VnTable/VnTable.vue';
+import { dashIfEmpty } from 'src/filters';
 
 const { t } = useI18n();
 const route = useRoute();
 
-const rows = ref([]);
-
 const filter = {
     include: [
-        { relation: 'mandateType', scope: { fields: ['id', 'name'] } },
+        { relation: 'mandateType', scope: { fields: ['id', 'code'] } },
         { relation: 'company', scope: { fields: ['id', 'code'] } },
     ],
     where: { clientFk: route.params.id },
@@ -22,114 +21,61 @@ const filter = {
     limit: 20,
 };
 
-const tableColumnComponents = {
-    id: {
-        component: 'span',
-        props: () => {},
-        event: () => {},
-    },
-    company: {
-        component: 'span',
-        props: () => {},
-        event: () => {},
-    },
-    type: {
-        component: 'span',
-        props: () => {},
-        event: () => {},
-    },
-    registerDate: {
-        component: 'span',
-        props: () => {},
-        event: () => {},
-    },
-    endDate: {
-        component: 'span',
-        props: () => {},
-        event: () => {},
-    },
-};
-
 const columns = computed(() => [
     {
         align: 'left',
-        field: 'id',
-        label: t('Id'),
         name: 'id',
+        label: t('globals.id'),
+        field: 'id',
+        isId: true,
     },
     {
         align: 'left',
-        field: (row) => row.company.code,
-        label: t('Company'),
+        cardVisible: true,
+        format: ({ company }) => company.code,
+        label: t('globals.company'),
         name: 'company',
     },
     {
         align: 'left',
-        field: (row) => row.mandateType.name,
-        label: t('Type'),
+        cardVisible: true,
+        format: ({ mandateType }) => mandateType.code,
+        label: t('globals.type'),
         name: 'type',
     },
     {
         align: 'left',
-        field: 'created',
+        cardVisible: true,
         label: t('Register date'),
-        name: 'registerDate',
-        format: (value) => toDateTimeFormat(value),
+        name: 'created',
+        format: ({ created }) => toDateTimeFormat(created),
     },
     {
-        align: 'left',
-        field: 'finished',
+        align: 'right',
+        cardVisible: true,
+        name: 'finished',
         label: t('End date'),
-        name: 'endDate',
-        format: (value) => (value ? toDateTimeFormat(value) : '-'),
+        format: ({ finished }) => dashIfEmpty(toDateTimeFormat(finished)),
     },
 ]);
 </script>
 
 <template>
-    <FetchData
-        :filter="filter"
-        @on-fetch="(data) => (rows = data)"
-        auto-load
-        url="Mandates"
-    />
-
     <QPage class="column items-center q-pa-md">
-        <QTable
+        <VnTable
+            :filter="filter"
+            auto-load
+            url="Mandates"
             :columns="columns"
-            :pagination="{ rowsPerPage: 12 }"
-            :rows="rows"
             class="full-width q-mt-md"
-            row-key="id"
-            v-if="rows?.length"
-        >
-            <template #body-cell="props">
-                <QTd :props="props">
-                    <QTr :props="props" class="cursor-pointer">
-                        <component
-                            :is="tableColumnComponents[props.col.name].component"
-                            @click="tableColumnComponents[props.col.name].event(props)"
-                            class="rounded-borders q-pa-sm"
-                            v-bind="tableColumnComponents[props.col.name].props(props)"
-                        >
-                            {{ props.value }}
-                        </component>
-                    </QTr>
-                </QTd>
-            </template>
-        </QTable>
-
-        <h5 class="flex justify-center color-vn-label" v-else>
-            {{ t('globals.noResults') }}
-        </h5>
+            :right-search="false"
+            :row-click="false"
+        />
     </QPage>
 </template>
 
 <i18n>
 es:
-    Id: Id
-    Company: Empresa
-    Type: Tipo
     Register date: Fecha alta
     End date: Fecha baja
 </i18n>

From 70d0132710a4f4075216642399edf94f70de4f6b Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 12 Sep 2024 21:03:19 +0200
Subject: [PATCH 42/69] fix: CustomerWebPayment

---
 src/pages/Customer/Card/CustomerWebPayment.vue | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/pages/Customer/Card/CustomerWebPayment.vue b/src/pages/Customer/Card/CustomerWebPayment.vue
index 13ec6b128..482582078 100644
--- a/src/pages/Customer/Card/CustomerWebPayment.vue
+++ b/src/pages/Customer/Card/CustomerWebPayment.vue
@@ -5,7 +5,7 @@ import { useRoute } from 'vue-router';
 
 import axios from 'axios';
 
-import { toCurrency, toDateHourMinSec } from 'src/filters';
+import { toCurrency, toDateHourMin } from 'src/filters';
 
 import CustomerCloseIconTooltip from '../components/CustomerCloseIconTooltip.vue';
 import CustomerCheckIconTooltip from '../components/CustomerCheckIconTooltip.vue';
@@ -74,7 +74,7 @@ const columns = computed(() => [
         field: 'created',
         label: t('Date'),
         name: 'date',
-        format: (value) => toDateHourMinSec(value),
+        format: (value) => toDateHourMin(value),
     },
     {
         align: 'left',

From 39f2deafc4b879426075095e28167642fd8845aa Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 12 Sep 2024 21:08:41 +0200
Subject: [PATCH 43/69] fix: CustomerCreditOpinion workerDescriptor

---
 src/pages/Customer/Card/CustomerCreditOpinion.vue | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/src/pages/Customer/Card/CustomerCreditOpinion.vue b/src/pages/Customer/Card/CustomerCreditOpinion.vue
index 6a839ff3f..92fef1d76 100644
--- a/src/pages/Customer/Card/CustomerCreditOpinion.vue
+++ b/src/pages/Customer/Card/CustomerCreditOpinion.vue
@@ -44,7 +44,7 @@ const columns = computed(() => [
     {
         align: 'right',
         field: 'rating',
-        label: t('Rating'),
+        label: t('customer.summary.rating'),
         name: 'rating',
         create: true,
         columnCreate: {
@@ -56,7 +56,7 @@ const columns = computed(() => [
         align: 'right',
         field: 'recommendedCredit',
         format: ({ recommendedCredit }) => toCurrency(recommendedCredit),
-        label: t('Recommended credit'),
+        label: t('customer.summary.recommendCredit'),
         name: 'recommendedCredit',
         create: true,
         columnCreate: {
@@ -89,8 +89,8 @@ const columns = computed(() => [
         }"
     >
         <template #column-employee="{ row }">
-            <span class="link" @click.stop>{{ row.worker.user.nickname }}</span>
-            <WorkerDescriptorProxy :id="row.clientFk" />
+            <span class="link">{{ row.worker.user.nickname }}</span>
+            <WorkerDescriptorProxy :id="row.worker.id" />
         </template>
     </VnTable>
     <!-- <QTable
@@ -113,7 +113,6 @@ const columns = computed(() => [
 
 <i18n>
 es:
-    Rating: Clasificación
     Recommended credit: Crédito recomendado
     Since: Desde
     Employee: Empleado

From 54fc7f6394e2f7bdc2698145c424c948fe0d4fdd Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 12 Sep 2024 21:10:06 +0200
Subject: [PATCH 44/69] fix: CustomerRecovery transalate label

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

diff --git a/src/pages/Customer/Card/CustomerRecoveries.vue b/src/pages/Customer/Card/CustomerRecoveries.vue
index 3ea8998e9..48576ca20 100644
--- a/src/pages/Customer/Card/CustomerRecoveries.vue
+++ b/src/pages/Customer/Card/CustomerRecoveries.vue
@@ -92,7 +92,7 @@ function setFinished(id) {
         :disable-option="{ card: true }"
         :create="{
             urlCreate: 'Recoveries',
-            title: 'New recovery',
+            title: t('New recovery'),
             onDataSaved: () => tableRef.reload(),
             formInitialData: { clientFk: route.params.id, started: Date.vnNew() },
         }"

From 6e784bb290d0f472f4e1d2c06a8f92dda8f45b59 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 12 Sep 2024 21:11:51 +0200
Subject: [PATCH 45/69] fix: address-create i18n

---
 src/i18n/locale/en.yml | 1 +
 src/i18n/locale/es.yml | 1 +
 2 files changed, 2 insertions(+)

diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index d29b864b9..213d302cb 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -133,6 +133,7 @@ globals:
         fiscalData: Fiscal data
         billingData: Billing data
         consignees: Consignees
+        'address-create': New address
         notes: Notes
         credits: Credits
         greuges: Greuges
diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml
index 3d905509d..72d4fb1b1 100644
--- a/src/i18n/locale/es.yml
+++ b/src/i18n/locale/es.yml
@@ -136,6 +136,7 @@ globals:
         fiscalData: Datos fiscales
         billingData: Forma de pago
         consignees: Consignatarios
+        'address-create': Nuevo consignatario
         notes: Notas
         credits: Créditos
         greuges: Greuges

From fa408dc2267405062175a77c3d561b3a0fae4302 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 12 Sep 2024 21:20:30 +0200
Subject: [PATCH 46/69] fix: CustomerAddress mobile

---
 src/pages/Customer/Card/CustomerAddress.vue | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/src/pages/Customer/Card/CustomerAddress.vue b/src/pages/Customer/Card/CustomerAddress.vue
index a9e7affef..d76707079 100644
--- a/src/pages/Customer/Card/CustomerAddress.vue
+++ b/src/pages/Customer/Card/CustomerAddress.vue
@@ -179,6 +179,13 @@ const toCustomerAddressEdit = (addressId) => {
                             {{ item.postalCode }} - {{ item.city }},
                             {{ setProvince(item.provinceFk) }}
                         </div>
+                        <div>
+                            {{ item.phone }}
+                            <span v-if="item.mobile"
+                                >,
+                                {{ item.mobile }}
+                            </span>
+                        </div>
                         <div class="flex">
                             <QCheckbox
                                 :label="t('Is equalizated')"

From aca13d91196ed8b0b884a211a2323d013e4c6cd3 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 12 Sep 2024 21:26:28 +0200
Subject: [PATCH 47/69] fix: CustomerBillingData

---
 .../Customer/Card/CustomerBillingData.vue     | 20 ++++++-------------
 1 file changed, 6 insertions(+), 14 deletions(-)

diff --git a/src/pages/Customer/Card/CustomerBillingData.vue b/src/pages/Customer/Card/CustomerBillingData.vue
index 5eeaea50b..a968d0ec8 100644
--- a/src/pages/Customer/Card/CustomerBillingData.vue
+++ b/src/pages/Customer/Card/CustomerBillingData.vue
@@ -3,7 +3,6 @@ import { ref } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useRoute } from 'vue-router';
 
-import FetchData from 'components/FetchData.vue';
 import FormModel from 'components/FormModel.vue';
 import VnRow from 'components/ui/VnRow.vue';
 import VnInput from 'src/components/common/VnInput.vue';
@@ -14,8 +13,6 @@ import CreateBankEntityForm from 'src/components/CreateBankEntityForm.vue';
 const { t } = useI18n();
 const route = useRoute();
 
-const payMethods = ref([]);
-const bankEntitiesOptions = ref([]);
 const bankEntitiesRef = ref(null);
 
 const filter = {
@@ -31,15 +28,6 @@ const getBankEntities = (data, formData) => {
 </script>
 
 <template>
-    <FetchData @on-fetch="(data) => (payMethods = data)" auto-load url="PayMethods" />
-    <FetchData
-        ref="bankEntitiesRef"
-        @on-fetch="(data) => (bankEntitiesOptions = data)"
-        :filter="filter"
-        auto-load
-        url="BankEntities"
-    />
-
     <FormModel
         :url-update="`Clients/${route.params.id}`"
         :url="`Clients/${route.params.id}/getCard`"
@@ -49,8 +37,9 @@ const getBankEntities = (data, formData) => {
         <template #form="{ data, validate }">
             <VnRow>
                 <VnSelect
+                    auto-load
+                    url="PayMethods"
                     :label="t('Billing data')"
-                    :options="payMethods"
                     hide-selected
                     option-label="name"
                     option-value="id"
@@ -69,7 +58,10 @@ const getBankEntities = (data, formData) => {
                 </VnInput>
                 <VnSelectDialog
                     :label="t('Swift / BIC')"
-                    :options="bankEntitiesOptions"
+                    ref="bankEntitiesRef"
+                    :filter="filter"
+                    auto-load
+                    url="BankEntities"
                     :acls="[{ model: 'BankEntity', props: '*', accessType: 'WRITE' }]"
                     :rules="validate('Worker.bankEntity')"
                     hide-selected

From 58be11df046f33259a794effec4bc84b9e0728b0 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Fri, 13 Sep 2024 10:13:03 +0200
Subject: [PATCH 48/69] chore: refs #7828 fix e2e

---
 test/cypress/integration/ticket/ticketDescriptor.spec.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/cypress/integration/ticket/ticketDescriptor.spec.js b/test/cypress/integration/ticket/ticketDescriptor.spec.js
index 516b0f13d..8192b7c7c 100644
--- a/test/cypress/integration/ticket/ticketDescriptor.spec.js
+++ b/test/cypress/integration/ticket/ticketDescriptor.spec.js
@@ -5,7 +5,7 @@ describe('Ticket descriptor', () => {
     const warehouseValue = ':nth-child(1) > :nth-child(6) > .value > span';
     const summaryHeader = '.summaryHeader > div';
     const weight = 25;
-    const weightValue = ':nth-child(10) > .value > span';
+    const weightValue = '.summaryBody.row :nth-child(1) > :nth-child(9) > .value > span';
     beforeEach(() => {
         cy.login('developer');
         cy.viewport(1920, 1080);

From 5796bc9c0a7e0dc27378b1b053eded6c07a53bbb Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Fri, 13 Sep 2024 10:32:40 +0200
Subject: [PATCH 49/69] fix: emit:updateModelValue

---
 src/components/common/VnLocation.vue          |  4 +++
 .../vnComponent/vnLocation.spec.js            | 32 ++++++++++++++++++-
 2 files changed, 35 insertions(+), 1 deletion(-)

diff --git a/src/components/common/VnLocation.vue b/src/components/common/VnLocation.vue
index e95d5a043..b6cbfbafd 100644
--- a/src/components/common/VnLocation.vue
+++ b/src/components/common/VnLocation.vue
@@ -20,6 +20,9 @@ const modelValue = ref(
 function showLabel(data) {
     return `${data.code} - ${data.town}(${data.province}), ${data.country}`;
 }
+const handleModelValue = (data) => {
+    emit('update:model-value', data);
+};
 </script>
 <template>
     <VnSelectDialog
@@ -29,6 +32,7 @@ function showLabel(data) {
             (opt) => (typeof modelValue === 'string' ? modelValue : showLabel(opt))
         "
         url="Postcodes/filter"
+        @update:model-value="handleModelValue"
         :use-like="false"
         :label="t('Location')"
         :placeholder="t('search_by_postalcode')"
diff --git a/test/cypress/integration/vnComponent/vnLocation.spec.js b/test/cypress/integration/vnComponent/vnLocation.spec.js
index 614b93a8a..8c187cc7e 100644
--- a/test/cypress/integration/vnComponent/vnLocation.spec.js
+++ b/test/cypress/integration/vnComponent/vnLocation.spec.js
@@ -33,11 +33,40 @@ describe('VnLocation', () => {
             cy.login('developer');
             cy.visit('/#/supplier/567/fiscal-data', { timeout: 7000 });
             cy.waitForElement('.q-form');
-            cy.get(createLocationButton).click();
         });
+        it('Fin by postalCode', () => {
+            const postCode = '46600';
+            const firstOption = '[role="listbox"] .q-item:nth-child(1)';
+
+            cy.get(inputLocation).click();
+            cy.get(inputLocation).clear();
+            cy.get(inputLocation).type(postCode);
+            cy.get(locationOptions).should('have.length.at.least', 2);
+            cy.get(firstOption).click();
+            cy.get('.q-btn-group > .q-btn--standard > .q-btn__content > .q-icon').click();
+            cy.reload();
+            cy.waitForElement('.q-form');
+            // cy.get('[href="#/supplier/567/basic-data"] > .q-item__section--main').click();
+            // cy.get(
+            //     '[href="#/supplier/567/fiscal-data"] > .q-item__section--main'
+            // ).click();
+            cy.get(inputLocation).should(
+                'have.value',
+                '46600 - Valencia(Province one), España'
+            );
+
+            // cy.get('.q-form > .q-card > .vn-row:nth-child(6)')
+            // .find('input')
+            // .invoke('val')
+            // .then((text) => {
+            //     expect(text).to.contain('46600 - Valencia(Province one), España');
+            // });
+        });
+
         it('Create postCode', () => {
             const postCode = '1234475';
             const province = 'Valencia';
+            cy.get(createLocationButton).click();
             cy.get('.q-card > h1').should('have.text', 'New postcode');
             cy.get(dialogInputs).eq(0).clear();
             cy.get(dialogInputs).eq(0).type(postCode);
@@ -54,6 +83,7 @@ describe('VnLocation', () => {
         it('Create city', () => {
             const postCode = '9011';
             const province = 'Saskatchew';
+            cy.get(createLocationButton).click();
             cy.get(dialogInputs).eq(0).type(postCode);
             // city create button
             cy.get(

From a56dc7aa089c2b46d37bb92aa2c7279316f93b07 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Fri, 13 Sep 2024 10:33:08 +0200
Subject: [PATCH 50/69] test: fix test

---
 .../integration/vnComponent/vnLocation.spec.js        | 11 -----------
 1 file changed, 11 deletions(-)

diff --git a/test/cypress/integration/vnComponent/vnLocation.spec.js b/test/cypress/integration/vnComponent/vnLocation.spec.js
index 8c187cc7e..3533a3c1f 100644
--- a/test/cypress/integration/vnComponent/vnLocation.spec.js
+++ b/test/cypress/integration/vnComponent/vnLocation.spec.js
@@ -46,21 +46,10 @@ describe('VnLocation', () => {
             cy.get('.q-btn-group > .q-btn--standard > .q-btn__content > .q-icon').click();
             cy.reload();
             cy.waitForElement('.q-form');
-            // cy.get('[href="#/supplier/567/basic-data"] > .q-item__section--main').click();
-            // cy.get(
-            //     '[href="#/supplier/567/fiscal-data"] > .q-item__section--main'
-            // ).click();
             cy.get(inputLocation).should(
                 'have.value',
                 '46600 - Valencia(Province one), España'
             );
-
-            // cy.get('.q-form > .q-card > .vn-row:nth-child(6)')
-            // .find('input')
-            // .invoke('val')
-            // .then((text) => {
-            //     expect(text).to.contain('46600 - Valencia(Province one), España');
-            // });
         });
 
         it('Create postCode', () => {

From 48a5c15b9d8a466676c4b069a0e67b41f4b5c14c Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Fri, 13 Sep 2024 10:54:09 +0200
Subject: [PATCH 51/69] feat: refs #7828 create axios instance which no manage
 errors

---
 src/boot/axios.js                           | 8 ++++++--
 src/pages/Worker/Card/WorkerTimeControl.vue | 6 ++++--
 2 files changed, 10 insertions(+), 4 deletions(-)

diff --git a/src/boot/axios.js b/src/boot/axios.js
index 9b32275bd..99a163cca 100644
--- a/src/boot/axios.js
+++ b/src/boot/axios.js
@@ -5,8 +5,10 @@ import useNotify from 'src/composables/useNotify.js';
 
 const session = useSession();
 const { notify } = useNotify();
+const baseUrl = '/api/';
 
-axios.defaults.baseURL = '/api/';
+axios.defaults.baseURL = baseUrl;
+const axiosNoError = axios.create({ baseURL: baseUrl });
 
 const onRequest = (config) => {
     const token = session.getToken();
@@ -79,5 +81,7 @@ const onResponseError = (error) => {
 
 axios.interceptors.request.use(onRequest, onRequestError);
 axios.interceptors.response.use(onResponse, onResponseError);
+axiosNoError.interceptors.request.use(onRequest);
+axiosNoError.interceptors.response.use(onResponse);
 
-export { onRequest, onResponseError };
+export { onRequest, onResponseError, axiosNoError };
diff --git a/src/pages/Worker/Card/WorkerTimeControl.vue b/src/pages/Worker/Card/WorkerTimeControl.vue
index 3893305a5..9ae91f8ce 100644
--- a/src/pages/Worker/Card/WorkerTimeControl.vue
+++ b/src/pages/Worker/Card/WorkerTimeControl.vue
@@ -2,6 +2,7 @@
 import { useI18n } from 'vue-i18n';
 import { useRoute } from 'vue-router';
 import { onMounted, ref, computed, onBeforeMount, nextTick, reactive } from 'vue';
+import { axiosNoError } from 'src/boot/axios';
 
 import FetchData from 'components/FetchData.vue';
 import WorkerTimeHourChip from 'pages/Worker/Card/WorkerTimeHourChip.vue';
@@ -266,10 +267,11 @@ const fetchWeekData = async () => {
     };
     try {
         const mail = (
-            await axios.get(`Workers/${route.params.id}/mail`, {
+            await axiosNoError.get(`Workers/${route.params.id}/mail`, {
                 params: { filter: { where } },
             })
         ).data[0];
+
         if (!mail) state.value = null;
         else {
             state.value = mail.state;
@@ -277,7 +279,7 @@ const fetchWeekData = async () => {
         }
 
         canResend.value = !!(
-            await axios.get('WorkerTimeControlMails/count', { params: { where } })
+            await axiosNoError.get('WorkerTimeControlMails/count', { params: { where } })
         ).data.count;
     } catch (err) {
         console.error('Error fetching week data');

From 0f604ea8b5eaf3485a4678326aa5e327f9154a81 Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Fri, 13 Sep 2024 11:34:45 +0200
Subject: [PATCH 52/69] fix: refs #7500 fixed e2e test

---
 test/cypress/integration/entry/entryDms.spec.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/test/cypress/integration/entry/entryDms.spec.js b/test/cypress/integration/entry/entryDms.spec.js
index ed4a3d79c..47dcdba9e 100644
--- a/test/cypress/integration/entry/entryDms.spec.js
+++ b/test/cypress/integration/entry/entryDms.spec.js
@@ -23,7 +23,7 @@ describe('EntryDms', () => {
             expect(value).to.have.length(newFileTd++);
             const newRowSelector = `tbody > :nth-child(${newFileTd})`;
             cy.waitForElement(newRowSelector);
-            cy.validateRow(newRowSelector, [u, u, u, u, 'ENTRADA ID 1']);
+            cy.validateRow(newRowSelector, [u, u, u, u, u, 'ENTRADA ID 1']);
 
             //Edit new dms
             const newDescription = 'entry id 1 modified';
@@ -38,7 +38,7 @@ describe('EntryDms', () => {
             cy.saveCard();
             cy.reload();
 
-            cy.validateRow(newRowSelector, [u, u, u, u, newDescription]);
+            cy.validateRow(newRowSelector, [u, u, u, u, u, newDescription]);
         });
     });
 });

From eca34e2c0fb1eef9d4ab4e2d86d4e952d61766bd Mon Sep 17 00:00:00 2001
From: guillermo <guillermo@verdnatura.es>
Date: Fri, 13 Sep 2024 14:57:33 +0200
Subject: [PATCH 53/69] feat: refs #7644 Icon

---
 src/pages/Entry/EntryBuysTableDialog.vue | 4 ++--
 src/pages/Entry/locale/en.yml            | 2 +-
 src/pages/Entry/locale/es.yml            | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/pages/Entry/EntryBuysTableDialog.vue b/src/pages/Entry/EntryBuysTableDialog.vue
index 9a76770aa..0f9be6298 100644
--- a/src/pages/Entry/EntryBuysTableDialog.vue
+++ b/src/pages/Entry/EntryBuysTableDialog.vue
@@ -116,7 +116,7 @@ const entriesTableColumns = computed(() => [
                                         {{ col.value }}
                                     </QTd>
                                     <QBtn
-                                        icon="print"
+                                        icon="visibility"
                                         v-if="props.row.stickers > 0"
                                         :loading="isLoading"
                                         @click="
@@ -126,7 +126,7 @@ const entriesTableColumns = computed(() => [
                                         "
                                         unelevated
                                     >
-                                        <QTooltip>{{ t('printLabel') }}</QTooltip>
+                                        <QTooltip>{{ t('viewLabel') }}</QTooltip>
                                     </QBtn>
                                 </QTr>
                             </template>
diff --git a/src/pages/Entry/locale/en.yml b/src/pages/Entry/locale/en.yml
index f0965b097..a9faa814b 100644
--- a/src/pages/Entry/locale/en.yml
+++ b/src/pages/Entry/locale/en.yml
@@ -11,4 +11,4 @@ shipped: Shipped
 fromShipped: Shipped(from)
 toShipped: Shipped(to)
 printLabels: Print stickers
-printLabel: Print sticker
+viewLabel: View sticker
diff --git a/src/pages/Entry/locale/es.yml b/src/pages/Entry/locale/es.yml
index aba04571e..eb1e3f88a 100644
--- a/src/pages/Entry/locale/es.yml
+++ b/src/pages/Entry/locale/es.yml
@@ -15,4 +15,4 @@ shipped: F. salida
 fromShipped: F. salida(desde)
 toShipped: F. salida(hasta)
 printLabels: Imprimir etiquetas
-printLabel: Imprimir etiqueta
+viewLabel: Ver etiqueta

From 6e6af0af8085fb1e82da7f3c3c17620ba2ecf0e7 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Fri, 13 Sep 2024 15:16:40 +0200
Subject: [PATCH 54/69] perf: improve style

---
 src/pages/Item/ItemFixedPrice.vue | 110 ++++++++++++------------------
 1 file changed, 44 insertions(+), 66 deletions(-)

diff --git a/src/pages/Item/ItemFixedPrice.vue b/src/pages/Item/ItemFixedPrice.vue
index d91b5189e..bd8c4b78c 100644
--- a/src/pages/Item/ItemFixedPrice.vue
+++ b/src/pages/Item/ItemFixedPrice.vue
@@ -76,16 +76,8 @@ const columns = computed(() => [
         name: 'rate2',
         ...defaultColumnAttrs,
         cardVisible: true,
-        columnField: {
-            class: 'expand',
-            component: 'input',
-            type: 'number',
-        },
-        columnFilter: {
-            class: 'expand',
-            component: 'input',
-            type: 'number',
-        },
+        component: 'input',
+        type: 'number',
     },
     {
         label: t('item.fixedPrice.packingPrice'),
@@ -93,35 +85,18 @@ const columns = computed(() => [
         name: 'rate3',
         ...defaultColumnAttrs,
         cardVisible: true,
-        columnField: {
-            class: 'expand',
-            component: 'input',
-            type: 'number',
-        },
-        columnFilter: {
-            class: 'expand',
-            component: 'input',
-            type: 'number',
-        },
+        component: 'input',
+        type: 'number',
     },
 
     {
         label: t('item.fixedPrice.minPrice'),
         field: 'minPrice',
-        columnClass: 'shrink',
         name: 'minPrice',
         ...defaultColumnAttrs,
         cardVisible: true,
-        columnField: {
-            class: 'expand',
-            component: 'input',
-            type: 'number',
-        },
-        columnFilter: {
-            class: 'expand',
-            component: 'input',
-            type: 'number',
-        },
+        component: 'input',
+        type: 'number',
     },
     {
         label: t('item.fixedPrice.started'),
@@ -162,16 +137,9 @@ const columns = computed(() => [
         name: 'warehouseFk',
         ...defaultColumnAttrs,
         columnClass: 'shrink',
-        columnFilter: {
-            component: 'select',
-        },
-        columnField: {
-            component: 'select',
-            class: 'expand',
-        },
-        attrs: {
-            options: warehousesOptions,
-        },
+        component: 'select',
+
+        options: warehousesOptions,
     },
     {
         align: 'right',
@@ -518,29 +486,35 @@ function handleOnDataSave({ CrudModelRef }) {
                 </span>
                 <span class="subName">{{ row.subName }}</span>
                 <ItemDescriptorProxy :id="row.itemFk" />
-                <FetchedTags style="width: max-content; max-width: 220px" :item="row" />
+                <FetchedTags :item="row" />
             </template>
             <template #column-rate2="props">
-                <VnInput
-                    mask="###.##"
-                    v-model.number="props.row.rate2"
-                    v-on="getRowUpdateInputEvents(props)"
-                >
-                    <template #append>€</template>
-                </VnInput>
+                <QTd class="col">
+                    <VnInput
+                        type="currency"
+                        style="width: 75px"
+                        v-model.number="props.row.rate2"
+                        v-on="getRowUpdateInputEvents(props)"
+                    >
+                        <template #append>€</template>
+                    </VnInput>
+                </QTd>
             </template>
             <template #column-rate3="props">
-                <VnInput
-                    mask="###.##"
-                    v-model.number="props.row.rate3"
-                    v-on="getRowUpdateInputEvents(props)"
-                >
-                    <template #append>€</template>
-                </VnInput>
+                <QTd class="col">
+                    <VnInput
+                        style="width: 75px"
+                        type="currency"
+                        v-model.number="props.row.rate3"
+                        v-on="getRowUpdateInputEvents(props)"
+                    >
+                        <template #append>€</template>
+                    </VnInput>
+                </QTd>
             </template>
             <template #column-minPrice="props">
                 <QTd class="col">
-                    <div class="row" style="width: 115px">
+                    <div class="row">
                         <QCheckbox
                             :model-value="props.row.hasMinPrice"
                             @update:model-value="updateMinPrice($event, props)"
@@ -549,6 +523,8 @@ function handleOnDataSave({ CrudModelRef }) {
                         />
                         <VnInput
                             class="col"
+                            type="currency"
+                            mask="###.##"
                             :disable="props.row.hasMinPrice === 1"
                             v-model.number="props.row.minPrice"
                             v-on="getRowUpdateInputEvents(props)"
@@ -577,15 +553,17 @@ function handleOnDataSave({ CrudModelRef }) {
                 />
             </template>
             <template #column-warehouseFk="props">
-                <VnSelect
-                    style="max-width: 150px"
-                    :options="warehousesOptions"
-                    hide-selected
-                    option-label="name"
-                    option-value="id"
-                    v-model="props.row.warehouseFk"
-                    v-on="getRowUpdateInputEvents(props, false, 'select')"
-                />
+                <QTd class="col">
+                    <VnSelect
+                        style="max-width: 150px"
+                        :options="warehousesOptions"
+                        hide-selected
+                        option-label="name"
+                        option-value="id"
+                        v-model="props.row.warehouseFk"
+                        v-on="getRowUpdateInputEvents(props, false, 'select')"
+                    />
+                </QTd>
             </template>
             <template #column-deleteAction="{ row, rowIndex }">
                 <QIcon

From 401400bdcfc8b03eab00a4e40dc78ad1ea1fe9e7 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Fri, 13 Sep 2024 22:52:26 +0200
Subject: [PATCH 55/69] fix: remove FetchData

---
 src/components/common/VnSelect.vue | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/src/components/common/VnSelect.vue b/src/components/common/VnSelect.vue
index 837a3d9f6..e767fb201 100644
--- a/src/components/common/VnSelect.vue
+++ b/src/components/common/VnSelect.vue
@@ -1,6 +1,7 @@
 <script setup>
-import { ref, toRefs, computed, watch, onMounted } from 'vue';
+import { ref, toRefs, computed, watch, onMounted, useAttrs } from 'vue';
 import { useI18n } from 'vue-i18n';
+import FetchData from 'src/components/FetchData.vue';
 import { useValidator } from 'src/composables/useValidator';
 
 const emit = defineEmits(['update:modelValue', 'update:options', 'remove']);
@@ -225,6 +226,16 @@ const getVal = (val) => ($props.useLike ? { like: `%${val}%` } : val);
 </script>
 
 <template>
+    <FetchData
+        ref="dataRef"
+        :url="$props.url"
+        @on-fetch="(data) => setOptions(data)"
+        :where="where || { [optionValue]: value }"
+        :limit="limit"
+        :sort-by="sortBy"
+        :fields="fields"
+        :params="params"
+    />
     <QSelect
         v-model="value"
         :options="myOptions"

From b6cce74449dfbf28c95ab36832635d41e20011bd Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Fri, 13 Sep 2024 23:11:18 +0200
Subject: [PATCH 56/69] fix: merge conflicts

---
 src/components/common/VnInputDate.vue | 53 ++++++++++++++-------------
 src/components/common/VnInputTime.vue |  6 ++-
 src/components/common/VnSelect.vue    | 12 +++---
 3 files changed, 37 insertions(+), 34 deletions(-)

diff --git a/src/components/common/VnInputDate.vue b/src/components/common/VnInputDate.vue
index 96e47d6d7..7113cadf9 100644
--- a/src/components/common/VnInputDate.vue
+++ b/src/components/common/VnInputDate.vue
@@ -2,6 +2,7 @@
 import { onMounted, watch, computed, ref } from 'vue';
 import { date } from 'quasar';
 import { useI18n } from 'vue-i18n';
+import { useAttrs } from 'vue';
 
 const model = defineModel({ type: [String, Date] });
 const $props = defineProps({
@@ -14,29 +15,19 @@ const $props = defineProps({
         default: true,
     },
 });
+import { useValidator } from 'src/composables/useValidator';
+const { validations } = useValidator();
 
 const { t } = useI18n();
-const requiredFieldRule = (val) => !!val || t('globals.fieldRequired');
+const requiredFieldRule = (val) => validations().required($attrs.required, val);
 
 const dateFormat = 'DD/MM/YYYY';
 const isPopupOpen = ref();
 const hover = ref();
 const mask = ref();
+const $attrs = useAttrs();
 
-onMounted(() => {
-    // fix quasar bug
-    mask.value = '##/##/####';
-});
-
-const styleAttrs = computed(() => {
-    return $props.isOutlined
-        ? {
-              dense: true,
-              outlined: true,
-              rounded: true,
-          }
-        : {};
-});
+const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])];
 
 const formattedDate = computed({
     get() {
@@ -48,15 +39,12 @@ const formattedDate = computed({
         let newDate;
         if (value) {
             // parse input
-            if (value.includes('/')) {
-                if (value.length == 6) value = value + new Date().getFullYear();
-                if (value.length >= 10) {
-                    if (value.at(2) == '/') value = value.split('/').reverse().join('/');
-                    value = date.formatDate(
-                        new Date(value).toISOString(),
-                        'YYYY-MM-DDTHH:mm:ss.SSSZ'
-                    );
-                }
+            if (value.includes('/') && value.length >= 10) {
+                if (value.at(2) == '/') value = value.split('/').reverse().join('/');
+                value = date.formatDate(
+                    new Date(value).toISOString(),
+                    'YYYY-MM-DDTHH:mm:ss.SSSZ'
+                );
             }
             const [year, month, day] = value.split('-').map((e) => parseInt(e));
             newDate = new Date(year, month - 1, day);
@@ -79,12 +67,25 @@ const formattedDate = computed({
 const popupDate = computed(() =>
     model.value ? date.formatDate(new Date(model.value), 'YYYY/MM/DD') : model.value
 );
-
+onMounted(() => {
+    // fix quasar bug
+    mask.value = '##/##/####';
+});
 watch(
     () => model.value,
     (val) => (formattedDate.value = val),
     { immediate: true }
 );
+
+const styleAttrs = computed(() => {
+    return $props.isOutlined
+        ? {
+              dense: true,
+              outlined: true,
+              rounded: true,
+          }
+        : {};
+});
 </script>
 
 <template>
@@ -96,7 +97,7 @@ watch(
             placeholder="dd/mm/aaaa"
             v-bind="{ ...$attrs, ...styleAttrs }"
             :class="{ required: $attrs.required }"
-            :rules="$attrs.required ? [requiredFieldRule] : null"
+            :rules="mixinRules"
             :clearable="false"
             @click="isPopupOpen = true"
         >
diff --git a/src/components/common/VnInputTime.vue b/src/components/common/VnInputTime.vue
index b610dc1e9..27395aa9e 100644
--- a/src/components/common/VnInputTime.vue
+++ b/src/components/common/VnInputTime.vue
@@ -1,8 +1,10 @@
 <script setup>
-import { computed, ref } from 'vue';
+import { computed, ref, useAttrs } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { date } from 'quasar';
-
+import { useValidator } from 'src/composables/useValidator';
+const { validations } = useValidator();
+const $attrs = useAttrs();
 const model = defineModel({ type: String });
 const props = defineProps({
     timeOnly: {
diff --git a/src/components/common/VnSelect.vue b/src/components/common/VnSelect.vue
index e767fb201..ed842103f 100644
--- a/src/components/common/VnSelect.vue
+++ b/src/components/common/VnSelect.vue
@@ -3,7 +3,6 @@ import { ref, toRefs, computed, watch, onMounted, useAttrs } from 'vue';
 import { useI18n } from 'vue-i18n';
 import FetchData from 'src/components/FetchData.vue';
 import { useValidator } from 'src/composables/useValidator';
-
 const emit = defineEmits(['update:modelValue', 'update:options', 'remove']);
 
 const $props = defineProps({
@@ -84,10 +83,11 @@ const $props = defineProps({
         default: false,
     },
 });
-
+const { validations } = useValidator();
+const requiredFieldRule = (val) => validations().required($attrs.required, val);
+const $attrs = useAttrs();
 const { t } = useI18n();
-const requiredFieldRule = (val) => val ?? t('globals.fieldRequired');
-
+const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])];
 const { optionLabel, optionValue, optionFilter, optionFilterValue, options, modelValue } =
     toRefs($props);
 const myOptions = ref([]);
@@ -100,7 +100,6 @@ const noOneOpt = ref({
     [optionValue.value]: false,
     [optionLabel.value]: noOneText,
 });
-const { validations } = useValidator();
 
 const value = computed({
     get() {
@@ -251,8 +250,9 @@ const getVal = (val) => ($props.useLike ? { like: `%${val}%` } : val);
         ref="vnSelectRef"
         lazy-rules
         :class="{ required: $attrs.required }"
-        :rules="$attrs.required ? [requiredFieldRule] : null"
+        :rules="mixinRules"
         virtual-scroll-slice-size="options.length"
+        hide-bottom-space
     >
         <template v-if="isClearable" #append>
             <QIcon

From 93488a5750b2a3de2c49ddff952defa55a3f3386 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Fri, 13 Sep 2024 23:19:16 +0200
Subject: [PATCH 57/69] fix: styles

---
 src/components/common/VnInputDate.vue | 1 +
 src/components/common/VnInputTime.vue | 1 +
 2 files changed, 2 insertions(+)

diff --git a/src/components/common/VnInputDate.vue b/src/components/common/VnInputDate.vue
index 7113cadf9..fd8993d6f 100644
--- a/src/components/common/VnInputDate.vue
+++ b/src/components/common/VnInputDate.vue
@@ -100,6 +100,7 @@ const styleAttrs = computed(() => {
             :rules="mixinRules"
             :clearable="false"
             @click="isPopupOpen = true"
+            hide-bottom-space
         >
             <template #append>
                 <QIcon
diff --git a/src/components/common/VnInputTime.vue b/src/components/common/VnInputTime.vue
index 27395aa9e..42ec79479 100644
--- a/src/components/common/VnInputTime.vue
+++ b/src/components/common/VnInputTime.vue
@@ -79,6 +79,7 @@ function dateToTime(newDate) {
             :rules="mixinRules"
             @click="isPopupOpen = false"
             type="time"
+            hide-bottom-space
         >
             <template #append>
                 <QIcon

From deb30ee9551cab217665947ec1fd1df37362536a Mon Sep 17 00:00:00 2001
From: pablone <pablone@verdnatura.es>
Date: Mon, 16 Sep 2024 08:59:36 +0200
Subject: [PATCH 58/69] feat: refs #7404 travel m3 form

---
 src/components/VnTable/VnOrder.vue            |   2 +-
 src/pages/Entry/EntryStockBought.vue          | 102 +++++++++---------
 src/pages/Entry/EntryStockBoughtDetail.vue    |  66 +++++++-----
 src/pages/Entry/EntryStockBoughtFilter.vue    |  29 +++--
 .../integration/entry/stockBought.spec.js     |  15 ++-
 5 files changed, 116 insertions(+), 98 deletions(-)

diff --git a/src/components/VnTable/VnOrder.vue b/src/components/VnTable/VnOrder.vue
index 98c7ab392..7fdd23b78 100644
--- a/src/components/VnTable/VnOrder.vue
+++ b/src/components/VnTable/VnOrder.vue
@@ -4,7 +4,7 @@ import { useArrayData } from 'composables/useArrayData';
 const model = defineModel({ type: Object });
 const $props = defineProps({
     name: {
-        type: String,
+        type: [String, Boolean],
         default: '',
     },
     label: {
diff --git a/src/pages/Entry/EntryStockBought.vue b/src/pages/Entry/EntryStockBought.vue
index ad338acb1..c8f147b1f 100644
--- a/src/pages/Entry/EntryStockBought.vue
+++ b/src/pages/Entry/EntryStockBought.vue
@@ -1,12 +1,13 @@
 <script setup>
-import { onMounted, ref, watch } from 'vue';
+import { ref } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useState } from 'src/composables/useState';
 import { useQuasar } from 'quasar';
 
 import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
-import axios from 'axios';
 import FetchData from 'components/FetchData.vue';
+import FormModelPopup from 'components/FormModelPopup.vue';
+import VnInput from 'src/components/common/VnInput.vue';
 import VnRow from 'components/ui/VnRow.vue';
 import RightMenu from 'src/components/common/RightMenu.vue';
 import EntryStockBoughtFilter from './EntryStockBoughtFilter.vue';
@@ -31,7 +32,9 @@ const columns = [
         align: 'left',
         name: 'workerFk',
         label: t('Buyer'),
+        isTitle: true,
         component: 'select',
+        cardVisible: true,
         create: true,
         attrs: {
             url: 'Workers/activeWithInheritedRole',
@@ -82,6 +85,7 @@ const columns = [
                         component: EntryStockBoughtDetail,
                         componentProps: {
                             workerFk: row.workerFk,
+                            dated: userParams.value.dated,
                         },
                     });
                 },
@@ -91,6 +95,7 @@ const columns = [
 ];
 
 const fetchDataRef = ref();
+const travelDialogRef = ref(false);
 const tableRef = ref();
 const travel = ref(null);
 const userParams = ref({
@@ -108,38 +113,21 @@ const filter = ref({
         {
             relation: 'warehouseIn',
             scope: {
-                where: { code: 'vnh' },
+                fields: ['code'],
             },
         },
     ],
 });
 
-function getFilter(dated) {
-    console.log('dated: ', dated);
-    const shipped = dated ? new Date(dated) : Date.vnNew();
-    shipped.setHours(0, 0, 0, 0);
-    return {
-        where: {
-            shipped,
-            m3: { neq: null },
-        },
-        include: [
-            {
-                relation: 'warehouseIn',
-                scope: {
-                    where: { code: 'vnh' },
-                },
-            },
-        ],
-    };
-}
-
 const setUserParams = async ({ dated }) => {
     const shipped = (dated ? new Date(dated) : Date.vnNew()).setHours(0, 0, 0, 0);
-
     filter.value.where.shipped = shipped;
     fetchDataRef.value?.fetch();
 };
+
+function openDialog() {
+    travelDialogRef.value = true;
+}
 </script>
 <template>
     <VnSubToolbar>
@@ -147,35 +135,55 @@ const setUserParams = async ({ dated }) => {
             <FetchData
                 ref="fetchDataRef"
                 url="Travels"
-                limit="1"
                 auto-load
                 :filter="filter"
-                @on-fetch="(data) => (travel = data)"
+                @on-fetch="
+                    (data) => {
+                        travel = data.filter((data) => data.warehouseIn.code === 'VNH');
+                    }
+                "
             />
-
-            <VnRow>
-                <div v-if="travel" class="q-pa-md">
-                    <QIcon
-                        name="local_airport"
-                        class="fill-icon q-mr-sm"
-                        size="md"
-                        :title="t('Travel')"
-                        color="primary"
-                    />
+            <VnRow class="travel">
+                <div v-if="travel">
+                    <span style="color: var(--vn-label-color)">
+                        {{ t('Booked trucks') }}:
+                    </span>
                     <span>
-                        {{ t('Booked trucks') + ': ' + travel[0]?.m3 }}
+                        {{ travel[0]?.m3 }}
                     </span>
                     <QBtn
+                        v-if="travel[0]?.m3"
                         style="max-width: 20%"
                         flat
                         icon="edit"
-                        @click="navigate(travel[0]?.id)"
+                        @click="openDialog()"
                         :title="t('Edit travel')"
+                        color="primary"
                     />
                 </div>
             </VnRow>
         </template>
     </VnSubToolbar>
+    <QDialog v-model="travelDialogRef" :maximized="true" :class="['vn-row', 'wrap']">
+        <FormModelPopup
+            :url-update="`Travels/${travel[0].id}`"
+            model="travel"
+            :title="t('Travel m3')"
+            :form-initial-data="{ id: travel[0].id, m3: travel[0].m3 }"
+            @on-data-saved="fetchDataRef.fetch()"
+        >
+            <template #form-inputs="{ data }">
+                <VnInput
+                    v-model="data.id"
+                    :label="t('id')"
+                    type="number"
+                    disable
+                    readonly
+                />
+                <VnInput v-model="data.m3" :label="t('m3')" type="number" />
+            </template>
+        </FormModelPopup>
+    </QDialog>
     <RightMenu>
         <template #right-panel>
             <EntryStockBoughtFilter
@@ -196,10 +204,7 @@ const setUserParams = async ({ dated }) => {
             :create="{
                 urlCreate: 'StockBoughts',
                 title: t('Reserve some space'),
-                onDataSaved: () => {
-                    tableRef.reload();
-                    fetchDataRef.reload();
-                },
+                onDataSaved: () => tableRef.reload(),
                 formInitialData: {
                     workerFk: user.id,
                     dated: Date.vnNow(),
@@ -214,17 +219,14 @@ const setUserParams = async ({ dated }) => {
                 <span class="link" @click.stop>
                     {{ row?.worker?.user?.name }}
                     <WorkerDescriptorProxy :id="row?.workerFk" />
-                </span>
-            </template>
+                </span> </template
+            >0
         </VnTable>
     </QPage>
 </template>
-<style lang="scss">
-.trucks {
-    text-align: center;
-    margin: 3%;
-    border: 3px solid var(--vn-header-color);
-    padding: 1%;
+<style lang="css" scoped>
+.travel {
+    margin-bottom: 0px;
 }
 </style>
 <i18n>
diff --git a/src/pages/Entry/EntryStockBoughtDetail.vue b/src/pages/Entry/EntryStockBoughtDetail.vue
index b9420af71..6d9227ad2 100644
--- a/src/pages/Entry/EntryStockBoughtDetail.vue
+++ b/src/pages/Entry/EntryStockBoughtDetail.vue
@@ -14,7 +14,7 @@ const $props = defineProps({
         required: true,
     },
     dated: {
-        type: String,
+        type: Date,
         required: true,
     },
 });
@@ -63,34 +63,46 @@ const columns = [
 ];
 </script>
 <template>
-    <QDialog>
-        <VnTable
-            ref="tableRef"
-            data-key="StockBoughtsDetail"
-            :url="customUrl"
-            order="itemName DESC"
-            :columns="columns"
-            :right-search="false"
-            :disable-infinite-scroll="true"
-            :disable-option="{ card: true }"
-            :limit="0"
-            auto-load
-        >
-            <template #column-entryFk="{ row }">
-                <span class="link" @click.stop>
-                    {{ row?.entryFk }}
-                    <EntryDescriptorProxy :id="row.entryFk" />
-                </span>
-            </template>
-            <template #column-itemName="{ row }">
-                <span class="link" @click.stop>
-                    {{ row?.itemName }}
-                    <ItemDescriptorProxy :id="row.itemFk" />
-                </span>
-            </template>
-        </VnTable>
+    <QDialog position="bottom" :maximized="true">
+        <div class="container">
+            <VnTable
+                ref="tableRef"
+                data-key="StockBoughtsDetail"
+                :url="customUrl"
+                order="itemName DESC"
+                :columns="columns"
+                :right-search="false"
+                :disable-infinite-scroll="true"
+                :disable-option="{ card: true }"
+                :limit="0"
+                auto-load
+            >
+                <template #column-entryFk="{ row }">
+                    <span class="link" @click.stop>
+                        {{ row?.entryFk }}
+                        <EntryDescriptorProxy :id="row.entryFk" />
+                    </span>
+                </template>
+                <template #column-itemName="{ row }">
+                    <span class="link" @click.stop>
+                        {{ row?.itemName }}
+                        <ItemDescriptorProxy :id="row.itemFk" />
+                    </span>
+                </template>
+            </VnTable>
+        </div>
     </QDialog>
 </template>
+<style lang="css">
+.q-dialog__inner {
+    max-width: 50vw;
+    overflow: auto;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    margin: auto;
+}
+</style>
 <i18n>
     es:
         Buyer: Comprador
diff --git a/src/pages/Entry/EntryStockBoughtFilter.vue b/src/pages/Entry/EntryStockBoughtFilter.vue
index aaf9e7a5b..7694cfe6c 100644
--- a/src/pages/Entry/EntryStockBoughtFilter.vue
+++ b/src/pages/Entry/EntryStockBoughtFilter.vue
@@ -1,11 +1,9 @@
 <script setup>
-import { ref } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { onMounted } from 'vue';
 import { useStateStore } from 'stores/useStateStore';
 
 import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
-import FetchData from 'components/FetchData.vue';
 import VnInputDate from 'src/components/common/VnInputDate.vue';
 
 const { t } = useI18n();
@@ -15,7 +13,6 @@ const props = defineProps({
         required: true,
     },
 });
-const companiesOptions = ref([]);
 const stateStore = useStateStore();
 const emit = defineEmits(['set-user-params']);
 const setUserParams = (params) => {
@@ -27,20 +24,10 @@ onMounted(async () => {
 </script>
 
 <template>
-    <FetchData
-        ref="buyer"
-        url="Workers/activeWithInheritedRole"
-        :filter="{
-            fields: ['id', 'name'],
-            where: { role: 'buyer' },
-        }"
-        order="name"
-        @on-fetch="(data) => (companiesOptions = data)"
-        auto-load
-    />
     <VnFilterPanel
         :data-key="props.dataKey"
         :search-button="true"
+        search-url="table"
         @set-user-params="setUserParams"
     >
         <template #tags="{ tag, formatFn }">
@@ -52,13 +39,25 @@ onMounted(async () => {
         <template #body="{ params }">
             <QItem class="q-my-sm">
                 <QItemSection>
-                    <VnInputDate v-model="params.dated" :label="t('Date')" is-outlined />
+                    <VnInputDate
+                        id="date"
+                        v-model="params.dated"
+                        :label="t('Date')"
+                        is-outlined
+                    />
                 </QItemSection>
             </QItem>
         </template>
     </VnFilterPanel>
 </template>
 <i18n>
+    en:
+        params:
+            dated: Date
+            workerFk: Worker
     es:
         Date: Fecha
+        params:
+            dated: Date
+            workerFk: Trabajador
 </i18n>
diff --git a/test/cypress/integration/entry/stockBought.spec.js b/test/cypress/integration/entry/stockBought.spec.js
index a18e2bd2e..b93afa520 100644
--- a/test/cypress/integration/entry/stockBought.spec.js
+++ b/test/cypress/integration/entry/stockBought.spec.js
@@ -1,15 +1,13 @@
 describe('EntryStockBought', () => {
-    const reserveField = 'input[name="reserve"]';
     beforeEach(() => {
         cy.viewport(1920, 1080);
         cy.login('buyer');
-        cy.visit(
-            `/#/entry/stock-Bought?table={"filter":"{}","dated":"2000-12-31T23:00:00.000Z"}`
-        );
+        cy.visit(`/#/entry/stock-Bought`);
     });
     it('Should edit the reserved space', () => {
+        cy.get('.q-field__native.q-placeholder').should('have.value', '01/01/2001');
         cy.get('tBody > tr').its('length').should('eq', 2);
-        cy.get(reserveField).type('10{enter}');
+        cy.get('input[name="reserve"]').type('10{enter}');
         cy.get('button[title="Save"]').click();
         cy.get('.q-notification__message').should('have.text', 'Data saved');
     });
@@ -33,4 +31,11 @@ describe('EntryStockBought', () => {
             'warningNo data available'
         );
     });
+    it('Should edit travel m3 and refresh', () => {
+        cy.get('.vn-row > div > .q-btn > .q-btn__content > .q-icon').click();
+        cy.get('input[aria-label="m3"]').clear();
+        cy.get('input[aria-label="m3"]').type('60');
+        cy.get('.q-mt-lg > .q-btn--standard > .q-btn__content > .block').click();
+        cy.get('.vn-row > div > :nth-child(2)').should('have.text', '60');
+    });
 });

From 8c4e4a16af99c429d3abbf0a355184df5a6e9769 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Mon, 16 Sep 2024 09:19:08 +0200
Subject: [PATCH 59/69] fix(VnSectionMain): add QPage

---
 src/components/common/VnSectionMain.vue | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/components/common/VnSectionMain.vue b/src/components/common/VnSectionMain.vue
index 0c1641ce1..97a150cf4 100644
--- a/src/components/common/VnSectionMain.vue
+++ b/src/components/common/VnSectionMain.vue
@@ -20,6 +20,8 @@ onMounted(() => (stateStore.leftDrawer = $props.leftDrawer));
         </QScrollArea>
     </QDrawer>
     <QPageContainer>
-        <RouterView></RouterView>
+        <QPage>
+            <RouterView></RouterView>
+        </QPage>
     </QPageContainer>
 </template>

From 4c8238455f9a21da8a59376a249d067352052fd0 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Mon, 16 Sep 2024 10:03:12 +0200
Subject: [PATCH 60/69] fix(ClaimList): fix summary

---
 src/components/VnTable/VnTable.vue      | 4 ++--
 src/components/common/VnSectionMain.vue | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index 899e4d000..6f678d5c1 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -633,7 +633,7 @@ function handleOnDataSaved(_, res) {
             </QTable>
         </template>
     </CrudModel>
-    <QPageSticky :offset="[20, 20]" style="z-index: 2">
+    <QPageSticky v-if="$props.create" :offset="[20, 20]" style="z-index: 2">
         <QBtn
             @click="
                 () =>
@@ -645,7 +645,7 @@ function handleOnDataSaved(_, res) {
             shortcut="+"
         />
         <QTooltip>
-            {{ createForm.title }}
+            {{ createForm?.title }}
         </QTooltip>
     </QPageSticky>
     <QDialog v-model="showForm" transition-show="scale" transition-hide="scale">
diff --git a/src/components/common/VnSectionMain.vue b/src/components/common/VnSectionMain.vue
index 97a150cf4..9975b1011 100644
--- a/src/components/common/VnSectionMain.vue
+++ b/src/components/common/VnSectionMain.vue
@@ -21,7 +21,7 @@ onMounted(() => (stateStore.leftDrawer = $props.leftDrawer));
     </QDrawer>
     <QPageContainer>
         <QPage>
-            <RouterView></RouterView>
+            <RouterView />
         </QPage>
     </QPageContainer>
 </template>

From 263dc29d7a4d4ba22d89fb7715a8f69d968bdfed Mon Sep 17 00:00:00 2001
From: pablone <pablone@verdnatura.es>
Date: Mon, 16 Sep 2024 12:19:03 +0200
Subject: [PATCH 61/69] fix: refs #7404 remove console.log

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

diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index a6a8aec11..b7321fe16 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -692,7 +692,6 @@ function handleOnDataSaved(_) {
                         :column-name="column.name"
                         :label="column.label"
                     >
-                        {{ console.log('data: ', data) }}
                         <VnTableColumn
                             :column="column"
                             :row="{}"

From ae76bd0b0e524035a3fe92aa73bd2a819bab1c0c Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Mon, 16 Sep 2024 12:58:06 +0200
Subject: [PATCH 62/69] refactor: refs #6346 wagons to VnTable

---
 src/pages/Wagon/Type/WagonTypeList.vue | 105 +++++++-------
 src/pages/Wagon/WagonList.vue          | 184 +++++++++++++++++--------
 2 files changed, 186 insertions(+), 103 deletions(-)

diff --git a/src/pages/Wagon/Type/WagonTypeList.vue b/src/pages/Wagon/Type/WagonTypeList.vue
index 7615dea02..2f0d55fbe 100644
--- a/src/pages/Wagon/Type/WagonTypeList.vue
+++ b/src/pages/Wagon/Type/WagonTypeList.vue
@@ -2,14 +2,13 @@
 import { ref, computed } from 'vue';
 import axios from 'axios';
 import { useQuasar } from 'quasar';
-import VnPaginate from 'src/components/ui/VnPaginate.vue';
 import { useArrayData } from 'src/composables/useArrayData';
 import { useI18n } from 'vue-i18n';
 import { useRouter } from 'vue-router';
-import CardList from 'components/ui/CardList.vue';
 import FormModelPopup from 'src/components/FormModelPopup.vue';
 import VnInput from 'src/components/common/VnInput.vue';
 import VnRow from 'src/components/ui/VnRow.vue';
+import VnTable from 'src/components/VnTable/VnTable.vue';
 
 const quasar = useQuasar();
 const arrayData = useArrayData('WagonTypeList');
@@ -17,7 +16,7 @@ const store = arrayData.store;
 const dialog = ref();
 const { push } = useRouter();
 const { t } = useI18n();
-const paginate = ref();
+const tableRef = ref();
 
 const initialData = computed(() => {
     return {
@@ -25,10 +24,46 @@ const initialData = computed(() => {
     };
 });
 
-function reloadData() {
-    initialData.value.name = null;
-    paginate.value.fetch();
-}
+const columns = computed(() => [
+    {
+        align: 'left',
+        name: 'id',
+        label: t('ID'),
+        isId: true,
+        cardVisible: true,
+    },
+    {
+        align: 'left',
+        name: 'name',
+        label: t('Name'),
+        isTitle: true,
+    },
+    {
+        align: 'left',
+        name: 'divisible',
+        label: t('Divisible'),
+        cardVisible: true,
+        component: 'checkbox',
+    },
+    {
+        align: 'right',
+        name: 'tableActions',
+        actions: [
+            {
+                title: t('components.smartCard.openCard'),
+                icon: 'arrow_forward',
+                isPrimary: true,
+                action: (row) => navigate(row.id, row.name),
+            },
+            {
+                title: t('wagon.list.remove'),
+                icon: 'delete',
+                isPrimary: true,
+                action: (row) => remove(row),
+            },
+        ],
+    },
+]);
 
 function navigate(id, name) {
     push({ path: `/wagon/type/${id}/edit`, query: { name } });
@@ -41,51 +76,25 @@ async function remove(row) {
         type: 'positive',
     });
     store.data.splice(store.data.indexOf(row), 1);
+    window.location.reload();
 }
 </script>
 
 <template>
     <QPage class="column items-center q-pa-md">
-        <div class="vn-card-list">
-            <VnPaginate
-                ref="paginate"
-                data-key="WagonTypeList"
-                url="WagonTypes"
-                order="id DESC"
-                auto-load
-            >
-                <template #body="{ rows }">
-                    <CardList
-                        v-for="row of rows"
-                        :key="row.id"
-                        :title="(row.name || '').toString()"
-                        :id="row.id"
-                        @click="navigate(row.id, row.name)"
-                    >
-                        <template #list-items>
-                            <QCheckbox
-                                :label="t('Divisble')"
-                                :model-value="row.divisible"
-                                disable
-                            />
-                        </template>
-                        <template #actions>
-                            <QBtn
-                                :label="t('components.smartCard.openCard')"
-                                @click.stop="navigate(row.id, row.name)"
-                                outline
-                            />
-                            <QBtn
-                                :label="t('wagon.list.remove')"
-                                @click.stop="remove(row)"
-                                color="primary"
-                                style="margin-top: 15px"
-                            />
-                        </template>
-                    </CardList>
-                </template>
-            </VnPaginate>
-        </div>
+        <VnTable
+            ref="tableRef"
+            data-key="WagonTypeList"
+            url="WagonTypes"
+            :columns="columns"
+            auto-load
+            order="id DESC"
+            :right-search="false"
+            :column-search="false"
+            :default-mode="'card'"
+            :disable-option="{ table: true }"
+        >
+        </VnTable>
         <QPageSticky :offset="[18, 18]">
             <QBtn @click.stop="dialog.show()" color="primary" fab icon="add" shortcut="+">
                 <QDialog ref="dialog">
@@ -94,7 +103,7 @@ async function remove(row) {
                         url-create="WagonTypes"
                         model="WagonType"
                         :form-initial-data="initialData"
-                        @on-data-saved="reloadData()"
+                        @on-data-saved="window.location.reload()"
                         auto-load
                     >
                         <template #form-inputs="{ data }">
diff --git a/src/pages/Wagon/WagonList.vue b/src/pages/Wagon/WagonList.vue
index c4824b861..129e803f5 100644
--- a/src/pages/Wagon/WagonList.vue
+++ b/src/pages/Wagon/WagonList.vue
@@ -1,12 +1,13 @@
 <script setup>
 import axios from 'axios';
 import { useQuasar } from 'quasar';
-import VnPaginate from 'src/components/ui/VnPaginate.vue';
 import { useArrayData } from 'src/composables/useArrayData';
 import { useI18n } from 'vue-i18n';
 import { useRouter } from 'vue-router';
-import CardList from 'components/ui/CardList.vue';
-import VnLv from 'components/ui/VnLv.vue';
+import VnTable from 'src/components/VnTable/VnTable.vue';
+import { computed } from 'vue';
+import VnSelect from 'src/components/common/VnSelect.vue';
+import VnInput from 'src/components/common/VnInput.vue';
 
 const quasar = useQuasar();
 const arrayData = useArrayData('WagonList');
@@ -23,14 +24,56 @@ const filter = {
     },
 };
 
+const columns = computed(() => [
+    {
+        align: 'left',
+        name: 'label',
+        label: t('Label'),
+        isTitle: true,
+    },
+    {
+        align: 'left',
+        name: 'plate',
+        label: t('wagon.list.plate'),
+        cardVisible: true,
+    },
+    {
+        align: 'left',
+        name: 'volume',
+        label: t('wagon.list.volume'),
+        cardVisible: true,
+    },
+    {
+        align: 'left',
+        name: 'name',
+        label: t('wagon.list.type'),
+        cardVisible: true,
+        format: (row) => row?.type?.name,
+    },
+    {
+        align: 'right',
+        name: 'tableActions',
+        actions: [
+            {
+                title: t('components.smartCard.openCard'),
+                icon: 'arrow_forward',
+                isPrimary: true,
+                action: (row) => navigate(row.id),
+            },
+            {
+                title: t('wagon.list.remove'),
+                icon: 'delete',
+                isPrimary: true,
+                action: (row) => remove(row),
+            },
+        ],
+    },
+]);
+
 function navigate(id) {
     router.push({ path: `/wagon/${id}/edit` });
 }
 
-function create() {
-    router.push({ path: `/wagon/create` });
-}
-
 async function remove(row) {
     try {
         await axios.delete(`Wagons/${row.id}`).then(async () => {
@@ -39,6 +82,7 @@ async function remove(row) {
                 type: 'positive',
             });
             store.data.splice(store.data.indexOf(row), 1);
+            window.location.reload();
         });
     } catch (error) {
         //
@@ -48,53 +92,83 @@ async function remove(row) {
 
 <template>
     <QPage class="column items-center q-pa-md">
-        <div class="vn-card-list">
-            <VnPaginate
-                data-key="WagonList"
-                url="/Wagons"
-                order="id DESC"
-                :filter="filter"
-                auto-load
-            >
-                <template #body="{ rows }">
-                    <CardList
-                        v-for="row of rows"
-                        :key="row.id"
-                        :title="(row.label || '').toString()"
-                        :id="row.id"
-                        @click="navigate(row.id)"
-                    >
-                        <template #list-items>
-                            <VnLv
-                                :label="t('wagon.list.plate')"
-                                :title-label="t('wagon.list.plate')"
-                                :value="row.plate"
-                            />
-                            <VnLv :label="t('wagon.list.volume')" :value="row?.volume" />
-                            <VnLv
-                                :label="t('wagon.list.type')"
-                                :value="row?.type?.name"
-                            />
-                        </template>
-                        <template #actions>
-                            <QBtn
-                                :label="t('components.smartCard.openCard')"
-                                @click.stop="navigate(row.id)"
-                                outline
-                            />
-                            <QBtn
-                                :label="t('wagon.list.remove')"
-                                @click.stop="remove(row)"
-                                color="primary"
-                                style="margin-top: 15px"
-                            />
-                        </template>
-                    </CardList>
-                </template>
-            </VnPaginate>
-        </div>
-        <QPageSticky position="bottom-right" :offset="[18, 18]">
-            <QBtn @click="create" fab icon="add" color="primary" shortcut="+" />
-        </QPageSticky>
+        <VnTable
+            ref="tableRef"
+            data-key="WagonList"
+            url="Wagons"
+            :filter="filter"
+            :columns="columns"
+            auto-load
+            order="id DESC"
+            :right-search="false"
+            :column-search="false"
+            :default-mode="'card'"
+            :disable-option="{ table: true }"
+            :create="{
+                urlCreate: 'Wagons',
+                title: t('Create new wagon'),
+                onDataSaved: () => {
+                    window.location.reload();
+                },
+                formInitialData: {},
+            }"
+        >
+            <template #more-create-dialog="{ data }">
+                <VnInput
+                    filled
+                    v-model="data.label"
+                    :label="t('wagon.create.label')"
+                    type="number"
+                    min="0"
+                    :rules="[(val) => !!val || t('wagon.warnings.labelNotEmpty')]"
+                />
+                <VnInput
+                    filled
+                    v-model="data.plate"
+                    :label="t('wagon.create.plate')"
+                    :rules="[(val) => !!val || t('wagon.warnings.plateNotEmpty')]"
+                />
+                <VnInput
+                    filled
+                    v-model="data.volume"
+                    :label="t('wagon.create.volume')"
+                    type="number"
+                    min="0"
+                    :rules="[(val) => !!val || t('wagon.warnings.volumeNotEmpty')]"
+                />
+                <VnSelect
+                    url="WagonTypes"
+                    filled
+                    v-model="data.typeFk"
+                    use-input
+                    fill-input
+                    hide-selected
+                    input-debounce="0"
+                    option-label="name"
+                    option-value="id"
+                    emit-value
+                    map-options
+                    :label="t('wagon.create.type')"
+                    :options="filteredWagonTypes"
+                    :rules="[(val) => !!val || t('wagon.warnings.typeNotEmpty')]"
+                    @filter="filterType"
+                >
+                    <template v-if="data.typeFk" #append>
+                        <QIcon
+                            name="cancel"
+                            @click.stop.prevent="data.typeFk = null"
+                            class="cursor-pointer"
+                        />
+                    </template>
+                    <template #no-option>
+                        <QItem>
+                            <QItemSection class="text-grey">
+                                {{ t('wagon.warnings.noData') }}
+                            </QItemSection>
+                        </QItem>
+                    </template>
+                </VnSelect>
+            </template>
+        </VnTable>
     </QPage>
 </template>

From 09cdd2f7e795ec28913cacfa21f0e0f381ade840 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Mon, 16 Sep 2024 15:17:05 +0200
Subject: [PATCH 63/69] chore: changelog

---
 CHANGELOG.md | 181 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 181 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8e1d4c433..666d3f4dd 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,184 @@
+# Version XX.XX - XXXX-XX-XX
+
+### Added 🆕
+
+- chore: refs #6772 fix e2e  (origin/6772-warmfix-fixE2e) by:jorgep
+- chore: refs #7323 worker changes  by:jorgep
+- chore: refs #7353 fix warnings  by:jorgep
+- chore: refs #7353 use Vue component nomenclature  by:jorgep
+- chore: refs #7356 fix type  by:jorgep
+- feat(AccountConnections): use VnToken  by:alexm
+- feat: add key to routerView  by:Javier Segarra
+- feat: add plus shortcut in VnTable  by:Javier Segarra
+- feat: add row  by:Javier Segarra
+- feat: addRow withour dialog  by:Javier Segarra
+- feat: apply mixin  by:Javier Segarra
+- feat  by:Javier Segarra
+- feat: change navBar buttons  by:Javier Segarra
+- feat: dense rows  by:Javier Segarra
+- feat: fields with wrong name  by:jgallego
+- feat: fix bugs and filters  by:Javier Segarra
+- feat: fix refund parameters  by:jgallego
+- feat: handle create row  by:Javier Segarra
+- feat: handle dates  by:Javier Segarra
+- feat: handle qCheckbox 3rd state  by:Javier Segarra
+- feat: imrpove VnInputTime to set cursor at start  by:Javier Segarra
+- feat: keyShortcut directive  by:Javier Segarra
+- feat: minor fixes  by:jgallego
+- feat: only filter by isDestiny  by:Javier Segarra
+- feat: refs #211153 businessDataLinkGrafana  by:robert
+- feat: refs #7129 add km start and end on create form  by:pablone
+- feat: refs #7353 add filter & fix customTags  by:jorgep
+- feat: refs #7353 add locale  by:jorgep
+- feat: refs #7353 add no one opt  by:jorgep
+- feat: refs #7353 add right icons  by:jorgep
+- feat: refs #7353 imporve toDateFormat  by:jorgep
+- feat: refs #7353 salesPerson nickname & id  by:jorgep
+- feat: refs #7353 split sections  by:jorgep
+- feat: refs #7847 remove reload btn  by:jorgep
+- feat: refs #7847 remove reload fn  by:jorgep
+- feat: refs #7889 added shortcuts to modules  by:Jon
+- feat: refs #7911 added shortcut to modules  by:Jon
+- feat: refuncInvoiceForm component  by:jgallego
+- feat: remove duplicity  by:Javier Segarra
+- feat: remove future itemFixedPrices  by:Javier Segarra
+- feat: replace stickyButtons by subtoolbar  by:Javier Segarra
+- feat: required validation  by:Javier Segarra
+- feat: show bad dates  by:Javier Segarra
+- feat: showdate icons  by:Javier Segarra
+- feat: solve ItemFixedFilterPanel  by:Javier Segarra
+- feat: transfer an invoice  by:jgallego
+- feat: try to fix ItemFixedFilterPanel  by:Javier Segarra
+- feat: unnecessary changes  by:Javier Segarra
+- feat: update changelog  (origin/7896_down_devToTest_2436) by:Javier Segarra
+- feat: updates  by:Javier Segarra
+- feat: update version and changelog  by:Javier Segarra
+- feat: vnInput*  by:Javier Segarra
+- feat:  with VnTable  by:Javier Segarra
+- refs #6772 feat: fix approach  by:Javier Segarra
+- refs #6772 feat: refresh shelving.basic-data  by:Javier Segarra
+- style: show subName value  by:Javier Segarra
+
+### Changed 📦
+
+- perf: add v-shortcut in VnCard  by:Javier Segarra
+- perf: approach  by:Javier Segarra
+- perf: change directive location  by:Javier Segarra
+- perf: change slots order  by:Javier Segarra
+- perf: examples  by:Javier Segarra
+- perf: hide icon for VnInputDate  by:Javier Segarra
+- perf: improve ItemFixedPricefilterPanel  by:Javier Segarra
+- perf: improve  mainShrotcutMixin  by:Javier Segarra
+- perf: minor clean code  by:Javier Segarra
+- perf: onRowchange  by:Javier Segarra
+- perf: order by  by:Javier Segarra
+- perf: order components  by:Javier Segarra
+- perf: refs #7889 perf shortcut test  by:Jon
+- perf: remove console.log  by:Javier Segarra
+- perf: remove icons in header slot  by:Javier Segarra
+- perf: remove print variables  by:Javier Segarra
+- perf: restore CustomerBasicData  by:Javier Segarra
+- refactor: deleted useless prop  by:Jon
+- refactor: deleted useless prop in FetchedTags  by:Jon
+- refactor: refs #7323 drop useless code  by:jorgep
+- refactor: refs #7353 clients correction  by:jorgep
+- refactor: refs #7353 clients correction wip  by:jorgep
+- refactor: refs #7353 ease logic  by:jorgep
+- refactor: refs #7353 order correction  by:jorgep
+- refactor: refs #7353 simplify code  by:jorgep
+- refactor: refs #7353 tickets correction  by:jorgep
+- refactor: refs #7353 use global locales  by:jorgep
+- refactor: refs #7354 changed descriptor menu options  by:Jon
+- refactor: refs #7354 changed icon color in table and notification when deleting a zone  by:Jon
+- refactor: refs #7354 fix tableFilters  by:Jon
+- refactor: refs #7354 modified VnInputTime  by:Jon
+- refactor: refs #7354 refactor deliveryPanel  by:Jon
+- refactor: refs #7354 refactor zones section and fixed e2e tests  by:Jon
+- refactor: refs #7354 requested changes  by:Jon
+- refactor: refs #7354 reverse deliveryPanel changes  by:Jon
+- refactor: refs #7354 Zone migration changes  by:Jon
+- refactor: refs #7889 deleted subtitle attr and use keyBinding instead  by:Jon
+- refactor: refs #7889 modified shortcut and dashboard, and added tootlip in LeftMenu  by:Jon
+- refs #6722 perf: not fetch when id not exists  by:Javier Segarra
+- refs #6772 perf: change variable name  by:JAVIER SEGARRA MARTINEZ
+- refs #6772 perf: use ArrayData  (6772_reload_sections) by:Javier Segarra
+- refs #7283 refactor fix ItemDescriptor  by:carlossa
+- refs #7283 refactor ItexDescriptor  by:carlossa
+
+### Fixed 🛠️
+
+- chore: refs #6772 fix e2e  (origin/6772-warmfix-fixE2e) by:jorgep
+- chore: refs #7353 fix warnings  by:jorgep
+- chore: refs #7356 fix type  by:jorgep
+- feat: fix bugs and filters  by:Javier Segarra
+- feat: fix refund parameters  by:jgallego
+- feat: minor fixes  by:jgallego
+- feat: refs #7353 add filter & fix customTags  by:jorgep
+- feat: try to fix ItemFixedFilterPanel  by:Javier Segarra
+- fix: add border-top  by:Javier Segarra
+- fix: added missing descriptors and small details  by:Jon
+- fix branch  by:carlossa
+- fix: call upsert when crudModel haschanges  by:Javier Segarra
+- fix(ClaimList): fix summary  by:alexm
+- fix: cli warnings  by:Javier Segarra
+- fix: editTableOptions  by:Javier Segarra
+- fix events and descriptor menu  by:Jon
+- fix: InvoiceIn sections  (origin/6772_reload_sections) by:Javier Segarra
+- fix: minor changes  by:Javier Segarra
+- fix: minor error whit dates  by:Javier Segarra
+- fix: module icon  by:Javier Segarra
+- fix: options QDate  by:Javier Segarra
+- fix: refs #6900 e2e error  by:jorgep
+- fix: refs #6900 rollback  by:jorgep
+- fix: refs #7353 css  by:jorgep
+- fix: refs #7353 hide search param  (origin/7353-warmfix-fixSearchbar) by:jorgep
+- fix: refs #7353 iron out filter  by:jorgep
+- fix: refs #7353 iron out ticket table  by:jorgep
+- fix: refs #7353 padding  by:jorgep
+- fix: refs #7353 salesClientTable  by:jorgep
+- fix: refs #7353 salesorderTable  by:jorgep
+- fix: refs #7353 saleTicketMonitors  by:jorgep
+- fix: refs #7353 use same datakey  by:jorgep
+- fix: refs #7353 vnTable colors  by:jorgep
+- fix: refs #7354 e2e tests  by:Jon
+- fix: refs #7354 fix delivery days  by:Jon
+- fix: refs #7354 fix list searchbar and filters  by:Jon
+- fix: refs #7354 fix VnSearchbar search for zone section & finished basic tests  by:Jon
+- fix: refs #7354 fix VnTable filters and agency field  by:Jon
+- fix: refs #7354 fix zoneSearchbar  by:Jon
+- fix: refs #7354 requested changes  by:Jon
+- fix: refs #7356 colors  by:jorgep
+- fix: refs #7356 create claim dialog  by:jorgep
+- fix: refs #7889 fixed shortcut test  by:Jon
+- fix: refs #7903 fixed ticket's search bar and keybinding tooltip  by:Jon
+- fix: refs #7911 fixed shortcut and related files  by:Jon
+- fix: remove condition duplicated  by:Javier Segarra
+- fix: remove property  by:Javier Segarra
+- fix tootltip  by:carlossa
+- fix traduction  by:carlossa
+- fix(VnSectionMain): add QPage  by:alexm
+- fix(zone): zoneLocation and the others searchbar  by:alexm
+- refactor: refs #7354 fix tableFilters  by:Jon
+- refactor: refs #7354 refactor zones section and fixed e2e tests  by:Jon
+- refs #6772 feat: fix approach  by:Javier Segarra
+- refs #6772 fix: claimPhoto reload  by:Javier Segarra
+- refs #6896 fix searchbar  by:carlossa
+- refs #6897 fix entry  by:carlossa
+- refs #6899 fix invoiceFix  by:carlossa
+- refs #6899 fix order  by:carlossa
+- refs #7283 fix  by:carlossa
+- refs #7283 fix ItemDescriptor warehouse  by:carlossa
+- refs #7283 refactor fix ItemDescriptor  by:carlossa
+- refs #7355 #7366 fix account, summary, list, travelList, tooltip  by:carlossa
+- refs #7355 fix accountPrivileges  by:carlossa
+- refs #7355 fix accounts, vnTable  by:carlossa
+- refs #7355 fix privileges  by:carlossa
+- refs #7355 fix roles filters  by:carlossa
+- refs #7355 fix total  by:carlossa
+- refs #7355 fix views summarys, entryList, travelList refact  by:carlossa
+- refs #7366  fix travel hours  by:carlossa
+- test: fix e2e  by:Javier Segarra
+
 # Version 24.36 - 2024-08-27
 
 ### Added 🆕

From dc047435f5c7bed72a3d035690b66d5d30de5baa Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Mon, 16 Sep 2024 15:17:31 +0200
Subject: [PATCH 64/69] chore: changelog

---
 CHANGELOG.md | 342 +++++++++++++++++++++++++--------------------------
 package.json |   2 +-
 2 files changed, 172 insertions(+), 172 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 666d3f4dd..f1d10b26e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,183 +1,183 @@
-# Version XX.XX - XXXX-XX-XX
+# Version 24.38 - 2024-09-17
 
 ### Added 🆕
 
-- chore: refs #6772 fix e2e  (origin/6772-warmfix-fixE2e) by:jorgep
-- chore: refs #7323 worker changes  by:jorgep
-- chore: refs #7353 fix warnings  by:jorgep
-- chore: refs #7353 use Vue component nomenclature  by:jorgep
-- chore: refs #7356 fix type  by:jorgep
-- feat(AccountConnections): use VnToken  by:alexm
-- feat: add key to routerView  by:Javier Segarra
-- feat: add plus shortcut in VnTable  by:Javier Segarra
-- feat: add row  by:Javier Segarra
-- feat: addRow withour dialog  by:Javier Segarra
-- feat: apply mixin  by:Javier Segarra
-- feat  by:Javier Segarra
-- feat: change navBar buttons  by:Javier Segarra
-- feat: dense rows  by:Javier Segarra
-- feat: fields with wrong name  by:jgallego
-- feat: fix bugs and filters  by:Javier Segarra
-- feat: fix refund parameters  by:jgallego
-- feat: handle create row  by:Javier Segarra
-- feat: handle dates  by:Javier Segarra
-- feat: handle qCheckbox 3rd state  by:Javier Segarra
-- feat: imrpove VnInputTime to set cursor at start  by:Javier Segarra
-- feat: keyShortcut directive  by:Javier Segarra
-- feat: minor fixes  by:jgallego
-- feat: only filter by isDestiny  by:Javier Segarra
-- feat: refs #211153 businessDataLinkGrafana  by:robert
-- feat: refs #7129 add km start and end on create form  by:pablone
-- feat: refs #7353 add filter & fix customTags  by:jorgep
-- feat: refs #7353 add locale  by:jorgep
-- feat: refs #7353 add no one opt  by:jorgep
-- feat: refs #7353 add right icons  by:jorgep
-- feat: refs #7353 imporve toDateFormat  by:jorgep
-- feat: refs #7353 salesPerson nickname & id  by:jorgep
-- feat: refs #7353 split sections  by:jorgep
-- feat: refs #7847 remove reload btn  by:jorgep
-- feat: refs #7847 remove reload fn  by:jorgep
-- feat: refs #7889 added shortcuts to modules  by:Jon
-- feat: refs #7911 added shortcut to modules  by:Jon
-- feat: refuncInvoiceForm component  by:jgallego
-- feat: remove duplicity  by:Javier Segarra
-- feat: remove future itemFixedPrices  by:Javier Segarra
-- feat: replace stickyButtons by subtoolbar  by:Javier Segarra
-- feat: required validation  by:Javier Segarra
-- feat: show bad dates  by:Javier Segarra
-- feat: showdate icons  by:Javier Segarra
-- feat: solve ItemFixedFilterPanel  by:Javier Segarra
-- feat: transfer an invoice  by:jgallego
-- feat: try to fix ItemFixedFilterPanel  by:Javier Segarra
-- feat: unnecessary changes  by:Javier Segarra
-- feat: update changelog  (origin/7896_down_devToTest_2436) by:Javier Segarra
-- feat: updates  by:Javier Segarra
-- feat: update version and changelog  by:Javier Segarra
-- feat: vnInput*  by:Javier Segarra
-- feat:  with VnTable  by:Javier Segarra
-- refs #6772 feat: fix approach  by:Javier Segarra
-- refs #6772 feat: refresh shelving.basic-data  by:Javier Segarra
-- style: show subName value  by:Javier Segarra
+-   chore: refs #6772 fix e2e (origin/6772-warmfix-fixE2e) by:jorgep
+-   chore: refs #7323 worker changes by:jorgep
+-   chore: refs #7353 fix warnings by:jorgep
+-   chore: refs #7353 use Vue component nomenclature by:jorgep
+-   chore: refs #7356 fix type by:jorgep
+-   feat(AccountConnections): use VnToken by:alexm
+-   feat: add key to routerView by:Javier Segarra
+-   feat: add plus shortcut in VnTable by:Javier Segarra
+-   feat: add row by:Javier Segarra
+-   feat: addRow withour dialog by:Javier Segarra
+-   feat: apply mixin by:Javier Segarra
+-   feat by:Javier Segarra
+-   feat: change navBar buttons by:Javier Segarra
+-   feat: dense rows by:Javier Segarra
+-   feat: fields with wrong name by:jgallego
+-   feat: fix bugs and filters by:Javier Segarra
+-   feat: fix refund parameters by:jgallego
+-   feat: handle create row by:Javier Segarra
+-   feat: handle dates by:Javier Segarra
+-   feat: handle qCheckbox 3rd state by:Javier Segarra
+-   feat: imrpove VnInputTime to set cursor at start by:Javier Segarra
+-   feat: keyShortcut directive by:Javier Segarra
+-   feat: minor fixes by:jgallego
+-   feat: only filter by isDestiny by:Javier Segarra
+-   feat: refs #211153 businessDataLinkGrafana by:robert
+-   feat: refs #7129 add km start and end on create form by:pablone
+-   feat: refs #7353 add filter & fix customTags by:jorgep
+-   feat: refs #7353 add locale by:jorgep
+-   feat: refs #7353 add no one opt by:jorgep
+-   feat: refs #7353 add right icons by:jorgep
+-   feat: refs #7353 imporve toDateFormat by:jorgep
+-   feat: refs #7353 salesPerson nickname & id by:jorgep
+-   feat: refs #7353 split sections by:jorgep
+-   feat: refs #7847 remove reload btn by:jorgep
+-   feat: refs #7847 remove reload fn by:jorgep
+-   feat: refs #7889 added shortcuts to modules by:Jon
+-   feat: refs #7911 added shortcut to modules by:Jon
+-   feat: refuncInvoiceForm component by:jgallego
+-   feat: remove duplicity by:Javier Segarra
+-   feat: remove future itemFixedPrices by:Javier Segarra
+-   feat: replace stickyButtons by subtoolbar by:Javier Segarra
+-   feat: required validation by:Javier Segarra
+-   feat: show bad dates by:Javier Segarra
+-   feat: showdate icons by:Javier Segarra
+-   feat: solve ItemFixedFilterPanel by:Javier Segarra
+-   feat: transfer an invoice by:jgallego
+-   feat: try to fix ItemFixedFilterPanel by:Javier Segarra
+-   feat: unnecessary changes by:Javier Segarra
+-   feat: update changelog (origin/7896_down_devToTest_2436) by:Javier Segarra
+-   feat: updates by:Javier Segarra
+-   feat: update version and changelog by:Javier Segarra
+-   feat: vnInput\* by:Javier Segarra
+-   feat: with VnTable by:Javier Segarra
+-   refs #6772 feat: fix approach by:Javier Segarra
+-   refs #6772 feat: refresh shelving.basic-data by:Javier Segarra
+-   style: show subName value by:Javier Segarra
 
 ### Changed 📦
 
-- perf: add v-shortcut in VnCard  by:Javier Segarra
-- perf: approach  by:Javier Segarra
-- perf: change directive location  by:Javier Segarra
-- perf: change slots order  by:Javier Segarra
-- perf: examples  by:Javier Segarra
-- perf: hide icon for VnInputDate  by:Javier Segarra
-- perf: improve ItemFixedPricefilterPanel  by:Javier Segarra
-- perf: improve  mainShrotcutMixin  by:Javier Segarra
-- perf: minor clean code  by:Javier Segarra
-- perf: onRowchange  by:Javier Segarra
-- perf: order by  by:Javier Segarra
-- perf: order components  by:Javier Segarra
-- perf: refs #7889 perf shortcut test  by:Jon
-- perf: remove console.log  by:Javier Segarra
-- perf: remove icons in header slot  by:Javier Segarra
-- perf: remove print variables  by:Javier Segarra
-- perf: restore CustomerBasicData  by:Javier Segarra
-- refactor: deleted useless prop  by:Jon
-- refactor: deleted useless prop in FetchedTags  by:Jon
-- refactor: refs #7323 drop useless code  by:jorgep
-- refactor: refs #7353 clients correction  by:jorgep
-- refactor: refs #7353 clients correction wip  by:jorgep
-- refactor: refs #7353 ease logic  by:jorgep
-- refactor: refs #7353 order correction  by:jorgep
-- refactor: refs #7353 simplify code  by:jorgep
-- refactor: refs #7353 tickets correction  by:jorgep
-- refactor: refs #7353 use global locales  by:jorgep
-- refactor: refs #7354 changed descriptor menu options  by:Jon
-- refactor: refs #7354 changed icon color in table and notification when deleting a zone  by:Jon
-- refactor: refs #7354 fix tableFilters  by:Jon
-- refactor: refs #7354 modified VnInputTime  by:Jon
-- refactor: refs #7354 refactor deliveryPanel  by:Jon
-- refactor: refs #7354 refactor zones section and fixed e2e tests  by:Jon
-- refactor: refs #7354 requested changes  by:Jon
-- refactor: refs #7354 reverse deliveryPanel changes  by:Jon
-- refactor: refs #7354 Zone migration changes  by:Jon
-- refactor: refs #7889 deleted subtitle attr and use keyBinding instead  by:Jon
-- refactor: refs #7889 modified shortcut and dashboard, and added tootlip in LeftMenu  by:Jon
-- refs #6722 perf: not fetch when id not exists  by:Javier Segarra
-- refs #6772 perf: change variable name  by:JAVIER SEGARRA MARTINEZ
-- refs #6772 perf: use ArrayData  (6772_reload_sections) by:Javier Segarra
-- refs #7283 refactor fix ItemDescriptor  by:carlossa
-- refs #7283 refactor ItexDescriptor  by:carlossa
+-   perf: add v-shortcut in VnCard by:Javier Segarra
+-   perf: approach by:Javier Segarra
+-   perf: change directive location by:Javier Segarra
+-   perf: change slots order by:Javier Segarra
+-   perf: examples by:Javier Segarra
+-   perf: hide icon for VnInputDate by:Javier Segarra
+-   perf: improve ItemFixedPricefilterPanel by:Javier Segarra
+-   perf: improve mainShrotcutMixin by:Javier Segarra
+-   perf: minor clean code by:Javier Segarra
+-   perf: onRowchange by:Javier Segarra
+-   perf: order by by:Javier Segarra
+-   perf: order components by:Javier Segarra
+-   perf: refs #7889 perf shortcut test by:Jon
+-   perf: remove console.log by:Javier Segarra
+-   perf: remove icons in header slot by:Javier Segarra
+-   perf: remove print variables by:Javier Segarra
+-   perf: restore CustomerBasicData by:Javier Segarra
+-   refactor: deleted useless prop by:Jon
+-   refactor: deleted useless prop in FetchedTags by:Jon
+-   refactor: refs #7323 drop useless code by:jorgep
+-   refactor: refs #7353 clients correction by:jorgep
+-   refactor: refs #7353 clients correction wip by:jorgep
+-   refactor: refs #7353 ease logic by:jorgep
+-   refactor: refs #7353 order correction by:jorgep
+-   refactor: refs #7353 simplify code by:jorgep
+-   refactor: refs #7353 tickets correction by:jorgep
+-   refactor: refs #7353 use global locales by:jorgep
+-   refactor: refs #7354 changed descriptor menu options by:Jon
+-   refactor: refs #7354 changed icon color in table and notification when deleting a zone by:Jon
+-   refactor: refs #7354 fix tableFilters by:Jon
+-   refactor: refs #7354 modified VnInputTime by:Jon
+-   refactor: refs #7354 refactor deliveryPanel by:Jon
+-   refactor: refs #7354 refactor zones section and fixed e2e tests by:Jon
+-   refactor: refs #7354 requested changes by:Jon
+-   refactor: refs #7354 reverse deliveryPanel changes by:Jon
+-   refactor: refs #7354 Zone migration changes by:Jon
+-   refactor: refs #7889 deleted subtitle attr and use keyBinding instead by:Jon
+-   refactor: refs #7889 modified shortcut and dashboard, and added tootlip in LeftMenu by:Jon
+-   refs #6722 perf: not fetch when id not exists by:Javier Segarra
+-   refs #6772 perf: change variable name by:JAVIER SEGARRA MARTINEZ
+-   refs #6772 perf: use ArrayData (6772_reload_sections) by:Javier Segarra
+-   refs #7283 refactor fix ItemDescriptor by:carlossa
+-   refs #7283 refactor ItexDescriptor by:carlossa
 
 ### Fixed 🛠️
 
-- chore: refs #6772 fix e2e  (origin/6772-warmfix-fixE2e) by:jorgep
-- chore: refs #7353 fix warnings  by:jorgep
-- chore: refs #7356 fix type  by:jorgep
-- feat: fix bugs and filters  by:Javier Segarra
-- feat: fix refund parameters  by:jgallego
-- feat: minor fixes  by:jgallego
-- feat: refs #7353 add filter & fix customTags  by:jorgep
-- feat: try to fix ItemFixedFilterPanel  by:Javier Segarra
-- fix: add border-top  by:Javier Segarra
-- fix: added missing descriptors and small details  by:Jon
-- fix branch  by:carlossa
-- fix: call upsert when crudModel haschanges  by:Javier Segarra
-- fix(ClaimList): fix summary  by:alexm
-- fix: cli warnings  by:Javier Segarra
-- fix: editTableOptions  by:Javier Segarra
-- fix events and descriptor menu  by:Jon
-- fix: InvoiceIn sections  (origin/6772_reload_sections) by:Javier Segarra
-- fix: minor changes  by:Javier Segarra
-- fix: minor error whit dates  by:Javier Segarra
-- fix: module icon  by:Javier Segarra
-- fix: options QDate  by:Javier Segarra
-- fix: refs #6900 e2e error  by:jorgep
-- fix: refs #6900 rollback  by:jorgep
-- fix: refs #7353 css  by:jorgep
-- fix: refs #7353 hide search param  (origin/7353-warmfix-fixSearchbar) by:jorgep
-- fix: refs #7353 iron out filter  by:jorgep
-- fix: refs #7353 iron out ticket table  by:jorgep
-- fix: refs #7353 padding  by:jorgep
-- fix: refs #7353 salesClientTable  by:jorgep
-- fix: refs #7353 salesorderTable  by:jorgep
-- fix: refs #7353 saleTicketMonitors  by:jorgep
-- fix: refs #7353 use same datakey  by:jorgep
-- fix: refs #7353 vnTable colors  by:jorgep
-- fix: refs #7354 e2e tests  by:Jon
-- fix: refs #7354 fix delivery days  by:Jon
-- fix: refs #7354 fix list searchbar and filters  by:Jon
-- fix: refs #7354 fix VnSearchbar search for zone section & finished basic tests  by:Jon
-- fix: refs #7354 fix VnTable filters and agency field  by:Jon
-- fix: refs #7354 fix zoneSearchbar  by:Jon
-- fix: refs #7354 requested changes  by:Jon
-- fix: refs #7356 colors  by:jorgep
-- fix: refs #7356 create claim dialog  by:jorgep
-- fix: refs #7889 fixed shortcut test  by:Jon
-- fix: refs #7903 fixed ticket's search bar and keybinding tooltip  by:Jon
-- fix: refs #7911 fixed shortcut and related files  by:Jon
-- fix: remove condition duplicated  by:Javier Segarra
-- fix: remove property  by:Javier Segarra
-- fix tootltip  by:carlossa
-- fix traduction  by:carlossa
-- fix(VnSectionMain): add QPage  by:alexm
-- fix(zone): zoneLocation and the others searchbar  by:alexm
-- refactor: refs #7354 fix tableFilters  by:Jon
-- refactor: refs #7354 refactor zones section and fixed e2e tests  by:Jon
-- refs #6772 feat: fix approach  by:Javier Segarra
-- refs #6772 fix: claimPhoto reload  by:Javier Segarra
-- refs #6896 fix searchbar  by:carlossa
-- refs #6897 fix entry  by:carlossa
-- refs #6899 fix invoiceFix  by:carlossa
-- refs #6899 fix order  by:carlossa
-- refs #7283 fix  by:carlossa
-- refs #7283 fix ItemDescriptor warehouse  by:carlossa
-- refs #7283 refactor fix ItemDescriptor  by:carlossa
-- refs #7355 #7366 fix account, summary, list, travelList, tooltip  by:carlossa
-- refs #7355 fix accountPrivileges  by:carlossa
-- refs #7355 fix accounts, vnTable  by:carlossa
-- refs #7355 fix privileges  by:carlossa
-- refs #7355 fix roles filters  by:carlossa
-- refs #7355 fix total  by:carlossa
-- refs #7355 fix views summarys, entryList, travelList refact  by:carlossa
-- refs #7366  fix travel hours  by:carlossa
-- test: fix e2e  by:Javier Segarra
+-   chore: refs #6772 fix e2e (origin/6772-warmfix-fixE2e) by:jorgep
+-   chore: refs #7353 fix warnings by:jorgep
+-   chore: refs #7356 fix type by:jorgep
+-   feat: fix bugs and filters by:Javier Segarra
+-   feat: fix refund parameters by:jgallego
+-   feat: minor fixes by:jgallego
+-   feat: refs #7353 add filter & fix customTags by:jorgep
+-   feat: try to fix ItemFixedFilterPanel by:Javier Segarra
+-   fix: add border-top by:Javier Segarra
+-   fix: added missing descriptors and small details by:Jon
+-   fix branch by:carlossa
+-   fix: call upsert when crudModel haschanges by:Javier Segarra
+-   fix(ClaimList): fix summary by:alexm
+-   fix: cli warnings by:Javier Segarra
+-   fix: editTableOptions by:Javier Segarra
+-   fix events and descriptor menu by:Jon
+-   fix: InvoiceIn sections (origin/6772_reload_sections) by:Javier Segarra
+-   fix: minor changes by:Javier Segarra
+-   fix: minor error whit dates by:Javier Segarra
+-   fix: module icon by:Javier Segarra
+-   fix: options QDate by:Javier Segarra
+-   fix: refs #6900 e2e error by:jorgep
+-   fix: refs #6900 rollback by:jorgep
+-   fix: refs #7353 css by:jorgep
+-   fix: refs #7353 hide search param (origin/7353-warmfix-fixSearchbar) by:jorgep
+-   fix: refs #7353 iron out filter by:jorgep
+-   fix: refs #7353 iron out ticket table by:jorgep
+-   fix: refs #7353 padding by:jorgep
+-   fix: refs #7353 salesClientTable by:jorgep
+-   fix: refs #7353 salesorderTable by:jorgep
+-   fix: refs #7353 saleTicketMonitors by:jorgep
+-   fix: refs #7353 use same datakey by:jorgep
+-   fix: refs #7353 vnTable colors by:jorgep
+-   fix: refs #7354 e2e tests by:Jon
+-   fix: refs #7354 fix delivery days by:Jon
+-   fix: refs #7354 fix list searchbar and filters by:Jon
+-   fix: refs #7354 fix VnSearchbar search for zone section & finished basic tests by:Jon
+-   fix: refs #7354 fix VnTable filters and agency field by:Jon
+-   fix: refs #7354 fix zoneSearchbar by:Jon
+-   fix: refs #7354 requested changes by:Jon
+-   fix: refs #7356 colors by:jorgep
+-   fix: refs #7356 create claim dialog by:jorgep
+-   fix: refs #7889 fixed shortcut test by:Jon
+-   fix: refs #7903 fixed ticket's search bar and keybinding tooltip by:Jon
+-   fix: refs #7911 fixed shortcut and related files by:Jon
+-   fix: remove condition duplicated by:Javier Segarra
+-   fix: remove property by:Javier Segarra
+-   fix tootltip by:carlossa
+-   fix traduction by:carlossa
+-   fix(VnSectionMain): add QPage by:alexm
+-   fix(zone): zoneLocation and the others searchbar by:alexm
+-   refactor: refs #7354 fix tableFilters by:Jon
+-   refactor: refs #7354 refactor zones section and fixed e2e tests by:Jon
+-   refs #6772 feat: fix approach by:Javier Segarra
+-   refs #6772 fix: claimPhoto reload by:Javier Segarra
+-   refs #6896 fix searchbar by:carlossa
+-   refs #6897 fix entry by:carlossa
+-   refs #6899 fix invoiceFix by:carlossa
+-   refs #6899 fix order by:carlossa
+-   refs #7283 fix by:carlossa
+-   refs #7283 fix ItemDescriptor warehouse by:carlossa
+-   refs #7283 refactor fix ItemDescriptor by:carlossa
+-   refs #7355 #7366 fix account, summary, list, travelList, tooltip by:carlossa
+-   refs #7355 fix accountPrivileges by:carlossa
+-   refs #7355 fix accounts, vnTable by:carlossa
+-   refs #7355 fix privileges by:carlossa
+-   refs #7355 fix roles filters by:carlossa
+-   refs #7355 fix total by:carlossa
+-   refs #7355 fix views summarys, entryList, travelList refact by:carlossa
+-   refs #7366 fix travel hours by:carlossa
+-   test: fix e2e by:Javier Segarra
 
 # Version 24.36 - 2024-08-27
 
diff --git a/package.json b/package.json
index f25e570a3..6a7c1ed55 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
     "name": "salix-front",
-    "version": "24.36.0",
+    "version": "24.38.0",
     "description": "Salix frontend",
     "productName": "Salix",
     "author": "Verdnatura",

From 37b6a032888abba2695fa6549d327b41bf93dec1 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Tue, 17 Sep 2024 09:25:53 +0200
Subject: [PATCH 65/69] fix(itemDescriptor): fix redirection to itemDiary

---
 src/pages/Item/Card/ItemDescriptor.vue | 17 ++++-------------
 1 file changed, 4 insertions(+), 13 deletions(-)

diff --git a/src/pages/Item/Card/ItemDescriptor.vue b/src/pages/Item/Card/ItemDescriptor.vue
index baac0c608..da6859d30 100644
--- a/src/pages/Item/Card/ItemDescriptor.vue
+++ b/src/pages/Item/Card/ItemDescriptor.vue
@@ -11,7 +11,6 @@ import VnConfirm from 'components/ui/VnConfirm.vue';
 import RegularizeStockForm from 'components/RegularizeStockForm.vue';
 import ItemDescriptorImage from 'src/pages/Item/Card/ItemDescriptorImage.vue';
 import useCardDescription from 'src/composables/useCardDescription';
-import { getUrl } from 'src/composables/getUrl';
 import axios from 'axios';
 import { dashIfEmpty } from 'src/filters';
 
@@ -51,10 +50,8 @@ const entityId = computed(() => {
 const regularizeStockFormDialog = ref(null);
 const available = ref(null);
 const visible = ref(null);
-const salixUrl = ref();
 
 onMounted(async () => {
-    salixUrl.value = await getUrl('');
     await getItemConfigs();
     await updateStock();
 });
@@ -200,16 +197,10 @@ const openCloneDialog = async () => {
         <template #actions="{}">
             <QCardActions class="row justify-center">
                 <QBtn
-                    :href="
-                        salixUrl +
-                        'item/' +
-                        entityId +
-                        '/diary?' +
-                        'warehouseFk=' +
-                        warehouseFk +
-                        '&lineFk=' +
-                        $props.saleFk
-                    "
+                    :to="{
+                        name: 'ItemDiary',
+                        query: { warehouseFk, lineFk: $props.saleFk },
+                    }"
                     size="md"
                     icon="vn:transaction"
                     color="primary"

From 3d1bb0c67a035e770f6aa02cf84e51307d9a9c8b Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Tue, 17 Sep 2024 10:20:44 +0200
Subject: [PATCH 66/69] fix(itemDescriptor): fix redirection to itemDiary

---
 src/components/common/VnCard.vue       | 10 +++++++---
 src/pages/Item/Card/ItemCard.vue       |  2 +-
 src/pages/Item/Card/ItemDescriptor.vue |  1 +
 src/pages/Login/ResetPassword.vue      |  1 -
 4 files changed, 9 insertions(+), 5 deletions(-)

diff --git a/src/components/common/VnCard.vue b/src/components/common/VnCard.vue
index 7d29da232..0d80f43ce 100644
--- a/src/components/common/VnCard.vue
+++ b/src/components/common/VnCard.vue
@@ -24,7 +24,9 @@ const stateStore = useStateStore();
 const route = useRoute();
 const router = useRouter();
 const url = computed(() => {
-    if (props.baseUrl) return `${props.baseUrl}/${route.params.id}`;
+    if (props.baseUrl) {
+        return `${props.baseUrl}/${route.params.id}`;
+    }
     return props.customUrl;
 });
 const searchRightDataKey = computed(() => {
@@ -40,8 +42,10 @@ onBeforeMount(async () => {
     try {
         if (!props.baseUrl) arrayData.store.filter.where = { id: route.params.id };
         await arrayData.fetch({ append: false, updateRouter: false });
-    } catch (e) {
-        router.push({ name: 'WorkerList' });
+    } catch {
+        const { matched: matches } = router.currentRoute.value;
+        const { path } = matches.at(-1);
+        router.push({ path: path.replace(/:id.*/, '') });
     }
 });
 
diff --git a/src/pages/Item/Card/ItemCard.vue b/src/pages/Item/Card/ItemCard.vue
index 1162327c1..2412f2bf9 100644
--- a/src/pages/Item/Card/ItemCard.vue
+++ b/src/pages/Item/Card/ItemCard.vue
@@ -12,7 +12,7 @@ import ItemListFilter from '../ItemListFilter.vue';
         search-data-key="ItemList"
         :searchbar-props="{
             url: 'Items/filter',
-            label: 'searchbar.labelr',
+            label: 'searchbar.label',
             info: 'searchbar.info',
         }"
     />
diff --git a/src/pages/Item/Card/ItemDescriptor.vue b/src/pages/Item/Card/ItemDescriptor.vue
index da6859d30..ef844824f 100644
--- a/src/pages/Item/Card/ItemDescriptor.vue
+++ b/src/pages/Item/Card/ItemDescriptor.vue
@@ -199,6 +199,7 @@ const openCloneDialog = async () => {
                 <QBtn
                     :to="{
                         name: 'ItemDiary',
+                        params: { id: entityId },
                         query: { warehouseFk, lineFk: $props.saleFk },
                     }"
                     size="md"
diff --git a/src/pages/Login/ResetPassword.vue b/src/pages/Login/ResetPassword.vue
index eff718e97..2751f1ceb 100644
--- a/src/pages/Login/ResetPassword.vue
+++ b/src/pages/Login/ResetPassword.vue
@@ -33,7 +33,6 @@ async function onSubmit() {
     };
 
     try {
-        console.log('newPassword: ', newPassword);
         await axios.post(
             'VnUsers/reset-password',
             { newPassword: newPassword.value },

From 044156356c559eb8660d851bb641f57a4909dd68 Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Tue, 17 Sep 2024 10:37:22 +0200
Subject: [PATCH 67/69] hotfix searchbar

---
 src/pages/InvoiceOut/InvoiceOutList.vue | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/pages/InvoiceOut/InvoiceOutList.vue b/src/pages/InvoiceOut/InvoiceOutList.vue
index 915b58b15..5157d957b 100644
--- a/src/pages/InvoiceOut/InvoiceOutList.vue
+++ b/src/pages/InvoiceOut/InvoiceOutList.vue
@@ -177,7 +177,7 @@ watchEffect(selectedRows);
     <VnSearchbar
         :info="t('youCanSearchByInvoiceReference')"
         :label="t('searchInvoice')"
-        data-key="InvoiceOutList"
+        data-key="invoiceOut"
     />
     <VnSubToolbar>
         <template #st-actions>

From e559ab43d7bbbfb19ad5917255ed271290052cac Mon Sep 17 00:00:00 2001
From: pablone <pablone@verdnatura.es>
Date: Tue, 17 Sep 2024 13:40:27 +0200
Subject: [PATCH 68/69] fix: refs #7404 remove some style

---
 src/components/VnTable/VnTable.vue         | 2 +-
 src/pages/Entry/EntryStockBought.vue       | 4 ++--
 src/pages/Entry/EntryStockBoughtDetail.vue | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index b7321fe16..648b1fb3b 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -430,7 +430,7 @@ function handleOnDataSaved(_) {
                     />
                 </template>
                 <template #header-cell="{ col }">
-                    <QTh table-header-style="max-width:50%" v-if="col.visible ?? true">
+                    <QTh v-if="col.visible ?? true">
                         <div
                             class="column self-start q-ml-xs ellipsis"
                             :class="`text-${col?.align ?? 'left'}`"
diff --git a/src/pages/Entry/EntryStockBought.vue b/src/pages/Entry/EntryStockBought.vue
index c8f147b1f..5b4d43e06 100644
--- a/src/pages/Entry/EntryStockBought.vue
+++ b/src/pages/Entry/EntryStockBought.vue
@@ -219,8 +219,8 @@ function openDialog() {
                 <span class="link" @click.stop>
                     {{ row?.worker?.user?.name }}
                     <WorkerDescriptorProxy :id="row?.workerFk" />
-                </span> </template
-            >0
+                </span>
+            </template>
         </VnTable>
     </QPage>
 </template>
diff --git a/src/pages/Entry/EntryStockBoughtDetail.vue b/src/pages/Entry/EntryStockBoughtDetail.vue
index 6d9227ad2..744b9d3fe 100644
--- a/src/pages/Entry/EntryStockBoughtDetail.vue
+++ b/src/pages/Entry/EntryStockBoughtDetail.vue
@@ -63,7 +63,7 @@ const columns = [
 ];
 </script>
 <template>
-    <QDialog position="bottom" :maximized="true">
+    <QDialog :maximized="true">
         <div class="container">
             <VnTable
                 ref="tableRef"

From 4f662375cd34e6e08bfe376b409a6d636611f65b Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Tue, 17 Sep 2024 15:04:58 +0200
Subject: [PATCH 69/69] fix(VnTable): sanitizer value is defined

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

diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index 648b1fb3b..55028080c 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -200,7 +200,7 @@ function setUserParams(watchedParams, watchedOrder) {
 
 function sanitizer(params) {
     for (const [key, value] of Object.entries(params)) {
-        if (typeof value == 'object') {
+        if (value && typeof value == 'object') {
             const param = Object.values(value)[0];
             if (typeof param == 'string') params[key] = param.replaceAll('%', '');
         }