From 168201c755f50cefb633598e5488a88bf48068d9 Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Mon, 15 Jul 2024 12:25:54 +0200
Subject: [PATCH 001/150] refs #7283 itemList table

---
 src/pages/Item/ItemList.vue | 477 +++++-------------------------------
 1 file changed, 68 insertions(+), 409 deletions(-)

diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index f1e3629cd..334ef2604 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -3,114 +3,48 @@ import { onMounted, ref, computed, reactive, onUnmounted } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useRouter } from 'vue-router';
 
-import FetchData from 'components/FetchData.vue';
-import FetchedTags from 'components/ui/FetchedTags.vue';
-import TableVisibleColumns from 'src/components/common/TableVisibleColumns.vue';
-import VnInput from 'src/components/common/VnInput.vue';
-import VnSelect from 'src/components/common/VnSelect.vue';
-import ItemDescriptorProxy from '../Item/Card/ItemDescriptorProxy.vue';
-import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
-import ItemSummary from '../Item/Card/ItemSummary.vue';
-import VnPaginate from 'components/ui/VnPaginate.vue';
-import ItemListFilter from './ItemListFilter.vue';
-
+import VnTable from 'components/VnTable/VnTable.vue';
 import { useStateStore } from 'stores/useStateStore';
 import { toDateFormat } from 'src/filters/date.js';
 import { dashIfEmpty } from 'src/filters';
-import { useSummaryDialog } from 'src/composables/useSummaryDialog';
-import { useVnConfirm } from 'composables/useVnConfirm';
 import axios from 'axios';
-import RightMenu from 'src/components/common/RightMenu.vue';
 import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
-import VnImg from 'src/components/ui/VnImg.vue';
 
 const router = useRouter();
 const stateStore = useStateStore();
 const { t } = useI18n();
-const { viewSummary } = useSummaryDialog();
-const { openConfirmationModal } = useVnConfirm();
+const tableRef = ref();
 
-const paginateRef = ref(null);
-const itemTypesOptions = ref([]);
-const originsOptions = ref([]);
-const buyersOptions = ref([]);
-const intrastatOptions = ref([]);
-const itemCategoriesOptions = ref([]);
-const visibleColumns = ref([]);
-const allColumnNames = ref([]);
-
-const exprBuilder = (param, value) => {
-    switch (param) {
-        case 'category':
-            return { 'ic.name': value };
-        case 'buyerFk':
-            return { 'it.workerFk': value };
-        case 'grouping':
-            return { 'b.grouping': value };
-        case 'packing':
-            return { 'b.packing': value };
-        case 'origin':
-            return { 'ori.code': value };
-        case 'typeFk':
-            return { 'i.typeFk': value };
-        case 'intrastat':
-            return { 'intr.description': value };
-        case 'name':
-            return { 'i.name': { like: `%${value}%` } };
-        case 'producer':
-            return { 'pr.name': { like: `%${value}%` } };
-        case 'id':
-        case 'size':
-        case 'subname':
-        case 'isActive':
-        case 'weightByPiece':
-        case 'stemMultiplier':
-        case 'stems':
-            return { [`i.${param}`]: value };
-    }
+const itemFilter = {
+    include: [
+        {
+            relation: 'trainingCourseType',
+            scope: {
+                fields: ['id', 'name'],
+            },
+        },
+        {
+            relation: 'trainingCenter',
+            scope: {
+                fields: ['id', 'name'],
+            },
+        },
+    ],
 };
-
-const params = reactive({ isFloramondo: false, isActive: true });
-
-const applyColumnFilter = async (col) => {
-    try {
-        const paramKey = col.columnFilter?.filterParamKey || col.field;
-        params[paramKey] = col.columnFilter.filterValue;
-        await paginateRef.value.addFilter(null, params);
-    } catch (err) {
-        console.error('Error applying column filter', err);
-    }
-};
-
-const getInputEvents = (col) => {
-    return col.columnFilter.type === 'select'
-        ? { 'update:modelValue': () => applyColumnFilter(col) }
-        : {
-              'keyup.enter': () => applyColumnFilter(col),
-          };
-};
-
 const columns = computed(() => [
     {
         label: '',
         name: 'picture',
         align: 'left',
-        columnFilter: null,
     },
     {
         label: t('item.list.id'),
         name: 'id',
         field: 'id',
         align: 'left',
-        sortable: true,
-        columnFilter: {
-            component: VnInput,
-            type: 'text',
-            filterValue: null,
-            event: getInputEvents,
-            attrs: {
-                dense: true,
-            },
+        isId: true,
+        chip: {
+            condition: () => true,
         },
     },
     {
@@ -118,101 +52,41 @@ const columns = computed(() => [
         field: 'grouping',
         name: 'grouping',
         align: 'left',
-        sortable: true,
-        columnFilter: {
-            component: VnInput,
-            type: 'text',
-            filterValue: null,
-            event: getInputEvents,
-            attrs: {
-                dense: true,
-            },
-        },
-        format: (val) => dashIfEmpty(val),
     },
     {
         label: t('item.list.packing'),
         field: 'packing',
         name: 'packing',
         align: 'left',
-        sortable: true,
-        columnFilter: {
-            component: VnInput,
-            type: 'text',
-            filterValue: null,
-            event: getInputEvents,
-            attrs: {
-                dense: true,
-            },
-        },
-        format: (val) => dashIfEmpty(val),
     },
     {
         label: t('globals.description'),
         field: 'name',
         name: 'description',
         align: 'left',
-        sortable: true,
-        columnFilter: {
-            component: VnInput,
-            type: 'text',
-            filterValue: null,
-            event: getInputEvents,
-            attrs: {
-                dense: true,
-            },
-        },
+        create: true,
     },
     {
         label: t('item.list.stems'),
         field: 'stems',
         name: 'stems',
         align: 'left',
-        sortable: true,
-        columnFilter: {
-            component: VnInput,
-            type: 'text',
-            filterValue: null,
-            event: getInputEvents,
-            attrs: {
-                dense: true,
-            },
-        },
     },
     {
         label: t('item.list.size'),
         field: 'size',
         name: 'size',
         align: 'left',
-        sortable: true,
-        columnFilter: {
-            component: VnInput,
-            type: 'text',
-            filterValue: null,
-            event: getInputEvents,
-            attrs: {
-                dense: true,
-            },
-        },
     },
     {
         label: t('item.list.typeName'),
         field: 'typeName',
         name: 'typeName',
         align: 'left',
-        sortable: true,
-        columnFilter: {
-            component: VnSelect,
-            filterParamKey: 'typeFk',
-            type: 'select',
-            filterValue: null,
-            event: getInputEvents,
-            attrs: {
-                options: itemTypesOptions.value,
-                'option-value': 'id',
-                'option-label': 'name',
-                dense: true,
-            },
+        component: 'select',
+        attrs: {
+            url: 'ItemType',
+            fields: ['id', 'name'],
         },
     },
 
@@ -221,18 +95,10 @@ const columns = computed(() => [
         field: 'category',
         name: 'category',
         align: 'left',
-        sortable: true,
-        columnFilter: {
-            component: VnSelect,
-            type: 'select',
-            filterValue: null,
-            event: getInputEvents,
-            attrs: {
-                options: itemCategoriesOptions.value,
-                'option-value': 'name',
-                'option-label': 'name',
-                dense: true,
-            },
+        component: 'select',
+        attrs: {
+            url: 'ItemCategory',
+            fields: ['id', 'name'],
         },
     },
 
@@ -241,18 +107,10 @@ const columns = computed(() => [
         field: 'intrastat',
         name: 'intrastat',
         align: 'left',
-        sortable: true,
-        columnFilter: {
-            component: VnSelect,
-            type: 'select',
-            filterValue: null,
-            event: getInputEvents,
-            attrs: {
-                options: intrastatOptions.value,
-                'option-value': 'description',
-                'option-label': 'description',
-                dense: true,
-            },
+        component: 'select',
+        attrs: {
+            url: 'Intrastat',
+            fields: ['id', 'description'],
         },
     },
     {
@@ -260,18 +118,10 @@ const columns = computed(() => [
         field: 'origin',
         name: 'origin',
         align: 'left',
-        sortable: true,
-        columnFilter: {
-            component: VnSelect,
-            type: 'select',
-            filterValue: null,
-            event: getInputEvents,
-            attrs: {
-                options: originsOptions.value,
-                'option-value': 'code',
-                'option-label': 'code',
-                dense: true,
-            },
+        component: 'select',
+        attrs: {
+            url: 'Origin',
+            fields: ['id', 'name'],
         },
     },
     {
@@ -279,36 +129,13 @@ const columns = computed(() => [
         field: 'userName',
         name: 'userName',
         align: 'left',
-        sortable: true,
-        columnFilter: {
-            component: VnSelect,
-            filterParamKey: 'buyerFk',
-            type: 'select',
-            filterValue: null,
-            event: getInputEvents,
-            attrs: {
-                options: buyersOptions.value,
-                'option-value': 'id',
-                'option-label': 'nickname',
-                dense: true,
-            },
-        },
     },
     {
         label: t('item.list.weightByPiece'),
         field: 'weightByPiece',
         name: 'weightByPiece',
         align: 'left',
-        sortable: true,
-        columnFilter: {
-            component: VnInput,
-            type: 'text',
-            filterValue: null,
-            event: getInputEvents,
-            attrs: {
-                dense: true,
-            },
-        },
+        component: 'input',
         format: (val) => dashIfEmpty(val),
     },
     {
@@ -316,16 +143,7 @@ const columns = computed(() => [
         field: 'stemMultiplier',
         name: 'stemMultiplier',
         align: 'left',
-        sortable: true,
-        columnFilter: {
-            component: VnInput,
-            type: 'text',
-            filterValue: null,
-            event: getInputEvents,
-            attrs: {
-                dense: true,
-            },
-        },
+        component: 'input',
         format: (val) => dashIfEmpty(val),
     },
     {
@@ -333,40 +151,26 @@ const columns = computed(() => [
         field: 'isActive',
         name: 'isActive',
         align: 'left',
-        sortable: true,
-        columnFilter: null,
+        component: 'checkbox',
     },
     {
         label: t('item.list.producer'),
         field: 'producer',
         name: 'producer',
         align: 'left',
-        sortable: true,
-        columnFilter: {
-            component: VnInput,
-            type: 'text',
-            filterValue: null,
-            event: getInputEvents,
-            attrs: {
-                dense: true,
-            },
+        component: 'select',
+        attrs: {
+            url: 'Producer',
+            fields: ['id', 'name'],
         },
-        format: (val) => dashIfEmpty(val),
     },
     {
         label: t('item.list.landed'),
         field: 'landed',
         name: 'landed',
         align: 'left',
-        sortable: true,
+        component: 'date',
         format: (val) => dashIfEmpty(toDateFormat(val)),
-        columnFilter: null,
-    },
-    {
-        label: '',
-        name: 'actions',
-        align: 'left',
-        columnFilter: null,
     },
 ]);
 
@@ -388,49 +192,11 @@ const cloneItem = async (itemFk) => {
     }
 };
 
-onMounted(async () => {
-    stateStore.rightDrawer = true;
-    const filteredColumns = columns.value.filter(
-        (col) => col.name !== 'picture' && col.name !== 'actions'
-    );
-    allColumnNames.value = filteredColumns.map((col) => col.name);
-});
-
 onUnmounted(() => (stateStore.rightDrawer = false));
 </script>
 
 <template>
-    <FetchData
-        url="ItemTypes"
-        :filter="{ fields: ['id', 'name'], order: 'name ASC' }"
-        auto-load
-        @on-fetch="(data) => (itemTypesOptions = data)"
-    />
-    <FetchData
-        url="ItemCategories"
-        :filter="{ fields: ['name'], order: 'name ASC' }"
-        auto-load
-        @on-fetch="(data) => (itemCategoriesOptions = data)"
-    />
-    <FetchData
-        url="Intrastats"
-        :filter="{ fields: ['description'], order: 'description ASC' }"
-        auto-load
-        @on-fetch="(data) => (intrastatOptions = data)"
-    />
-    <FetchData
-        url="Origins"
-        :filter="{ fields: ['code'], order: 'code ASC' }"
-        auto-load
-        @on-fetch="(data) => (originsOptions = data)"
-    />
-    <FetchData
-        url="TicketRequests/getItemTypeWorker"
-        :filter="{ fields: ['id', 'nickname'], order: 'nickname ASC' }"
-        auto-load
-        @on-fetch="(data) => (buyersOptions = data)"
-    />
-    <VnSubToolbar>
+    <!-- <VnSubToolbar>
         <template #st-data>
             <TableVisibleColumns
                 :all-columns="allColumnNames"
@@ -439,135 +205,28 @@ onUnmounted(() => (stateStore.rightDrawer = false));
                 @on-config-saved="visibleColumns = ['picture', ...$event, 'actions']"
             />
         </template>
-    </VnSubToolbar>
-    <RightMenu>
-        <template #right-panel>
-            <ItemListFilter data-key="ItemList" />
-        </template>
-    </RightMenu>
-    <QPage class="column items-center q-pa-md">
-        <VnPaginate
-            ref="paginateRef"
-            data-key="ItemList"
-            url="Items/filter"
-            :order="['isActive DESC', 'name', 'id']"
-            :limit="12"
-            :expr-builder="exprBuilder"
-            :user-params="params"
-            :keep-opts="['userParams']"
-            :offset="50"
-            auto-load
-        >
-            <template #body="{ rows }">
-                <QTable
-                    :rows="rows"
-                    :columns="columns"
-                    row-key="id"
-                    :pagination="{ rowsPerPage: 0 }"
-                    class="full-width q-mt-md"
-                    :visible-columns="visibleColumns"
-                    :no-data-label="t('globals.noResults')"
-                    @row-click="(_, row) => redirectToItemSummary(row.id)"
-                >
-                    <template #top-row="{ cols }">
-                        <QTr>
-                            <QTd
-                                v-for="(col, index) in cols"
-                                :key="index"
-                                style="max-width: 100px"
-                            >
-                                <component
-                                    :is="col.columnFilter.component"
-                                    v-if="col.columnFilter"
-                                    v-model="col.columnFilter.filterValue"
-                                    v-bind="col.columnFilter.attrs"
-                                    v-on="col.columnFilter.event(col)"
-                                    dense
-                                />
-                            </QTd>
-                        </QTr>
-                    </template>
-                    <template #body-cell-picture="{ row }">
-                        <QTd>
-                            <VnImg
-                                size="50x50"
-                                :id="row.id"
-                                height="50px"
-                                width="50px"
-                                class="image"
-                            />
-                        </QTd>
-                    </template>
-                    <template #body-cell-id="{ row }">
-                        <QTd @click.stop>
-                            <QBtn flat color="primary">
-                                {{ row.id }}
-                            </QBtn>
-                            <ItemDescriptorProxy :id="row.id" />
-                        </QTd>
-                    </template>
-                    <template #body-cell-userName="{ row }">
-                        <QTd @click.stop>
-                            <QBtn flat color="primary" dense>
-                                {{ row.userName }}
-                            </QBtn>
-                            <WorkerDescriptorProxy :id="row.buyerFk" />
-                        </QTd>
-                    </template>
-                    <template #body-cell-description="{ row }">
-                        <QTd class="col">
-                            <span>{{ row.name }} {{ row.subName }}</span>
-                            <FetchedTags :item="row" :max-length="6" />
-                        </QTd>
-                    </template>
-                    <template #body-cell-isActive="{ row }">
-                        <QTd>
-                            <QCheckbox :model-value="!!row.isActive" disable />
-                        </QTd>
-                    </template>
-                    <template #body-cell-actions="{ row }">
-                        <QTd>
-                            <QIcon
-                                @click.stop="
-                                    openConfirmationModal(
-                                        t(`All it's properties will be copied`),
-                                        t('Do you want to clone this item?'),
-                                        () => cloneItem(row.id)
-                                    )
-                                "
-                                class="q-ml-sm"
-                                color="primary"
-                                name="vn:clone"
-                                size="sm"
-                            >
-                                <QTooltip>
-                                    {{ t('globals.clone') }}
-                                </QTooltip>
-                            </QIcon>
-                            <QIcon
-                                @click.stop="viewSummary(row.id, ItemSummary)"
-                                class="q-ml-md"
-                                color="primary"
-                                name="preview"
-                                size="sm"
-                            >
-                                <QTooltip class="text-no-wrap">
-                                    {{ t('Preview') }}
-                                </QTooltip>
-                            </QIcon>
-                        </QTd>
-                    </template>
-                </QTable>
-            </template>
-        </VnPaginate>
-
-        <QPageSticky :offset="[20, 20]">
-            <QBtn @click="redirectToItemCreate()" color="primary" fab icon="add" />
-            <QTooltip class="text-no-wrap">
-                {{ t('New item') }}
-            </QTooltip>
-        </QPageSticky>
-    </QPage>
+    </VnSubToolbar> -->
+    <VnTable
+        ref="tableRef"
+        data-key="ItemList"
+        url="Items"
+        url-create="Items"
+        save-url="Items/crud"
+        :create="{
+            urlCreate: 'Items',
+            title: 'Create Item',
+            onDataSaved: () => tableRef.redirect(),
+            formInitialData: {
+                editorFk: entityId,
+            },
+        }"
+        order="id ASC"
+        :columns="columns"
+        auto-load
+        :right-search="false"
+        :is-editable="false"
+        :use-model="true"
+    />
 </template>
 
 <i18n>

From 16b5b5d9a1111f31a8c07f53272a8f6f77ae71ba Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Mon, 15 Jul 2024 13:48:59 +0200
Subject: [PATCH 002/150] refs #7283 filter

---
 src/pages/Item/ItemList.vue | 64 +++++++++++++++++++++++++++++++------
 1 file changed, 55 insertions(+), 9 deletions(-)

diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index 334ef2604..4c49f068f 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -1,30 +1,39 @@
 <script setup>
 import { onMounted, ref, computed, reactive, onUnmounted } from 'vue';
 import { useI18n } from 'vue-i18n';
-import { useRouter } from 'vue-router';
+import { useRouter, useRoute } from 'vue-router';
+import VnImg from 'src/components/ui/VnImg.vue';
 
 import VnTable from 'components/VnTable/VnTable.vue';
 import { useStateStore } from 'stores/useStateStore';
-import { toDateFormat } from 'src/filters/date.js';
+import { toDate } from 'src/filters';
 import { dashIfEmpty } from 'src/filters';
 import axios from 'axios';
 import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
+const entityId = computed(() => route.params.id);
 
 const router = useRouter();
 const stateStore = useStateStore();
 const { t } = useI18n();
 const tableRef = ref();
+const route = useRoute();
 
 const itemFilter = {
     include: [
         {
-            relation: 'trainingCourseType',
+            relation: 'itemType',
             scope: {
                 fields: ['id', 'name'],
             },
         },
         {
-            relation: 'trainingCenter',
+            relation: 'intrastat',
+            scope: {
+                fields: ['id', 'description'],
+            },
+        },
+        {
+            relation: 'origin',
             scope: {
                 fields: ['id', 'name'],
             },
@@ -34,8 +43,18 @@ const itemFilter = {
 const columns = computed(() => [
     {
         label: '',
-        name: 'picture',
+        name: 'image',
         align: 'left',
+        columnField: {
+            component: VnImg,
+            attrs: (id) => {
+                return {
+                    id,
+                    width: '50px',
+                };
+            },
+        },
+        columnFilter: false,
     },
     {
         label: t('item.list.id'),
@@ -88,6 +107,10 @@ const columns = computed(() => [
             url: 'ItemType',
             fields: ['id', 'name'],
         },
+        columnField: {
+            component: null,
+        },
+        create: true,
     },
 
     {
@@ -100,6 +123,9 @@ const columns = computed(() => [
             url: 'ItemCategory',
             fields: ['id', 'name'],
         },
+        columnField: {
+            component: null,
+        },
     },
 
     {
@@ -112,6 +138,10 @@ const columns = computed(() => [
             url: 'Intrastat',
             fields: ['id', 'description'],
         },
+        columnField: {
+            component: null,
+        },
+        create: true,
     },
     {
         label: t('item.list.origin'),
@@ -123,6 +153,10 @@ const columns = computed(() => [
             url: 'Origin',
             fields: ['id', 'name'],
         },
+        columnField: {
+            component: null,
+        },
+        create: true,
     },
     {
         label: t('item.list.userName'),
@@ -136,7 +170,9 @@ const columns = computed(() => [
         name: 'weightByPiece',
         align: 'left',
         component: 'input',
-        format: (val) => dashIfEmpty(val),
+        columnField: {
+            component: null,
+        },
     },
     {
         label: t('item.list.stemMultiplier'),
@@ -144,7 +180,9 @@ const columns = computed(() => [
         name: 'stemMultiplier',
         align: 'left',
         component: 'input',
-        format: (val) => dashIfEmpty(val),
+        columnField: {
+            component: null,
+        },
     },
     {
         label: t('item.list.isActive'),
@@ -163,6 +201,9 @@ const columns = computed(() => [
             url: 'Producer',
             fields: ['id', 'name'],
         },
+        columnField: {
+            component: null,
+        },
     },
     {
         label: t('item.list.landed'),
@@ -170,7 +211,10 @@ const columns = computed(() => [
         name: 'landed',
         align: 'left',
         component: 'date',
-        format: (val) => dashIfEmpty(toDateFormat(val)),
+        columnField: {
+            component: null,
+        },
+        format: (row, dashIfEmpty) => dashIfEmpty(toDate(row.landed)),
     },
 ]);
 
@@ -209,9 +253,10 @@ onUnmounted(() => (stateStore.rightDrawer = false));
     <VnTable
         ref="tableRef"
         data-key="ItemList"
-        url="Items"
+        url="Items/filter"
         url-create="Items"
         save-url="Items/crud"
+        :filter="itemFilter"
         :create="{
             urlCreate: 'Items',
             title: 'Create Item',
@@ -223,6 +268,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
         order="id ASC"
         :columns="columns"
         auto-load
+        redirect="Item"
         :right-search="false"
         :is-editable="false"
         :use-model="true"

From afbcd2ebda00ec2487ebc090940f83405ce54f1f Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Mon, 15 Jul 2024 14:31:33 +0200
Subject: [PATCH 003/150] refs #7283 item filters

---
 src/pages/Item/ItemList.vue     |  14 ++--
 src/pages/Item/ItemTypeList.vue | 140 ++++++++++++++++++++------------
 2 files changed, 91 insertions(+), 63 deletions(-)

diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index 4c49f068f..e2c5f4bb3 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -7,9 +7,7 @@ import VnImg from 'src/components/ui/VnImg.vue';
 import VnTable from 'components/VnTable/VnTable.vue';
 import { useStateStore } from 'stores/useStateStore';
 import { toDate } from 'src/filters';
-import { dashIfEmpty } from 'src/filters';
 import axios from 'axios';
-import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
 const entityId = computed(() => route.params.id);
 
 const router = useRouter();
@@ -104,7 +102,7 @@ const columns = computed(() => [
         align: 'left',
         component: 'select',
         attrs: {
-            url: 'ItemType',
+            url: 'ItemTypes',
             fields: ['id', 'name'],
         },
         columnField: {
@@ -112,7 +110,6 @@ const columns = computed(() => [
         },
         create: true,
     },
-
     {
         label: t('item.list.category'),
         field: 'category',
@@ -120,14 +117,13 @@ const columns = computed(() => [
         align: 'left',
         component: 'select',
         attrs: {
-            url: 'ItemCategory',
+            url: 'ItemCategories',
             fields: ['id', 'name'],
         },
         columnField: {
             component: null,
         },
     },
-
     {
         label: t('item.list.intrastat'),
         field: 'intrastat',
@@ -135,7 +131,7 @@ const columns = computed(() => [
         align: 'left',
         component: 'select',
         attrs: {
-            url: 'Intrastat',
+            url: 'Intrastats',
             fields: ['id', 'description'],
         },
         columnField: {
@@ -150,7 +146,7 @@ const columns = computed(() => [
         align: 'left',
         component: 'select',
         attrs: {
-            url: 'Origin',
+            url: 'Origins',
             fields: ['id', 'name'],
         },
         columnField: {
@@ -198,7 +194,7 @@ const columns = computed(() => [
         align: 'left',
         component: 'select',
         attrs: {
-            url: 'Producer',
+            url: 'Producers',
             fields: ['id', 'name'],
         },
         columnField: {
diff --git a/src/pages/Item/ItemTypeList.vue b/src/pages/Item/ItemTypeList.vue
index 125672d60..c02f9bb43 100644
--- a/src/pages/Item/ItemTypeList.vue
+++ b/src/pages/Item/ItemTypeList.vue
@@ -1,20 +1,13 @@
 <script setup>
 import { useI18n } from 'vue-i18n';
 import { useRouter } from 'vue-router';
-
-import VnPaginate from 'src/components/ui/VnPaginate.vue';
-import VnLv from 'src/components/ui/VnLv.vue';
-import CardList from 'src/components/ui/CardList.vue';
-import ItemTypeSummary from 'src/pages/ItemType/Card/ItemTypeSummary.vue';
-import ItemTypeFilter from 'src/pages/ItemType/ItemTypeFilter.vue';
+import { ref, computed } from 'vue';
 import ItemTypeSearchbar from '../ItemType/ItemTypeSearchbar.vue';
-import { useSummaryDialog } from 'src/composables/useSummaryDialog';
-import RightMenu from 'src/components/common/RightMenu.vue';
+import VnTable from 'components/VnTable/VnTable.vue';
 
 const router = useRouter();
 const { t } = useI18n();
-const { viewSummary } = useSummaryDialog();
-
+const tableRef = ref();
 const redirectToItemTypeSummary = (id) => {
     router.push({ name: 'ItemTypeSummary', params: { id } });
 };
@@ -56,52 +49,91 @@ const exprBuilder = (param, value) => {
             }
     }
 };
+
+const columns = computed(() => [
+    {
+        align: 'left',
+        name: 'id',
+        label: t('id'),
+        isId: true,
+        cardVisible: true,
+    },
+    {
+        align: 'left',
+        name: 'code',
+        label: t('code'),
+        isTitle: true,
+        cardVisible: true,
+        create: true,
+    },
+    {
+        align: 'left',
+        name: 'name',
+        label: t('name'),
+        cardVisible: true,
+        create: true,
+    },
+    {
+        align: 'left',
+        name: 'worker',
+        label: t('worker'),
+        create: true,
+        component: 'select',
+        attrs: {
+            url: 'Workers',
+            fields: ['id', 'firstName'],
+        },
+        cardVisible: true,
+    },
+    {
+        align: 'left',
+        name: 'ItemCategory',
+        label: t('ItemCategory'),
+        create: true,
+        component: 'select',
+        attrs: {
+            url: 'ItemCategories',
+            fields: ['id', 'name'],
+        },
+        cardVisible: true,
+    },
+    {
+        align: 'left',
+        name: 'Temperature',
+        label: t('Temperature'),
+        create: true,
+        component: 'select',
+        attrs: {
+            url: 'Temperatures',
+            fields: ['id', 'name'],
+        },
+        cardVisible: true,
+    },
+]);
 </script>
 
 <template>
     <ItemTypeSearchbar />
-    <RightMenu>
-        <template #right-panel>
-            <ItemTypeFilter data-key="ItemTypeList" />
-        </template>
-    </RightMenu>
-    <QPage class="column items-center q-pa-md">
-        <div class="vn-card-list">
-            <VnPaginate
-                data-key="ItemTypeList"
-                url="ItemTypes"
-                :order="['name']"
-                auto-load
-                :expr-builder="exprBuilder"
-            >
-                <template #body="{ rows }">
-                    <CardList
-                        v-for="row of rows"
-                        :key="row.id"
-                        :title="row.code"
-                        @click="redirectToItemTypeSummary(row.id)"
-                        :id="row.id"
-                    >
-                        <template #list-items>
-                            <VnLv :label="t('Name')" :value="row.name" />
-                        </template>
-                        <template #actions>
-                            <QBtn
-                                :label="t('components.smartCard.openSummary')"
-                                @click.stop="viewSummary(row.id, ItemTypeSummary)"
-                                color="primary"
-                                type="submit"
-                            />
-                        </template>
-                    </CardList>
-                </template>
-            </VnPaginate>
-        </div>
-    </QPage>
-    <QPageSticky :offset="[20, 20]">
-        <QBtn fab icon="add" color="primary" @click="redirectToCreateView()" />
-        <QTooltip>
-            {{ t('New item type') }}
-        </QTooltip>
-    </QPageSticky>
+    <VnTable
+        ref="tableRef"
+        data-key="ItemTypeList"
+        :url="`ItemTypes`"
+        :url-create="`ItemTypes`"
+        save-url="ItemTypes/crud"
+        :filter="courseFilter"
+        :create="{
+            urlCreate: 'ItemTypes',
+            title: 'Create ItemTypes',
+            onDataSaved: () => tableRef.reload(),
+            formInitialData: {
+                workerFk: entityId,
+            },
+        }"
+        order="id DESC"
+        :columns="columns"
+        auto-load
+        :right-search="false"
+        :is-editable="false"
+        :use-model="true"
+    />
 </template>

From daf99f47306f2ae346e9fd023bce9e8b8c222c7b Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Mon, 15 Jul 2024 15:00:43 +0200
Subject: [PATCH 004/150] refs #7283 itemRequestList

---
 src/pages/Item/ItemRequest.vue | 26 ++++++++++++++++++--------
 1 file changed, 18 insertions(+), 8 deletions(-)

diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue
index ae6638953..10cb6c2a6 100644
--- a/src/pages/Item/ItemRequest.vue
+++ b/src/pages/Item/ItemRequest.vue
@@ -18,6 +18,7 @@ import useNotify from 'src/composables/useNotify.js';
 import { getDateQBadgeColor } from 'src/composables/getDateQBadgeColor.js';
 import axios from 'axios';
 import RightMenu from 'src/components/common/RightMenu.vue';
+import { toDate } from 'src/filters';
 
 const { t } = useI18n();
 const { notify } = useNotify();
@@ -46,21 +47,28 @@ const columns = computed(() => [
         name: 'id',
         field: 'id',
         align: 'left',
-        sortable: true,
+        isId: true,
+        chip: {
+            condition: () => true,
+        },
+        cardVisible: true,
     },
     {
         label: t('item.buyRequest.shipped'),
         field: 'shipped',
         name: 'shipped',
         align: 'left',
-        sortable: true,
+        component: 'date',
+        columnField: {
+            component: null,
+        },
+        format: (row, dashIfEmpty) => dashIfEmpty(toDate(row.shipped)),
     },
     {
         label: t('globals.description'),
         field: 'description',
         name: 'description',
         align: 'left',
-        sortable: true,
     },
     {
         label: t('item.buyRequest.requester'),
@@ -80,29 +88,31 @@ const columns = computed(() => [
         field: 'price',
         name: 'price',
         align: 'left',
-        sortable: true,
-        format: (val) => toCurrency(val),
+        format: (row) => toCurrency(row.price),
     },
     {
         label: t('item.buyRequest.attender'),
         field: 'attender',
         name: 'attender',
         align: 'left',
-        sortable: true,
+        attrs: {
+            url: 'Workers',
+            fields: ['id', 'firstName'],
+        },
     },
     {
         label: t('item.buyRequest.item'),
         field: 'item',
         name: 'item',
         align: 'left',
-        sortable: true,
+        component: 'input',
     },
     {
         label: t('item.buyRequest.achieved'),
         field: 'achieved',
         name: 'achieved',
         align: 'left',
-        sortable: true,
+        component: 'input',
     },
     {
         label: t('item.buyRequest.concept'),

From b7ba8eec3da2524c3fc2834dc2e4ffb64dbfc20d Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Tue, 16 Jul 2024 08:24:57 +0200
Subject: [PATCH 005/150] refs #7283 fix searchbar

---
 src/pages/Item/ItemList.vue | 41 +++++++++++++++++++------------------
 1 file changed, 21 insertions(+), 20 deletions(-)

diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index e2c5f4bb3..17a62c5b9 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -3,11 +3,12 @@ import { onMounted, ref, computed, reactive, onUnmounted } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useRouter, useRoute } from 'vue-router';
 import VnImg from 'src/components/ui/VnImg.vue';
-
+import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
 import VnTable from 'components/VnTable/VnTable.vue';
 import { useStateStore } from 'stores/useStateStore';
 import { toDate } from 'src/filters';
 import axios from 'axios';
+import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
 const entityId = computed(() => route.params.id);
 
 const router = useRouter();
@@ -212,16 +213,21 @@ const columns = computed(() => [
         },
         format: (row, dashIfEmpty) => dashIfEmpty(toDate(row.landed)),
     },
+    {
+        align: 'right',
+        label: '',
+        name: 'tableActions',
+        actions: [
+            {
+                title: t('Clone item'),
+                icon: 'vn:clone',
+                action: cloneItem,
+                isPrimary: true,
+            },
+        ],
+    },
 ]);
 
-const redirectToItemCreate = () => {
-    router.push({ name: 'ItemCreate' });
-};
-
-const redirectToItemSummary = (id) => {
-    router.push({ name: 'ItemSummary', params: { id } });
-};
-
 const cloneItem = async (itemFk) => {
     try {
         const { data } = await axios.post(`Items/${itemFk}/clone`);
@@ -236,16 +242,11 @@ onUnmounted(() => (stateStore.rightDrawer = false));
 </script>
 
 <template>
-    <!-- <VnSubToolbar>
-        <template #st-data>
-            <TableVisibleColumns
-                :all-columns="allColumnNames"
-                table-code="itemsIndex"
-                labels-traductions-path="item.list"
-                @on-config-saved="visibleColumns = ['picture', ...$event, 'actions']"
-            />
-        </template>
-    </VnSubToolbar> -->
+    <VnSearchbar
+        data-key="ItemList"
+        :label="t('Search Item')"
+        :info="t('You can search by id')"
+    />
     <VnTable
         ref="tableRef"
         data-key="ItemList"
@@ -265,7 +266,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
         :columns="columns"
         auto-load
         redirect="Item"
-        :right-search="false"
+        :right-search="true"
         :is-editable="false"
         :use-model="true"
     />

From 85b030c7bc07fa7e2abf7e1c7e0d969c7000322d Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Tue, 16 Jul 2024 08:31:04 +0200
Subject: [PATCH 006/150] refs #7283 view summary

---
 src/pages/Item/ItemList.vue | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index 17a62c5b9..0e444254f 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -9,8 +9,10 @@ import { useStateStore } from 'stores/useStateStore';
 import { toDate } from 'src/filters';
 import axios from 'axios';
 import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
+import { useSummaryDialog } from 'src/composables/useSummaryDialog';
 const entityId = computed(() => route.params.id);
 
+const { viewSummary } = useSummaryDialog();
 const router = useRouter();
 const stateStore = useStateStore();
 const { t } = useI18n();
@@ -224,6 +226,11 @@ const columns = computed(() => [
                 action: cloneItem,
                 isPrimary: true,
             },
+            {
+                title: t('view Summary'),
+                icon: 'preview',
+                action: (row) => viewSummary(row.id),
+            },
         ],
     },
 ]);

From cce1d891fb67b738a45d6f3d59cbcda6dd1dde5d Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Tue, 16 Jul 2024 08:48:50 +0200
Subject: [PATCH 007/150] refs #7283 fix viewSummary

---
 src/pages/Item/ItemList.vue | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index 0e444254f..273374150 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -10,6 +10,7 @@ import { toDate } from 'src/filters';
 import axios from 'axios';
 import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
 import { useSummaryDialog } from 'src/composables/useSummaryDialog';
+import ItemSummary from '../Item/Card/ItemSummary.vue';
 const entityId = computed(() => route.params.id);
 
 const { viewSummary } = useSummaryDialog();
@@ -229,7 +230,7 @@ const columns = computed(() => [
             {
                 title: t('view Summary'),
                 icon: 'preview',
-                action: (row) => viewSummary(row.id),
+                action: (row) => viewSummary(row.id, ItemSummary),
             },
         ],
     },

From 339f6e810bf87a9115691c69f447b43988b08e2d Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Tue, 16 Jul 2024 10:34:00 +0200
Subject: [PATCH 008/150] refs #7283 fix descriptorproxy

---
 src/pages/Item/ItemList.vue | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index 273374150..6893d55a1 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -11,6 +11,7 @@ import axios from 'axios';
 import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
 import { useSummaryDialog } from 'src/composables/useSummaryDialog';
 import ItemSummary from '../Item/Card/ItemSummary.vue';
+import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
 const entityId = computed(() => route.params.id);
 
 const { viewSummary } = useSummaryDialog();
@@ -277,7 +278,14 @@ onUnmounted(() => (stateStore.rightDrawer = false));
         :right-search="true"
         :is-editable="false"
         :use-model="true"
-    />
+    >
+        <template #column-userName="{ row }">
+            <span class="link" @click.stop>
+                {{ row.userName }}
+                <WorkerDescriptorProxy :id="row.buyerFk" />
+            </span>
+        </template>
+    </VnTable>
 </template>
 
 <i18n>

From f29d873ed42abad1c45d641a51aa02bc2ab02db1 Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Tue, 16 Jul 2024 12:15:39 +0200
Subject: [PATCH 009/150] refs #7283 fix request

---
 src/pages/Item/ItemRequest.vue | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue
index 10cb6c2a6..e5faff6b2 100644
--- a/src/pages/Item/ItemRequest.vue
+++ b/src/pages/Item/ItemRequest.vue
@@ -120,13 +120,13 @@ const columns = computed(() => [
         name: 'concept',
         align: 'left',
         sortable: true,
+        component: 'input',
     },
     {
         label: t('item.buyRequest.state'),
         field: 'state',
         name: 'state',
         align: 'left',
-        sortable: true,
     },
     {
         label: '',
@@ -252,7 +252,6 @@ onBeforeMount(() => {
                     <TicketDescriptorProxy :id="row.ticketFk" />
                 </QTd>
             </template>
-
             <template #body-cell-shipped="{ row }">
                 <QTd>
                     <QBadge

From 86ae827fda7a8c45b346fdfaa490225b10039cbd Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Tue, 16 Jul 2024 13:48:32 +0200
Subject: [PATCH 010/150] refs #7283 clone

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

diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index 6893d55a1..c44438087 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -239,7 +239,7 @@ const columns = computed(() => [
 
 const cloneItem = async (itemFk) => {
     try {
-        const { data } = await axios.post(`Items/${itemFk}/clone`);
+        const { data } = await axios.post(`Items/${itemFk.id}/clone`);
         if (!data) return;
         router.push({ name: 'ItemTags', params: { id: data.id } });
     } catch (err) {

From 4d394a98a4f3d2b23bd580fcbe0a0186892e9b0c Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Mon, 22 Jul 2024 08:24:59 +0200
Subject: [PATCH 011/150] refs #7283 fix yml list basicData

---
 src/i18n/locale/en.yml                | 21 +++++++++++-
 src/i18n/locale/es.yml                | 21 +++++++++++-
 src/pages/Item/Card/ItemBasicData.vue | 47 ++++++++++++++++-----------
 src/pages/Item/ItemList.vue           |  1 -
 4 files changed, 68 insertions(+), 22 deletions(-)

diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index 2f1209a3a..a3d7362b3 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -246,7 +246,6 @@ globals:
         mailForwarding: Mail forwarding
         mailAlias: Mail alias
         privileges: Privileges
-        labeler: Labeler
     created: Created
     worker: Worker
     now: Now
@@ -1194,6 +1193,26 @@ item:
         stemMultiplier: Multiplier
         producer: Producer
         landed: Landed
+    basicData:
+        type: Type
+        reference: Reference
+        relevancy: Relevancy
+        stems: Stems
+        multiplier: Multiplier
+        generic: Generic
+        intrastat: Intrastat
+        expense: Expense
+        weightByPiece: Weight/Piece
+        boxUnits: Units/Box
+        recycledPlastic: Recycled Plastic
+        nonRecycledPlastic: Non recycled plastic
+        isActive: Active
+        hasKgPrice: Price in kg
+        isFragile: Fragile
+        isFragileTooltip: Is shown at website, app that this item cannot travel (wreath, palms, ...)
+        isPhotoRequested: Do photo
+        isPhotoRequestedTooltip: This item does need a photo
+        description: Description
     fixedPrice:
         itemId: Item ID
         groupingPrice: Grouping price
diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml
index c5c4fab66..71b2c5919 100644
--- a/src/i18n/locale/es.yml
+++ b/src/i18n/locale/es.yml
@@ -248,7 +248,6 @@ globals:
         components: Componentes
         pictures: Fotos
         packages: Bultos
-        labeler: Etiquetas
     created: Fecha creación
     worker: Trabajador
     now: Ahora
@@ -1175,6 +1174,26 @@ item:
         stemMultiplier: Multiplicador
         producer: Productor
         landed: F. entrega
+    basicData:
+        type: Tipo
+        reference: Referencia
+        relevancy: Relevancia
+        stems: Tallos
+        multiplier: Multiplicador
+        generic: Genérico
+        intrastat: Intrastat
+        expense: Gasto
+        weightByPiece: Peso (gramos)/tallo
+        boxUnits: Unidades/caja
+        recycledPlastic: Plastico reciclado
+        nonRecycledPlastic: Plático no reciclado
+        isActive: Activo
+        hasKgPrice: Precio en kg
+        isFragile: Frágil
+        isFragileTooltip: Se muestra en la web, app que este artículo no puede viajar (coronas, palmas, ...)
+        isPhotoRequested: Hacer foto
+        isPhotoRequestedTooltip: Este artículo necesita una foto
+        description: Descripción
     fixedPrice:
         itemId: ID Artículo
         groupingPrice: Precio grouping
diff --git a/src/pages/Item/Card/ItemBasicData.vue b/src/pages/Item/Card/ItemBasicData.vue
index eb486f551..07242635b 100644
--- a/src/pages/Item/Card/ItemBasicData.vue
+++ b/src/pages/Item/Card/ItemBasicData.vue
@@ -73,7 +73,7 @@ const onIntrastatCreated = (response, formData) => {
         <template #form="{ data }">
             <VnRow class="row q-gutter-md q-mb-md">
                 <VnSelect
-                    :label="t('basicData.type')"
+                    :label="t('item.basicData.type')"
                     v-model="data.typeFk"
                     :options="itemTypesOptions"
                     option-value="id"
@@ -92,17 +92,20 @@ const onIntrastatCreated = (response, formData) => {
                         </QItem>
                     </template>
                 </VnSelect>
-                <VnInput :label="t('basicData.reference')" v-model="data.comment" />
-                <VnInput :label="t('basicData.relevancy')" v-model="data.relevancy" />
+                <VnInput :label="t('item.basicData.reference')" v-model="data.comment" />
+                <VnInput
+                    :label="t('item.basicData.relevancy')"
+                    v-model="data.relevancy"
+                />
             </VnRow>
             <VnRow class="row q-gutter-md q-mb-md">
-                <VnInput :label="t('basicData.stems')" v-model="data.stems" />
+                <VnInput :label="t('item.basicData.stems')" v-model="data.stems" />
                 <VnInput
-                    :label="t('basicData.multiplier')"
+                    :label="t('item.basicData.multiplier')"
                     v-model="data.stemMultiplier"
                 />
                 <VnSelectDialog
-                    :label="t('basicData.generic')"
+                    :label="t('item.basicData.generic')"
                     v-model="data.genericFk"
                     :options="itemsWithNameOptions"
                     option-value="id"
@@ -129,7 +132,7 @@ const onIntrastatCreated = (response, formData) => {
             </VnRow>
             <VnRow class="row q-gutter-md q-mb-md">
                 <VnSelectDialog
-                    :label="t('basicData.intrastat')"
+                    :label="t('item.basicData.intrastat')"
                     v-model="data.intrastatFk"
                     :options="intrastatsOptions"
                     option-value="id"
@@ -156,7 +159,7 @@ const onIntrastatCreated = (response, formData) => {
                 </VnSelectDialog>
                 <div class="col">
                     <VnSelect
-                        :label="t('basicData.expense')"
+                        :label="t('item.basicData.expense')"
                         v-model="data.expenseFk"
                         :options="expensesOptions"
                         option-value="id"
@@ -168,61 +171,67 @@ const onIntrastatCreated = (response, formData) => {
             </VnRow>
             <VnRow class="row q-gutter-md q-mb-md">
                 <VnInput
-                    :label="t('basicData.weightByPiece')"
+                    :label="t('item.basicData.weightByPiece')"
                     v-model.number="data.weightByPiece"
                     :min="0"
                     type="number"
                 />
                 <VnInput
-                    :label="t('basicData.boxUnits')"
+                    :label="t('item.basicData.boxUnits')"
                     v-model.number="data.packingOut"
                     :min="0"
                     type="number"
                 />
                 <VnInput
-                    :label="t('basicData.recycledPlastic')"
+                    :label="t('item.basicData.recycledPlastic')"
                     v-model.number="data.recycledPlastic"
                     :min="0"
                     type="number"
                 />
                 <VnInput
-                    :label="t('basicData.nonRecycledPlastic')"
+                    :label="t('item.basicData.nonRecycledPlastic')"
                     v-model.number="data.nonRecycledPlastic"
                     :min="0"
                     type="number"
                 />
             </VnRow>
             <VnRow class="row q-gutter-md q-mb-md">
-                <QCheckbox v-model="data.isActive" :label="t('basicData.isActive')" />
-                <QCheckbox v-model="data.hasKgPrice" :label="t('basicData.hasKgPrice')" />
+                <QCheckbox
+                    v-model="data.isActive"
+                    :label="t('item.basicData.isActive')"
+                />
+                <QCheckbox
+                    v-model="data.hasKgPrice"
+                    :label="t('item.basicData.hasKgPrice')"
+                />
                 <div>
                     <QCheckbox
                         v-model="data.isFragile"
-                        :label="t('basicData.isFragile')"
+                        :label="t('item.basicData.isFragile')"
                         class="q-mr-sm"
                     />
                     <QIcon name="info" class="cursor-pointer" size="xs">
                         <QTooltip max-width="300px">
-                            {{ t('basicData.isFragileTooltip') }}
+                            {{ t('item.basicData.isFragileTooltip') }}
                         </QTooltip>
                     </QIcon>
                 </div>
                 <div>
                     <QCheckbox
                         v-model="data.isPhotoRequested"
-                        :label="t('basicData.isPhotoRequested')"
+                        :label="t('item.basicData.isPhotoRequested')"
                         class="q-mr-sm"
                     />
                     <QIcon name="info" class="cursor-pointer" size="xs">
                         <QTooltip>
-                            {{ t('basicData.isPhotoRequestedTooltip') }}
+                            {{ t('item.basicData.isPhotoRequestedTooltip') }}
                         </QTooltip>
                     </QIcon>
                 </div>
             </VnRow>
             <VnRow>
                 <VnInput
-                    :label="t('basicData.description')"
+                    :label="t('item.basicData.description')"
                     type="textarea"
                     v-model="data.description"
                     fill-input
diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index c44438087..169ee93d6 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -226,7 +226,6 @@ const columns = computed(() => [
                 title: t('Clone item'),
                 icon: 'vn:clone',
                 action: cloneItem,
-                isPrimary: true,
             },
             {
                 title: t('view Summary'),

From f678c6304313cbc6d976c9df21f7f0e33c958dac Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Mon, 22 Jul 2024 13:35:41 +0200
Subject: [PATCH 012/150] refs #7283 request

---
 src/i18n/locale/en.yml         |  2 +-
 src/pages/Item/ItemList.vue    |  6 +----
 src/pages/Item/ItemRequest.vue | 40 +++++++++++++++++-----------------
 3 files changed, 22 insertions(+), 26 deletions(-)

diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index a3d7362b3..33862f766 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -1235,7 +1235,7 @@ item:
         requester: 'Requester'
         requested: 'Requested'
         price: 'Price'
-        attender: 'Atender'
+        attender: 'Attender'
         item: 'Item'
         achieved: 'Achieved'
         concept: 'Concept'
diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index 169ee93d6..2532a6df2 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -1,9 +1,8 @@
 <script setup>
-import { onMounted, ref, computed, reactive, onUnmounted } from 'vue';
+import { ref, computed, onUnmounted } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useRouter, useRoute } from 'vue-router';
 import VnImg from 'src/components/ui/VnImg.vue';
-import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
 import VnTable from 'components/VnTable/VnTable.vue';
 import { useStateStore } from 'stores/useStateStore';
 import { toDate } from 'src/filters';
@@ -245,8 +244,6 @@ const cloneItem = async (itemFk) => {
         console.error('Error cloning item', err);
     }
 };
-
-onUnmounted(() => (stateStore.rightDrawer = false));
 </script>
 
 <template>
@@ -274,7 +271,6 @@ onUnmounted(() => (stateStore.rightDrawer = false));
         :columns="columns"
         auto-load
         redirect="Item"
-        :right-search="true"
         :is-editable="false"
         :use-model="true"
     >
diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue
index e5faff6b2..ed6f623aa 100644
--- a/src/pages/Item/ItemRequest.vue
+++ b/src/pages/Item/ItemRequest.vue
@@ -1,24 +1,14 @@
 <script setup>
 import { ref, computed, onMounted, onBeforeMount, watch } from 'vue';
 import { useI18n } from 'vue-i18n';
-import FetchData from 'components/FetchData.vue';
-import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
 import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
-import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
-import VnInput from 'src/components/common/VnInput.vue';
-import ItemRequestDenyForm from './ItemRequestDenyForm.vue';
-import ItemRequestFilter from './ItemRequestFilter.vue';
-import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
-import VnSelect from 'components/common/VnSelect.vue';
 import { useStateStore } from 'stores/useStateStore';
 import { useArrayData } from 'composables/useArrayData';
-import { toDateFormat } from 'src/filters/date';
 import { toCurrency } from 'filters/index';
 import useNotify from 'src/composables/useNotify.js';
-import { getDateQBadgeColor } from 'src/composables/getDateQBadgeColor.js';
 import axios from 'axios';
-import RightMenu from 'src/components/common/RightMenu.vue';
 import { toDate } from 'src/filters';
+import VnTable from 'components/VnTable/VnTable.vue';
 
 const { t } = useI18n();
 const { notify } = useNotify();
@@ -72,16 +62,15 @@ const columns = computed(() => [
     },
     {
         label: t('item.buyRequest.requester'),
-        name: 'requester',
+        field: 'requesterName',
+        name: 'requesterFk',
         align: 'left',
-        sortable: true,
     },
     {
         label: t('item.buyRequest.requested'),
         field: 'quantity',
         name: 'requested',
         align: 'left',
-        sortable: true,
     },
     {
         label: t('item.buyRequest.price'),
@@ -95,10 +84,6 @@ const columns = computed(() => [
         field: 'attender',
         name: 'attender',
         align: 'left',
-        attrs: {
-            url: 'Workers',
-            fields: ['id', 'firstName'],
-        },
     },
     {
         label: t('item.buyRequest.item'),
@@ -218,7 +203,7 @@ onBeforeMount(() => {
 </script>
 
 <template>
-    <FetchData
+    <!-- <FetchData
         url="Workers"
         :filter="{ where: { role: 'buyer' } }"
         order="id"
@@ -350,7 +335,22 @@ onBeforeMount(() => {
                 @on-data-saved="onDenyAccept"
             />
         </QDialog>
-    </QPage>
+    </QPage> -->
+    <VnTable
+        ref="tableRef"
+        data-key="itemRequest"
+        url="ticketRequests"
+        order="id DESC"
+        :columns="columns"
+        auto-load
+    >
+        <template #column-attender="{ row }">
+            <span class="link" @click.stop>
+                {{ row.attenderFk }}
+                <WorkerDescriptorProxy :id="row.attenderFk" />
+            </span>
+        </template>
+    </VnTable>
 </template>
 
 <i18n>

From b668d07e2dc4fb453efcd1050abbc0f6fd570687 Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Thu, 1 Aug 2024 13:20:33 +0200
Subject: [PATCH 013/150] refs #7283 fetchedTags

---
 src/pages/Item/ItemList.vue    | 46 +++++++++++++++++++++++-----------
 src/pages/Item/ItemRequest.vue | 17 ++++++++++---
 2 files changed, 44 insertions(+), 19 deletions(-)

diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index 2532a6df2..a3b70372f 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -7,6 +7,7 @@ import VnTable from 'components/VnTable/VnTable.vue';
 import { useStateStore } from 'stores/useStateStore';
 import { toDate } from 'src/filters';
 import axios from 'axios';
+import FetchedTags from 'src/components/ui/FetchedTags.vue';
 import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
 import { useSummaryDialog } from 'src/composables/useSummaryDialog';
 import ItemSummary from '../Item/Card/ItemSummary.vue';
@@ -43,21 +44,21 @@ const itemFilter = {
     ],
 };
 const columns = computed(() => [
-    {
-        label: '',
-        name: 'image',
-        align: 'left',
-        columnField: {
-            component: VnImg,
-            attrs: (id) => {
-                return {
-                    id,
-                    width: '50px',
-                };
-            },
-        },
-        columnFilter: false,
-    },
+    // {
+    //     label: '',
+    //     name: 'image',
+    //     align: 'left',
+    //     columnField: {
+    //         component: VnImg,
+    //         attrs: (id) => {
+    //             return {
+    //                 id,
+    //                 width: '50px',
+    //             };
+    //         },
+    //     },
+    //     columnFilter: false,
+    // },
     {
         label: t('item.list.id'),
         name: 'id',
@@ -280,9 +281,24 @@ const cloneItem = async (itemFk) => {
                 <WorkerDescriptorProxy :id="row.buyerFk" />
             </span>
         </template>
+        <template #column-description="{ row }">
+            <div class="row column full-width justify-between items-start">
+                {{ row?.name }}
+                <div v-if="row?.subName" class="subName">
+                    {{ row?.subName.toUpperCase() }}
+                </div>
+            </div>
+            <FetchedTags :item="row" :max-length="6" />
+        </template>
     </VnTable>
 </template>
 
+<style lang="scss" scoped>
+.subName {
+    text-transform: uppercase;
+    color: var(--vn-label-color);
+}
+</style>
 <i18n>
 es:
     New item: Nuevo artículo
diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue
index ed6f623aa..0389ba864 100644
--- a/src/pages/Item/ItemRequest.vue
+++ b/src/pages/Item/ItemRequest.vue
@@ -13,7 +13,6 @@ import VnTable from 'components/VnTable/VnTable.vue';
 const { t } = useI18n();
 const { notify } = useNotify();
 const stateStore = useStateStore();
-const workersOptions = ref([]);
 let filterParams = ref({});
 const denyFormRef = ref(null);
 const denyRequestId = ref(null);
@@ -91,6 +90,7 @@ const columns = computed(() => [
         name: 'item',
         align: 'left',
         component: 'input',
+        visible: false,
     },
     {
         label: t('item.buyRequest.achieved'),
@@ -98,6 +98,7 @@ const columns = computed(() => [
         name: 'achieved',
         align: 'left',
         component: 'input',
+        visible: false,
     },
     {
         label: t('item.buyRequest.concept'),
@@ -106,6 +107,7 @@ const columns = computed(() => [
         align: 'left',
         sortable: true,
         component: 'input',
+        visible: false,
     },
     {
         label: t('item.buyRequest.state'),
@@ -114,10 +116,17 @@ const columns = computed(() => [
         align: 'left',
     },
     {
+        align: 'right',
         label: '',
-        name: 'action',
-        align: 'left',
-        columnFilter: null,
+        name: 'tableActions',
+        actions: [
+            {
+                title: t('Client ticket list'),
+                icon: 'thumb_down',
+                action: onDenyAccept,
+                isPrimary: true,
+            },
+        ],
     },
 ]);
 

From fe78de0c4761772593a3e714c69894f36651e696 Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Mon, 5 Aug 2024 11:49:06 +0200
Subject: [PATCH 014/150] refs #7283 fixedPrices

---
 src/pages/Item/ItemFixedPrice.vue | 325 +++++++-----------------------
 1 file changed, 70 insertions(+), 255 deletions(-)

diff --git a/src/pages/Item/ItemFixedPrice.vue b/src/pages/Item/ItemFixedPrice.vue
index 2ecd1f21b..41f2bac2f 100644
--- a/src/pages/Item/ItemFixedPrice.vue
+++ b/src/pages/Item/ItemFixedPrice.vue
@@ -1,7 +1,7 @@
 <script setup>
 import { onMounted, ref, reactive, computed, onUnmounted, watch } from 'vue';
 import { useI18n } from 'vue-i18n';
-
+import { toDate } from 'src/filters';
 import FetchData from 'components/FetchData.vue';
 import FetchedTags from 'components/ui/FetchedTags.vue';
 import VnInput from 'src/components/common/VnInput.vue';
@@ -10,7 +10,7 @@ import VnInputDate from 'src/components/common/VnInputDate.vue';
 import EditTableCellValueForm from 'src/components/EditTableCellValueForm.vue';
 import ItemFixedPriceFilter from './ItemFixedPriceFilter.vue';
 import ItemDescriptorProxy from './Card/ItemDescriptorProxy.vue';
-
+import VnTable from 'components/VnTable/VnTable.vue';
 import { useStateStore } from 'stores/useStateStore';
 import { dashIfEmpty } from 'src/filters';
 import { useVnConfirm } from 'composables/useVnConfirm';
@@ -27,7 +27,7 @@ const { t } = useI18n();
 const { openConfirmationModal } = useVnConfirm();
 const state = useState();
 const { notify } = useNotify();
-
+const tableRef = ref();
 const editTableCellDialogRef = ref(null);
 const user = state.getUser();
 const fixedPrices = ref([]);
@@ -111,86 +111,81 @@ const defaultColumnAttrs = {
 const columns = computed(() => [
     {
         label: t('item.fixedPrice.itemId'),
-        name: 'itemId',
-        field: 'itemFk',
-        ...defaultColumnAttrs,
-        columnFilter: {
-            ...defaultColumnFilter,
+        name: 'itemFk',
+        align: 'left',
+        isId: true,
+        chip: {
+            condition: () => true,
         },
     },
     {
         label: t('globals.description'),
-        field: 'name',
         name: 'description',
-        ...defaultColumnAttrs,
-        columnFilter: {
-            ...defaultColumnFilter,
-        },
+        align: 'left',
+        create: true,
     },
     {
         label: t('item.fixedPrice.groupingPrice'),
-        field: 'rate2',
-        name: 'groupingPrice',
-        ...defaultColumnAttrs,
-        columnFilter: {
-            ...defaultColumnFilter,
-        },
-        format: (val) => toCurrency(val),
+        name: 'rate2',
+        align: 'left',
+        create: true,
     },
     {
         label: t('item.fixedPrice.packingPrice'),
-        field: 'rate3',
-        name: 'packingPrice',
-        ...defaultColumnAttrs,
-        columnFilter: {
-            ...defaultColumnFilter,
-        },
-        format: (val) => dashIfEmpty(val),
+        name: 'rate3',
+        align: 'left',
+        create: true,
     },
 
     {
         label: t('item.fixedPrice.minPrice'),
-        field: 'minPrice',
         name: 'minPrice',
-        ...defaultColumnAttrs,
-        columnFilter: {
-            ...defaultColumnFilter,
-        },
+        align: 'left',
+        create: true,
     },
     {
         label: t('item.fixedPrice.started'),
-        field: 'started',
         name: 'started',
-        ...defaultColumnAttrs,
-        columnFilter: null,
+        align: 'left',
+        create: true,
+        format: (row) => toDate(row.started),
     },
     {
         label: t('item.fixedPrice.ended'),
-        field: 'ended',
         name: 'ended',
-        ...defaultColumnAttrs,
-        columnFilter: null,
+        align: 'left',
+        create: true,
+        format: (row) => toDate(row.ended),
     },
-
     {
         label: t('item.fixedPrice.warehouse'),
-        field: 'warehouseFk',
-        name: 'warehouse',
-        ...defaultColumnAttrs,
+        name: 'warehouseFk',
         columnFilter: {
-            component: VnSelect,
-            type: 'select',
-            filterValue: null,
-            event: getColumnInputEvents,
+            component: 'select',
             attrs: {
-                options: warehousesOptions.value,
-                'option-value': 'id',
-                'option-label': 'name',
-                dense: true,
+                url: 'Warehouses',
+                optionValue: 'id',
+                optionLabel: 'name',
             },
         },
+        component: 'select',
+        attrs: {
+            url: 'Warehouses',
+            fields: ['id', 'name'],
+        },
+        disable: false,
+    },
+    {
+        align: 'right',
+        label: '',
+        name: 'tableActions',
+        actions: [
+            {
+                title: t('Delete'),
+                icon: 'delete',
+            },
+        ],
     },
-    { name: 'deleteAction', align: 'center' },
 ]);
 
 const editTableFieldsOptions = [
@@ -364,212 +359,32 @@ onUnmounted(() => (stateStore.rightDrawer = false));
 </script>
 
 <template>
-    <FetchData
-        url="Warehouses"
-        :filter="{ order: ['name'] }"
+    <VnTable
+        ref="tableRef"
+        data-key="ItemFixedPrices"
+        url="FixedPrices/filter"
+        :create="{
+            urlCreate: 'PriceFixed',
+            title: 'Create Item',
+            onDataSaved: () => tableRef.redirect(),
+            formInitialData: {},
+        }"
+        order="id ASC"
+        :columns="columns"
         auto-load
-        @on-fetch="(data) => onWarehousesFetched(data)"
-    />
-    <RightMenu>
-        <template #right-panel>
-            <ItemFixedPriceFilter
-                data-key="ItemFixedPrices"
-                :warehouses-options="warehousesOptions"
-            />
+        :is-editable="false"
+        :use-model="true"
+    >
+        <template #column-description="{ row }">
+            <div class="row column full-width justify-between items-start">
+                {{ row?.description }}
+                <div v-if="row?.description" class="subName">
+                    {{ row?.subName.toUpperCase() }}
+                </div>
+            </div>
+            <FetchedTags :item="row" :max-length="6" />
         </template>
-    </RightMenu>
-    <QPage class="column items-center q-pa-md">
-        <QTable
-            :rows="fixedPrices"
-            :columns="columns"
-            row-key="id"
-            selection="multiple"
-            v-model:selected="rowsSelected"
-            :pagination="{ rowsPerPage: 0 }"
-            class="full-width q-mt-md"
-            :no-data-label="t('globals.noResults')"
-        >
-            <template #top-row="{ cols }">
-                <QTr>
-                    <QTd />
-                    <QTd
-                        v-for="(col, index) in cols"
-                        :key="index"
-                        style="max-width: 100px"
-                    >
-                        <component
-                            :is="col.columnFilter.component"
-                            v-if="col.columnFilter"
-                            v-model="col.columnFilter.filterValue"
-                            v-bind="col.columnFilter.attrs"
-                            v-on="col.columnFilter.event(col)"
-                            dense
-                        />
-                    </QTd>
-                </QTr>
-            </template>
-
-            <template #body-cell-itemId="props">
-                <QTd>
-                    <VnSelect
-                        url="Items/withName"
-                        hide-selected
-                        option-label="id"
-                        option-value="id"
-                        v-model="props.row.itemFk"
-                        v-on="getRowUpdateInputEvents(props, true, 'select')"
-                    >
-                        <template #option="scope">
-                            <QItem v-bind="scope.itemProps">
-                                <QItemSection>
-                                    <QItemLabel> #{{ scope.opt?.id }} </QItemLabel>
-                                    <QItemLabel caption>{{ scope.opt?.name }}</QItemLabel>
-                                </QItemSection>
-                            </QItem>
-                        </template>
-                    </VnSelect>
-                </QTd>
-            </template>
-            <template #body-cell-description="{ row }">
-                <QTd class="col">
-                    <span class="link">
-                        {{ row.name }}
-                    </span>
-                    <ItemDescriptorProxy :id="row.itemFk" />
-                    <FetchedTags :item="row" :max-length="6" />
-                </QTd>
-            </template>
-            <template #body-cell-groupingPrice="props">
-                <QTd class="col">
-                    <VnInput
-                        v-model.number="props.row.rate2"
-                        v-on="getRowUpdateInputEvents(props)"
-                    >
-                        <template #append>€</template>
-                    </VnInput>
-                </QTd>
-            </template>
-            <template #body-cell-packingPrice="props">
-                <QTd class="col">
-                    <VnInput
-                        v-model.number="props.row.rate3"
-                        v-on="getRowUpdateInputEvents(props)"
-                    >
-                        <template #append>€</template>
-                    </VnInput>
-                </QTd>
-            </template>
-            <template #body-cell-minPrice="props">
-                <QTd class="col">
-                    <div class="row">
-                        <QCheckbox
-                            class="col"
-                            :model-value="props.row.hasMinPrice"
-                            @update:model-value="updateMinPrice($event, props)"
-                            :false-value="0"
-                            :true-value="1"
-                            :toggle-indeterminate="false"
-                        />
-                        <VnInput
-                            class="col"
-                            :disable="!props.row.hasMinPrice"
-                            v-model.number="props.row.minPrice"
-                            v-on="getRowUpdateInputEvents(props)"
-                            type="number"
-                        />
-                    </div>
-                </QTd>
-            </template>
-            <template #body-cell-started="props">
-                <QTd class="col" style="min-width: 160px">
-                    <VnInputDate
-                        v-model="props.row.started"
-                        v-on="getRowUpdateInputEvents(props, false, 'date')"
-                        v-bind="
-                            isBigger(props.row.started)
-                                ? { 'bg-color': 'warning', 'is-outlined': true }
-                                : {}
-                        "
-                    />
-                </QTd>
-            </template>
-            <template #body-cell-ended="props">
-                <QTd class="col" style="min-width: 150px">
-                    <VnInputDate
-                        v-model="props.row.ended"
-                        v-on="getRowUpdateInputEvents(props, false, 'date')"
-                        v-bind="
-                            isLower(props.row.ended)
-                                ? { 'bg-color': 'warning', 'is-outlined': true }
-                                : {}
-                        "
-                    />
-                </QTd>
-            </template>
-            <template #body-cell-warehouse="props">
-                <QTd class="col">
-                    <VnSelect
-                        :options="warehousesOptions"
-                        hide-selected
-                        option-label="name"
-                        option-value="id"
-                        v-model="props.row.warehouseFk"
-                        v-on="getRowUpdateInputEvents(props, false, 'select')"
-                    />
-                </QTd>
-            </template>
-            <template #body-cell-deleteAction="{ row, rowIndex }">
-                <QTd class="col">
-                    <QIcon
-                        name="delete"
-                        size="sm"
-                        class="cursor-pointer fill-icon-on-hover"
-                        color="primary"
-                        @click.stop="
-                            openConfirmationModal(
-                                t('This row will be removed'),
-                                t('Do you want to clone this item?'),
-                                () => removePrice(row.id, rowIndex)
-                            )
-                        "
-                    >
-                        <QTooltip class="text-no-wrap">
-                            {{ t('Delete') }}
-                        </QTooltip>
-                    </QIcon>
-                </QTd>
-            </template>
-            <template #bottom-row>
-                <QTd align="center">
-                    <QIcon
-                        @click.stop="addRow()"
-                        class="fill-icon-on-hover"
-                        color="primary"
-                        name="add_circle"
-                        size="sm"
-                    >
-                        <QTooltip>
-                            {{ t('Add fixed price') }}
-                        </QTooltip>
-                    </QIcon>
-                </QTd>
-            </template>
-        </QTable>
-        <QPageSticky v-if="rowsSelected.length" :offset="[20, 20]">
-            <QBtn @click="openEditTableCellDialog()" color="primary" fab icon="edit" />
-            <QTooltip>
-                {{ t('Edit fixed price(s)') }}
-            </QTooltip>
-        </QPageSticky>
-        <QDialog ref="editTableCellDialogRef">
-            <EditTableCellValueForm
-                edit-url="FixedPrices/editFixedPrice"
-                :rows="rowsSelected"
-                :fields-options="editTableFieldsOptions"
-                @on-data-saved="onEditCellDataSaved()"
-            />
-        </QDialog>
-    </QPage>
+    </VnTable>
 </template>
 
 <i18n>

From 6ca8ede946356c0d3b04cf57671e583f349f7f2b Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Wed, 7 Aug 2024 09:53:42 +0200
Subject: [PATCH 015/150] refs #7283 item request

---
 src/pages/Item/ItemRequest.vue  | 28 ++++++++--------------------
 src/pages/Item/ItemTypeList.vue |  3 +++
 2 files changed, 11 insertions(+), 20 deletions(-)

diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue
index 0389ba864..8791a575f 100644
--- a/src/pages/Item/ItemRequest.vue
+++ b/src/pages/Item/ItemRequest.vue
@@ -33,8 +33,7 @@ watch(
 const columns = computed(() => [
     {
         label: t('item.buyRequest.ticketId'),
-        name: 'id',
-        field: 'id',
+        name: 'ticketFk',
         align: 'left',
         isId: true,
         chip: {
@@ -44,7 +43,6 @@ const columns = computed(() => [
     },
     {
         label: t('item.buyRequest.shipped'),
-        field: 'shipped',
         name: 'shipped',
         align: 'left',
         component: 'date',
@@ -61,57 +59,46 @@ const columns = computed(() => [
     },
     {
         label: t('item.buyRequest.requester'),
-        field: 'requesterName',
-        name: 'requesterFk',
+        name: 'requesterName',
         align: 'left',
     },
     {
         label: t('item.buyRequest.requested'),
-        field: 'quantity',
-        name: 'requested',
+        name: 'saleQuantity',
         align: 'left',
     },
     {
         label: t('item.buyRequest.price'),
-        field: 'price',
         name: 'price',
         align: 'left',
         format: (row) => toCurrency(row.price),
     },
     {
         label: t('item.buyRequest.attender'),
-        field: 'attender',
-        name: 'attender',
+        name: 'attenderName',
         align: 'left',
     },
     {
         label: t('item.buyRequest.item'),
-        field: 'item',
         name: 'item',
         align: 'left',
         component: 'input',
-        visible: false,
     },
     {
         label: t('item.buyRequest.achieved'),
-        field: 'achieved',
         name: 'achieved',
         align: 'left',
         component: 'input',
-        visible: false,
     },
     {
         label: t('item.buyRequest.concept'),
-        field: 'concept',
         name: 'concept',
         align: 'left',
         sortable: true,
         component: 'input',
-        visible: false,
     },
     {
         label: t('item.buyRequest.state'),
-        field: 'state',
         name: 'state',
         align: 'left',
     },
@@ -123,7 +110,7 @@ const columns = computed(() => [
             {
                 title: t('Client ticket list'),
                 icon: 'thumb_down',
-                action: onDenyAccept,
+                action: showDenyRequestForm,
                 isPrimary: true,
             },
         ],
@@ -348,14 +335,15 @@ onBeforeMount(() => {
     <VnTable
         ref="tableRef"
         data-key="itemRequest"
-        url="ticketRequests"
+        url="ticketRequests/filter"
         order="id DESC"
         :columns="columns"
+        :is-editable="false"
         auto-load
     >
         <template #column-attender="{ row }">
             <span class="link" @click.stop>
-                {{ row.attenderFk }}
+                {{ row }}
                 <WorkerDescriptorProxy :id="row.attenderFk" />
             </span>
         </template>
diff --git a/src/pages/Item/ItemTypeList.vue b/src/pages/Item/ItemTypeList.vue
index c02f9bb43..5ebbec62b 100644
--- a/src/pages/Item/ItemTypeList.vue
+++ b/src/pages/Item/ItemTypeList.vue
@@ -84,6 +84,7 @@ const columns = computed(() => [
             fields: ['id', 'firstName'],
         },
         cardVisible: true,
+        visible: false,
     },
     {
         align: 'left',
@@ -96,6 +97,7 @@ const columns = computed(() => [
             fields: ['id', 'name'],
         },
         cardVisible: true,
+        visible: false,
     },
     {
         align: 'left',
@@ -108,6 +110,7 @@ const columns = computed(() => [
             fields: ['id', 'name'],
         },
         cardVisible: true,
+        visible: false,
     },
 ]);
 </script>

From 589ee629cfbfc3c2b79c32ecef9ff47f30d33c00 Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Tue, 13 Aug 2024 12:45:30 +0200
Subject: [PATCH 016/150] refs #7283 fix conflicts

---
 src/pages/Item/ItemFixedPrice.vue | 255 +++++++++---------------------
 src/pages/Item/ItemRequest.vue    |  15 +-
 2 files changed, 89 insertions(+), 181 deletions(-)

diff --git a/src/pages/Item/ItemFixedPrice.vue b/src/pages/Item/ItemFixedPrice.vue
index 41f2bac2f..56ad7a17d 100644
--- a/src/pages/Item/ItemFixedPrice.vue
+++ b/src/pages/Item/ItemFixedPrice.vue
@@ -2,25 +2,15 @@
 import { onMounted, ref, reactive, computed, onUnmounted, watch } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { toDate } from 'src/filters';
-import FetchData from 'components/FetchData.vue';
 import FetchedTags from 'components/ui/FetchedTags.vue';
 import VnInput from 'src/components/common/VnInput.vue';
-import VnSelect from 'src/components/common/VnSelect.vue';
-import VnInputDate from 'src/components/common/VnInputDate.vue';
-import EditTableCellValueForm from 'src/components/EditTableCellValueForm.vue';
-import ItemFixedPriceFilter from './ItemFixedPriceFilter.vue';
-import ItemDescriptorProxy from './Card/ItemDescriptorProxy.vue';
 import VnTable from 'components/VnTable/VnTable.vue';
 import { useStateStore } from 'stores/useStateStore';
-import { dashIfEmpty } from 'src/filters';
 import { useVnConfirm } from 'composables/useVnConfirm';
 import { useState } from 'src/composables/useState';
-import { toCurrency } from 'filters/index';
 import useNotify from 'src/composables/useNotify.js';
 import axios from 'axios';
 import { useArrayData } from 'composables/useArrayData';
-import { isLower, isBigger } from 'src/filters/date.js';
-import RightMenu from 'src/components/common/RightMenu.vue';
 
 const stateStore = useStateStore();
 const { t } = useI18n();
@@ -75,39 +65,6 @@ watch(
     (data) => onFixedPricesFetched(data)
 );
 
-const applyColumnFilter = async (col) => {
-    try {
-        const paramKey = col.columnFilter?.filterParamKey || col.field;
-        params[paramKey] = col.columnFilter.filterValue;
-        await arrayData.addFilter({ params });
-    } catch (err) {
-        console.error('Error applying column filter', err);
-    }
-};
-
-const getColumnInputEvents = (col) => {
-    return col.columnFilter.type === 'select'
-        ? { 'update:modelValue': () => applyColumnFilter(col) }
-        : {
-              'keyup.enter': () => applyColumnFilter(col),
-          };
-};
-
-const defaultColumnFilter = {
-    component: VnInput,
-    type: 'text',
-    filterValue: null,
-    event: getColumnInputEvents,
-    attrs: {
-        dense: true,
-    },
-};
-
-const defaultColumnAttrs = {
-    align: 'left',
-    sortable: true,
-};
-
 const columns = computed(() => [
     {
         label: t('item.fixedPrice.itemId'),
@@ -127,12 +84,14 @@ const columns = computed(() => [
     {
         label: t('item.fixedPrice.groupingPrice'),
         name: 'rate2',
+        component: 'input',
         align: 'left',
         create: true,
     },
     {
         label: t('item.fixedPrice.packingPrice'),
         name: 'rate3',
+        component: 'input',
         align: 'left',
         create: true,
     },
@@ -140,6 +99,7 @@ const columns = computed(() => [
     {
         label: t('item.fixedPrice.minPrice'),
         name: 'minPrice',
+        component: 'input',
         align: 'left',
         create: true,
     },
@@ -188,70 +148,14 @@ const columns = computed(() => [
     },
 ]);
 
-const editTableFieldsOptions = [
-    {
-        field: 'rate2',
-        label: t('item.fixedPrice.groupingPrice'),
-        component: 'input',
-        attrs: {
-            type: 'number',
-        },
-    },
-    {
-        field: 'rate3',
-        label: t('item.fixedPrice.packingPrice'),
-        component: 'input',
-        attrs: {
-            type: 'number',
-        },
-    },
-    {
-        field: 'minPrice',
-        label: t('item.fixedPrice.minPrice'),
-        component: 'input',
-        attrs: {
-            type: 'number',
-        },
-    },
-    {
-        field: 'hasMinPrice',
-        label: t('item.fixedPrice.hasMinPrice'),
-        component: 'checkbox',
-        attrs: {
-            'false-value': 0,
-            'true-value': 1,
-        },
-    },
-    {
-        field: 'started',
-        label: t('item.fixedPrice.started'),
-        component: 'date',
-    },
-    {
-        field: 'ended',
-        label: t('item.fixedPrice.ended'),
-        component: 'date',
-    },
-    {
-        field: 'warehouseFk',
-        label: t('item.fixedPrice.warehouse'),
-        component: 'select',
-        attrs: {
-            options: [],
-            'option-label': 'name',
-            'option-value': 'id',
-        },
-    },
-];
-
-const getRowUpdateInputEvents = (props, resetMinPrice, inputType = 'text') => {
-    return inputType === 'text'
-        ? {
-              'keyup.enter': () => upsertPrice(props, resetMinPrice),
-              blur: () => upsertPrice(props, resetMinPrice),
-          }
-        : { 'update:modelValue': () => upsertPrice(props, resetMinPrice) };
-};
+// const getRowUpdateInputEvents = (props, resetMinPrice, inputType = 'text') => {
+//     return inputType === 'text'
+//         ? {
+//               'keyup.enter': () => upsertPrice(props, resetMinPrice),
+//               blur: () => upsertPrice(props, resetMinPrice),
+//           }
+//         : { 'update:modelValue': () => upsertPrice(props, resetMinPrice) };
+// };
 
 const validations = (row, rowIndex, col) => {
     const isNew = !row.id;
@@ -281,81 +185,73 @@ const upsertPrice = async ({ row, col, rowIndex }, resetMinPrice = false) => {
     }
 };
 
-const addRow = () => {
-    if (!fixedPrices.value || fixedPrices.value.length === 0) {
-        fixedPrices.value = [];
+// const addRow = () => {
+//     if (!fixedPrices.value || fixedPrices.value.length === 0) {
+//         fixedPrices.value = [];
 
-        const today = Date.vnNew();
-        const millisecsInDay = 86400000;
-        const daysInWeek = 7;
-        const nextWeek = new Date(today.getTime() + daysInWeek * millisecsInDay);
+//         const today = Date.vnNew();
+//         const millisecsInDay = 86400000;
+//         const daysInWeek = 7;
+//         const nextWeek = new Date(today.getTime() + daysInWeek * millisecsInDay);
 
-        const newPrice = {
-            started: today,
-            ended: nextWeek,
-            hasMinPrice: 0,
-        };
+//         const newPrice = {
+//             started: today,
+//             ended: nextWeek,
+//             hasMinPrice: 0,
+//         };
 
-        fixedPricesOriginalData.value.push({ ...newPrice });
-        fixedPrices.value.push({ ...newPrice });
-        return;
-    }
+//         fixedPricesOriginalData.value.push({ ...newPrice });
+//         fixedPrices.value.push({ ...newPrice });
+//         return;
+//     }
 
-    const lastItemCopy = JSON.parse(
-        JSON.stringify(fixedPrices.value[fixedPrices.value.length - 1])
-    );
-    delete lastItemCopy.id;
-    fixedPricesOriginalData.value.push(lastItemCopy);
-    fixedPrices.value.push(lastItemCopy);
-};
+//     const lastItemCopy = JSON.parse(
+//         JSON.stringify(fixedPrices.value[fixedPrices.value.length - 1])
+//     );
+//     delete lastItemCopy.id;
+//     fixedPricesOriginalData.value.push(lastItemCopy);
+//     fixedPrices.value.push(lastItemCopy);
+// };
 
-const openEditTableCellDialog = () => {
-    editTableCellDialogRef.value.show();
-};
+// const openEditTableCellDialog = () => {
+//     editTableCellDialogRef.value.show();
+// };
 
-const onEditCellDataSaved = async () => {
-    rowsSelected.value = [];
-    await fetchFixedPrices();
-};
+// const onEditCellDataSaved = async () => {
+//     rowsSelected.value = [];
+//     await fetchFixedPrices();
+// };
 
-const onWarehousesFetched = (data) => {
-    warehousesOptions.value = data;
-    // Actualiza las 'options' del elemento con field 'warehouseFk' en 'editTableFieldsOptions'.
-    const warehouseField = editTableFieldsOptions.find(
-        (field) => field.field === 'warehouseFk'
-    );
-    warehouseField.attrs.options = data;
-};
+// const onWarehousesFetched = (data) => {
+//     warehousesOptions.value = data;
+//     // Actualiza las 'options' del elemento con field 'warehouseFk' en 'editTableFieldsOptions'.
+//     const warehouseField = editTableFieldsOptions.find(
+//         (field) => field.field === 'warehouseFk'
+//     );
+//     warehouseField.attrs.options = data;
+// };
 
-const removePrice = async (id, rowIndex) => {
-    try {
-        await axios.delete(`FixedPrices/${id}`);
-        fixedPrices.value.splice(rowIndex, 1);
-        fixedPricesOriginalData.value.splice(rowIndex, 1);
-        notify(t('globals.dataSaved'), 'positive');
-    } catch (err) {
-        console.error('Error removing price', err);
-    }
-};
+// const removePrice = async (id, rowIndex) => {
+//     try {
+//         await axios.delete(`FixedPrices/${id}`);
+//         fixedPrices.value.splice(rowIndex, 1);
+//         fixedPricesOriginalData.value.splice(rowIndex, 1);
+//         notify(t('globals.dataSaved'), 'positive');
+//     } catch (err) {
+//         console.error('Error removing price', err);
+//     }
+// };
 
-const updateMinPrice = async (value, props) => {
-    // El checkbox hasMinPrice se encuentra en la misma columna que el input hasMinPrice
-    // Por lo tanto le mandamos otro objeto con las mismas propiedades pero con el campo 'field' cambiado
-    props.row.hasMinPrice = value;
-    await upsertPrice({
-        row: props.row,
-        col: { field: 'hasMinPrice' },
-        rowIndex: props.rowIndex,
-    });
-};
-
-onMounted(async () => {
-    stateStore.rightDrawer = true;
-    params.warehouseFk = user.value.warehouseFk;
-    await fetchFixedPrices();
-});
-
-onUnmounted(() => (stateStore.rightDrawer = false));
+// const updateMinPrice = async (value, props) => {
+//     // El checkbox hasMinPrice se encuentra en la misma columna que el input hasMinPrice
+//     // Por lo tanto le mandamos otro objeto con las mismas propiedades pero con el campo 'field' cambiado
+//     props.row.hasMinPrice = value;
+//     await upsertPrice({
+//         row: props.row,
+//         col: { field: 'hasMinPrice' },
+//         rowIndex: props.rowIndex,
+//     });
+// };
 </script>
 
 <template>
@@ -372,13 +268,13 @@ onUnmounted(() => (stateStore.rightDrawer = false));
         order="id ASC"
         :columns="columns"
         auto-load
-        :is-editable="false"
+        :is-editable="true"
         :use-model="true"
     >
         <template #column-description="{ row }">
             <div class="row column full-width justify-between items-start">
-                {{ row?.description }}
-                <div v-if="row?.description" class="subName">
+                {{ row?.name }}
+                <div v-if="row?.subName" class="subName">
                     {{ row?.subName.toUpperCase() }}
                 </div>
             </div>
@@ -386,7 +282,12 @@ onUnmounted(() => (stateStore.rightDrawer = false));
         </template>
     </VnTable>
 </template>
-
+<style lang="scss" scoped>
+.subName {
+    text-transform: uppercase;
+    color: var(--vn-label-color);
+}
+</style>
 <i18n>
 es:
     Add fixed price: Añadir precio fijado
diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue
index 8791a575f..f67f7eda2 100644
--- a/src/pages/Item/ItemRequest.vue
+++ b/src/pages/Item/ItemRequest.vue
@@ -100,6 +100,7 @@ const columns = computed(() => [
     {
         label: t('item.buyRequest.state'),
         name: 'state',
+        action: (row) => getState(row.isOk),
         align: 'left',
     },
     {
@@ -146,7 +147,6 @@ const confirmRequest = async (request) => {
                 `TicketRequests/${request.id}/confirm`,
                 params
             );
-
             request.itemDescription = data.concept;
             request.isOk = true;
             notify(t('globals.dataSaved'), 'positive');
@@ -338,15 +338,22 @@ onBeforeMount(() => {
         url="ticketRequests/filter"
         order="id DESC"
         :columns="columns"
-        :is-editable="false"
+        :is-editable="true"
         auto-load
     >
-        <template #column-attender="{ row }">
+        <template #column-attenderName="{ row }">
             <span class="link" @click.stop>
-                {{ row }}
+                {{ row.attenderName }}
                 <WorkerDescriptorProxy :id="row.attenderFk" />
             </span>
         </template>
+
+        <template #column-requesterName="{ row }">
+            <span class="link" @click.stop>
+                {{ row.requesterName }}
+                <WorkerDescriptorProxy :id="row.requesterFk" />
+            </span>
+        </template>
     </VnTable>
 </template>
 

From 1df089cef78e6728c764d7d72cc015b573f44f66 Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Wed, 14 Aug 2024 15:50:13 +0200
Subject: [PATCH 017/150] refs #7283 fix itemFixedPrice

---
 src/pages/Item/ItemFixedPrice.vue | 65 ++++++++++++++++++++++++-------
 1 file changed, 51 insertions(+), 14 deletions(-)

diff --git a/src/pages/Item/ItemFixedPrice.vue b/src/pages/Item/ItemFixedPrice.vue
index 56ad7a17d..30eb8544e 100644
--- a/src/pages/Item/ItemFixedPrice.vue
+++ b/src/pages/Item/ItemFixedPrice.vue
@@ -1,9 +1,10 @@
 <script setup>
-import { onMounted, ref, reactive, computed, onUnmounted, watch } from 'vue';
+import { ref, reactive, computed, watch } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { toDate } from 'src/filters';
+import { useQuasar } from 'quasar';
 import FetchedTags from 'components/ui/FetchedTags.vue';
-import VnInput from 'src/components/common/VnInput.vue';
+import { useRoute } from 'vue-router';
 import VnTable from 'components/VnTable/VnTable.vue';
 import { useStateStore } from 'stores/useStateStore';
 import { useVnConfirm } from 'composables/useVnConfirm';
@@ -11,20 +12,30 @@ import { useState } from 'src/composables/useState';
 import useNotify from 'src/composables/useNotify.js';
 import axios from 'axios';
 import { useArrayData } from 'composables/useArrayData';
+import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
+import VnConfirm from 'components/ui/VnConfirm.vue';
 
-const stateStore = useStateStore();
+const route = useRoute();
 const { t } = useI18n();
 const { openConfirmationModal } = useVnConfirm();
 const state = useState();
 const { notify } = useNotify();
 const tableRef = ref();
-const editTableCellDialogRef = ref(null);
+const quasar = useQuasar();
 const user = state.getUser();
 const fixedPrices = ref([]);
 const fixedPricesOriginalData = ref([]);
 const warehousesOptions = ref([]);
 const rowsSelected = ref([]);
 
+// function localUserData() {
+//     state.setUser(user.value);
+// }
+
+// const userLocal = localUserData();
+// console.log('localUserData', userLocal);
+
+console.log('user', user.value.warehouseFk);
 const exprBuilder = (param, value) => {
     switch (param) {
         case 'name':
@@ -80,6 +91,10 @@ const columns = computed(() => [
         name: 'description',
         align: 'left',
         create: true,
+        columnCreate: {
+            component: 'select',
+            url: 'Items',
+        },
     },
     {
         label: t('item.fixedPrice.groupingPrice'),
@@ -109,6 +124,9 @@ const columns = computed(() => [
         align: 'left',
         create: true,
         format: (row) => toDate(row.started),
+        columnCreate: {
+            component: 'date',
+        },
     },
     {
         label: t('item.fixedPrice.ended'),
@@ -116,6 +134,9 @@ const columns = computed(() => [
         align: 'left',
         create: true,
         format: (row) => toDate(row.ended),
+        columnCreate: {
+            component: 'date',
+        },
     },
     {
         label: t('item.fixedPrice.warehouse'),
@@ -148,6 +169,27 @@ const columns = computed(() => [
     },
 ]);
 
+function confirmRemove(row) {
+    quasar.dialog({
+        component: VnConfirm,
+        componentProps: {
+            title: t('confirmDeletion'),
+            message: t('confirmDeletionMessage'),
+            promise: () => remove(row),
+        },
+    });
+}
+
+async function remove(item) {
+    await axios.post('FixedPrices/filter', {
+        rows: [item.id],
+    });
+    quasar.notify({
+        message: t('globals.dataDeleted'),
+        type: 'positive',
+    });
+    tableRef.value.reload();
+}
 // const getRowUpdateInputEvents = (props, resetMinPrice, inputType = 'text') => {
 //     return inputType === 'text'
 //         ? {
@@ -213,15 +255,6 @@ const upsertPrice = async ({ row, col, rowIndex }, resetMinPrice = false) => {
 //     fixedPrices.value.push(lastItemCopy);
 // };
 
-// const openEditTableCellDialog = () => {
-//     editTableCellDialogRef.value.show();
-// };
-
-// const onEditCellDataSaved = async () => {
-//     rowsSelected.value = [];
-//     await fetchFixedPrices();
-// };
-
 // const onWarehousesFetched = (data) => {
 //     warehousesOptions.value = data;
 //     // Actualiza las 'options' del elemento con field 'warehouseFk' en 'editTableFieldsOptions'.
@@ -259,6 +292,7 @@ const upsertPrice = async ({ row, col, rowIndex }, resetMinPrice = false) => {
         ref="tableRef"
         data-key="ItemFixedPrices"
         url="FixedPrices/filter"
+        :filter="{ where: { warehouseFk: user.warehouseFk } }"
         :create="{
             urlCreate: 'PriceFixed',
             title: 'Create Item',
@@ -273,7 +307,10 @@ const upsertPrice = async ({ row, col, rowIndex }, resetMinPrice = false) => {
     >
         <template #column-description="{ row }">
             <div class="row column full-width justify-between items-start">
-                {{ row?.name }}
+                <span class="link" @click.stop>
+                    {{ row?.name }}
+                    <ItemDescriptorProxy class="link" :id="row.itemFk" />
+                </span>
                 <div v-if="row?.subName" class="subName">
                     {{ row?.subName.toUpperCase() }}
                 </div>

From 12a5d6cec035b72deaa3c9f316d0f329d5411081 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Mon, 19 Aug 2024 17:05:00 +0200
Subject: [PATCH 018/150] fix: refs #7283 css

---
 src/components/VnTable/VnTable.vue | 7 ++++---
 src/pages/Item/ItemRequest.vue     | 1 +
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index 6c77d44df..69c4739bf 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -486,11 +486,12 @@ defineExpose({
                                 :icon="btn.icon"
                                 class="q-px-sm"
                                 flat
-                                :class="
+                                :class="[
                                     btn.isPrimary
                                         ? 'text-primary-light'
-                                        : 'color-vn-text '
-                                "
+                                        : 'color-vn-text ',
+                                    btn.class,
+                                ]"
                                 :style="`visibility: ${
                                     (btn.show && btn.show(row)) ?? true
                                         ? 'visible'
diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue
index f67f7eda2..65c0aaae1 100644
--- a/src/pages/Item/ItemRequest.vue
+++ b/src/pages/Item/ItemRequest.vue
@@ -111,6 +111,7 @@ const columns = computed(() => [
             {
                 title: t('Client ticket list'),
                 icon: 'thumb_down',
+                class: 'fill-icon',
                 action: showDenyRequestForm,
                 isPrimary: true,
             },

From bae0b4de351f43a65de5b6ef05fbdaa9cd525d7c Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 22 Aug 2024 10:38:15 +0200
Subject: [PATCH 019/150] fix: itemBotanical

---
 src/pages/Item/Card/ItemBotanical.vue | 35 +++++++++++++--------------
 1 file changed, 17 insertions(+), 18 deletions(-)

diff --git a/src/pages/Item/Card/ItemBotanical.vue b/src/pages/Item/Card/ItemBotanical.vue
index 416c7f78b..88d251c47 100644
--- a/src/pages/Item/Card/ItemBotanical.vue
+++ b/src/pages/Item/Card/ItemBotanical.vue
@@ -13,7 +13,6 @@ import CreateSpecieForm from './CreateSpecieForm.vue';
 const route = useRoute();
 const { t } = useI18n();
 
-const itemBotanicalsRef = ref(null);
 const itemGenusOptions = ref([]);
 const itemSpeciesOptions = ref([]);
 const itemBotanicals = ref([]);
@@ -31,22 +30,19 @@ const onSpecieCreated = (response, formData) => {
 const entityId = computed(() => {
     return route.params.id;
 });
-onMounted(async () => {
-    itemBotanicalsForm.itemFk = entityId.value;
-    itemBotanicals.value = await itemBotanicalsRef.value.fetch();
-    if (itemBotanicals.value.length > 0)
-        Object.assign(itemBotanicalsForm, itemBotanicals.value[0]);
-});
+// onMounted(async () => {
+//     itemBotanicalsForm.itemFk = entityId.value;
+//     // itemBotanicals.value = await itemBotanicalsRef.value.fetch();
+//     if (itemBotanicals.value.length > 0)
+//         Object.assign(itemBotanicalsForm, itemBotanicals.value[0]);
+// });
+async function handleItemBotanical(data) {
+    itemBotanicalsForm = data;
+    // if (data.length > 0) Object.assign(itemBotanicalsForm, itemBotanicals.value[0]);
+}
 </script>
 <template>
-    <FetchData
-        ref="itemBotanicalsRef"
-        url="ItemBotanicals"
-        :filter="{
-            where: { itemFk: entityId },
-        }"
-        @on-fetch="(data) => (itemBotanicals = data)"
-    />
+    <!-- <FetchData ref="itemBotanicalsRef" @on-fetch="(data) => (itemBotanicals = data)" /> -->
     <FetchData
         url="Genera"
         :filter="{ fields: ['id', 'name'], order: 'name ASC' }"
@@ -60,11 +56,14 @@ onMounted(async () => {
         auto-load
     />
     <FormModel
+        url="ItemBotanicals"
         url-update="ItemBotanicals"
-        model="entry"
+        model="item"
         auto-load
-        :form-initial-data="itemBotanicalsForm"
-        :clear-store-on-unmount="false"
+        :filter="{
+            where: { itemFk: entityId },
+        }"
+        @on-fetch="handleItemBotanical"
     >
         <template #form="{ data }">
             <VnRow>

From 2ef56f9f9730c0cbc8c287531ad12363a9f4c447 Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Tue, 27 Aug 2024 08:00:07 +0200
Subject: [PATCH 020/150] refs #7283 fixedPrice

---
 src/pages/Item/ItemFixedPrice.vue | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/pages/Item/ItemFixedPrice.vue b/src/pages/Item/ItemFixedPrice.vue
index 30eb8544e..05a8545c8 100644
--- a/src/pages/Item/ItemFixedPrice.vue
+++ b/src/pages/Item/ItemFixedPrice.vue
@@ -292,6 +292,7 @@ const upsertPrice = async ({ row, col, rowIndex }, resetMinPrice = false) => {
         ref="tableRef"
         data-key="ItemFixedPrices"
         url="FixedPrices/filter"
+        save-url="FixedPrices/crud"
         :filter="{ where: { warehouseFk: user.warehouseFk } }"
         :create="{
             urlCreate: 'PriceFixed',

From 6de59c64194c4aec32294575a531dda73d5d8b0d Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Tue, 27 Aug 2024 14:39:21 +0200
Subject: [PATCH 021/150] refs #7283 itemRequestFixed

---
 src/pages/Item/ItemFixedPrice.vue |  1 +
 src/pages/Item/ItemList.vue       |  2 ++
 src/pages/Item/ItemRequest.vue    | 31 +++++++++++++++++++++++++++----
 3 files changed, 30 insertions(+), 4 deletions(-)

diff --git a/src/pages/Item/ItemFixedPrice.vue b/src/pages/Item/ItemFixedPrice.vue
index 05a8545c8..7ba4984e7 100644
--- a/src/pages/Item/ItemFixedPrice.vue
+++ b/src/pages/Item/ItemFixedPrice.vue
@@ -164,6 +164,7 @@ const columns = computed(() => [
             {
                 title: t('Delete'),
                 icon: 'delete',
+                isPrimary: true,
             },
         ],
     },
diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index a3b70372f..2f3d20390 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -226,11 +226,13 @@ const columns = computed(() => [
                 title: t('Clone item'),
                 icon: 'vn:clone',
                 action: cloneItem,
+                isPrimary: true,
             },
             {
                 title: t('view Summary'),
                 icon: 'preview',
                 action: (row) => viewSummary(row.id, ItemSummary),
+                isPrimary: true,
             },
         ],
     },
diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue
index 65c0aaae1..0d641f8e2 100644
--- a/src/pages/Item/ItemRequest.vue
+++ b/src/pages/Item/ItemRequest.vue
@@ -9,7 +9,8 @@ import useNotify from 'src/composables/useNotify.js';
 import axios from 'axios';
 import { toDate } from 'src/filters';
 import VnTable from 'components/VnTable/VnTable.vue';
-
+import VnInput from 'src/components/common/VnInput.vue';
+import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
 const { t } = useI18n();
 const { notify } = useNotify();
 const stateStore = useStateStore();
@@ -64,7 +65,7 @@ const columns = computed(() => [
     },
     {
         label: t('item.buyRequest.requested'),
-        name: 'saleQuantity',
+        name: 'quantity',
         align: 'left',
     },
     {
@@ -100,7 +101,7 @@ const columns = computed(() => [
     {
         label: t('item.buyRequest.state'),
         name: 'state',
-        action: (row) => getState(row.isOk),
+        format: (row) => getState(row.isOk),
         align: 'left',
     },
     {
@@ -333,11 +334,12 @@ onBeforeMount(() => {
             />
         </QDialog>
     </QPage> -->
+    <VnSubToolbar />
     <VnTable
         ref="tableRef"
         data-key="itemRequest"
         url="ticketRequests/filter"
-        order="id DESC"
+        order="shippedDate ASC, isOk ASC"
         :columns="columns"
         :is-editable="true"
         auto-load
@@ -355,6 +357,27 @@ onBeforeMount(() => {
                 <WorkerDescriptorProxy :id="row.requesterFk" />
             </span>
         </template>
+
+        <template #column-item="{ row }">
+            <span @click.stop disabled="row.isOk != null">
+                <VnInput type="number" v-model="row.item" fill-input />
+            </span>
+        </template>
+        <template #column-achieved="{ row }">
+            <span @click.stop disabled="!request.itemFk || request.isOk != null">
+                <VnInput
+                    type="number"
+                    v-model="row.achieved"
+                    fill-input
+                    @keyup.enter="changeQuantity(row)"
+                />
+            </span>
+        </template>
+        <template #column-concept="{ row }">
+            <span @click.stop disabled="row.isOk != null">
+                {{ row.itemDescription }}
+            </span>
+        </template>
     </VnTable>
 </template>
 

From cd1b5f56f711ad8a0b0c3141c085f03897f36f8c Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Wed, 28 Aug 2024 11:35:57 +0200
Subject: [PATCH 022/150] refs #7283 column-action

---
 src/pages/Item/ItemRequest.vue | 28 +++++++++++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue
index 0d641f8e2..41248afbb 100644
--- a/src/pages/Item/ItemRequest.vue
+++ b/src/pages/Item/ItemRequest.vue
@@ -110,7 +110,7 @@ const columns = computed(() => [
         name: 'tableActions',
         actions: [
             {
-                title: t('Client ticket list'),
+                title: t('Deny Request'),
                 icon: 'thumb_down',
                 class: 'fill-icon',
                 action: showDenyRequestForm,
@@ -378,6 +378,32 @@ onBeforeMount(() => {
                 {{ row.itemDescription }}
             </span>
         </template>
+        <template #column-action="{ row, rowIndex }">
+            <QTd>
+                <QIcon
+                    v-if="row.response?.length"
+                    name="insert_drive_file"
+                    color="primary"
+                    size="sm"
+                >
+                    <QTooltip>
+                        {{ row.response }}
+                    </QTooltip>
+                </QIcon>
+                <QIcon
+                    v-if="row.isOk == null"
+                    name="thumb_down"
+                    color="primary"
+                    size="sm"
+                    class="fill-icon"
+                    @click="showDenyRequestForm(row.id, rowIndex)"
+                >
+                    <QTooltip>
+                        {{ t('Discard') }}
+                    </QTooltip>
+                </QIcon>
+            </QTd>
+        </template>
     </VnTable>
 </template>
 

From e4aec1773b74c002913bf60f807c07ca39a873f5 Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Thu, 29 Aug 2024 12:38:57 +0200
Subject: [PATCH 023/150] refs #7283 itemRequest fix deny

---
 src/pages/Item/ItemRequest.vue | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue
index 41248afbb..0e59a16c4 100644
--- a/src/pages/Item/ItemRequest.vue
+++ b/src/pages/Item/ItemRequest.vue
@@ -7,6 +7,7 @@ import { useArrayData } from 'composables/useArrayData';
 import { toCurrency } from 'filters/index';
 import useNotify from 'src/composables/useNotify.js';
 import axios from 'axios';
+import ItemRequestDenyForm from './ItemRequestDenyForm.vue';
 import { toDate } from 'src/filters';
 import VnTable from 'components/VnTable/VnTable.vue';
 import VnInput from 'src/components/common/VnInput.vue';
@@ -26,6 +27,10 @@ const arrayData = useArrayData('ItemRequests', {
 });
 const store = arrayData.store;
 
+const userParams = {
+    state: 'pending',
+};
+
 watch(
     () => store.data,
     (value) => (itemRequestsOptions.value = value)
@@ -174,7 +179,9 @@ const onDenyAccept = (_, responseData) => {
     itemRequestsOptions.value[denyRequestIndex.value].isOk = responseData.isOk;
     itemRequestsOptions.value[denyRequestIndex.value].attenderFk =
         responseData.attenderFk;
+    console.log('itemRequestsOptions: ', itemRequestsOptions.value);
     itemRequestsOptions.value[denyRequestIndex.value].response = responseData.response;
+    console.log('itemRequestsOptions.value', itemRequestsOptions.value);
     denyRequestId.value = null;
     denyRequestIndex.value = null;
 };
@@ -341,8 +348,10 @@ onBeforeMount(() => {
         url="ticketRequests/filter"
         order="shippedDate ASC, isOk ASC"
         :columns="columns"
+        :user-params="userParams"
         :is-editable="true"
         auto-load
+        :disable-option="{ card: true }"
     >
         <template #column-attenderName="{ row }">
             <span class="link" @click.stop>
@@ -378,7 +387,7 @@ onBeforeMount(() => {
                 {{ row.itemDescription }}
             </span>
         </template>
-        <template #column-action="{ row, rowIndex }">
+        <template #column-tableActions="{ row, rowIndex }">
             <QTd>
                 <QIcon
                     v-if="row.response?.length"
@@ -405,6 +414,9 @@ onBeforeMount(() => {
             </QTd>
         </template>
     </VnTable>
+    <QDialog ref="denyFormRef" transition-show="scale" transition-hide="scale">
+        <ItemRequestDenyForm :request-id="denyRequestId" @on-data-saved="onDenyAccept" />
+    </QDialog>
 </template>
 
 <i18n>

From 388036a2eb589b6475132e630ee555147938b4a6 Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Mon, 2 Sep 2024 09:19:49 +0200
Subject: [PATCH 024/150] refs #7283 itemRequest fix

---
 src/pages/Item/ItemRequest.vue | 146 ++-------------------------------
 1 file changed, 7 insertions(+), 139 deletions(-)

diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue
index 0e59a16c4..10ed237b4 100644
--- a/src/pages/Item/ItemRequest.vue
+++ b/src/pages/Item/ItemRequest.vue
@@ -208,140 +208,6 @@ onBeforeMount(() => {
 </script>
 
 <template>
-    <!-- <FetchData
-        url="Workers"
-        :filter="{ where: { role: 'buyer' } }"
-        order="id"
-        @on-fetch="(data) => (workersOptions = data)"
-        auto-load
-    />
-    <VnSearchbar
-        data-key="ItemRequests"
-        url="TicketRequests/filter"
-        :label="t('globals.search')"
-        :info="t('You can search by Id or alias')"
-        :redirect="false"
-    />
-    <RightMenu>
-        <template #right-panel>
-            <ItemRequestFilter data-key="ItemRequests" />
-        </template>
-    </RightMenu>
-    <QPage class="column items-center q-pa-md">
-        <QTable
-            :rows="itemRequestsOptions"
-            :columns="columns"
-            row-key="id"
-            :pagination="{ rowsPerPage: 0 }"
-            class="full-width q-mt-md"
-            :no-data-label="t('globals.noResults')"
-        >
-            <template #body-cell-id="{ row }">
-                <QTd>
-                    <QBtn flat color="primary"> {{ row.ticketFk }}</QBtn>
-                    <TicketDescriptorProxy :id="row.ticketFk" />
-                </QTd>
-            </template>
-            <template #body-cell-shipped="{ row }">
-                <QTd>
-                    <QBadge
-                        v-if="getDateQBadgeColor(row.shipped)"
-                        :color="getDateQBadgeColor(row.shipped)"
-                        text-color="black"
-                        class="q-ma-none"
-                        dense
-                        style="font-size: 14px"
-                    >
-                        {{ toDateFormat(row.shipped) }}
-                    </QBadge>
-                    <span v-else>{{ toDateFormat(row.shipped) }}</span>
-                </QTd>
-            </template>
-            <template #body-cell-requester="{ row }">
-                <QTd>
-                    <QBtn flat dense color="primary"> {{ row.requesterName }}</QBtn>
-                    <WorkerDescriptorProxy :id="row.requesterFk" />
-                </QTd>
-            </template>
-            <template #body-cell-attender="{ row }">
-                <QTd>
-                    <VnSelect
-                        v-model="row.attenderFk"
-                        :options="workersOptions"
-                        hide-selected
-                        option-label="firstName"
-                        option-value="id"
-                        dense
-                    />
-                </QTd>
-            </template>
-            <template #body-cell-item="{ row }">
-                <QTd>
-                    <VnInput
-                        v-model.number="row.itemFk"
-                        type="number"
-                        :disable="row.isOk != null"
-                        dense
-                    />
-                </QTd>
-            </template>
-            <template #body-cell-achieved="{ row }">
-                <QTd>
-                    <VnInput
-                        v-model.number="row.saleQuantity"
-                        @blur="changeQuantity(row)"
-                        type="number"
-                        :disable="!row.itemFk || row.isOk != null"
-                        dense
-                    />
-                </QTd>
-            </template>
-            <template #body-cell-concept="{ row }">
-                <QTd>
-                    <QBtn flat dense color="primary"> {{ row.itemDescription }}</QBtn>
-                    <ItemDescriptorProxy :id="row.itemFk" />
-                </QTd>
-            </template>
-            <template #body-cell-state="{ row }">
-                <QTd>
-                    <span>{{ getState(row.isOk) }}</span>
-                </QTd>
-            </template>
-            <template #body-cell-action="{ row, rowIndex }">
-                <QTd>
-                    <QIcon
-                        v-if="row.response?.length"
-                        name="insert_drive_file"
-                        color="primary"
-                        size="sm"
-                    >
-                        <QTooltip>
-                            {{ row.response }}
-                        </QTooltip>
-                    </QIcon>
-                    <QIcon
-                        v-if="row.isOk == null"
-                        name="thumb_down"
-                        color="primary"
-                        size="sm"
-                        class="fill-icon"
-                        @click="showDenyRequestForm(row.id, rowIndex)"
-                    >
-                        <QTooltip>
-                            {{ t('Discard') }}
-                        </QTooltip>
-                    </QIcon>
-                </QTd>
-            </template>
-        </QTable>
-        <QDialog ref="denyFormRef" transition-show="scale" transition-hide="scale">
-            <ItemRequestDenyForm
-                :request-id="denyRequestId"
-                @on-data-saved="onDenyAccept"
-            />
-        </QDialog>
-    </QPage> -->
-    <VnSubToolbar />
     <VnTable
         ref="tableRef"
         data-key="itemRequest"
@@ -368,17 +234,19 @@ onBeforeMount(() => {
         </template>
 
         <template #column-item="{ row }">
-            <span @click.stop disabled="row.isOk != null">
-                <VnInput type="number" v-model="row.item" fill-input />
+            <span>
+                <VnInput v-model.number="row.itemFk" dense />
             </span>
         </template>
         <template #column-achieved="{ row }">
-            <span @click.stop disabled="!request.itemFk || request.isOk != null">
+            <span>
                 <VnInput
                     type="number"
-                    v-model="row.achieved"
-                    fill-input
+                    v-model.number="row.saleQuantity"
+                    :disable="!row.itemFk || row.isOk != null"
+                    @blur="changeQuantity(row)"
                     @keyup.enter="changeQuantity(row)"
+                    dense
                 />
             </span>
         </template>

From 3bf3e8eeaa4c93dd2c8a737d5a95fb0528abbe50 Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Mon, 2 Sep 2024 11:08:50 +0200
Subject: [PATCH 025/150] refs #7283 itemRequest fix deny

---
 src/pages/Item/ItemRequest.vue | 17 +++++------------
 1 file changed, 5 insertions(+), 12 deletions(-)

diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue
index 10ed237b4..8273c5c91 100644
--- a/src/pages/Item/ItemRequest.vue
+++ b/src/pages/Item/ItemRequest.vue
@@ -112,16 +112,7 @@ const columns = computed(() => [
     {
         align: 'right',
         label: '',
-        name: 'tableActions',
-        actions: [
-            {
-                title: t('Deny Request'),
-                icon: 'thumb_down',
-                class: 'fill-icon',
-                action: showDenyRequestForm,
-                isPrimary: true,
-            },
-        ],
+        name: 'denyOptions',
     },
 ]);
 
@@ -171,7 +162,9 @@ const getState = (isOk) => {
 
 const showDenyRequestForm = (requestId, rowIndex) => {
     denyRequestId.value = requestId;
+    console.log('denyRequestId.value: ', denyRequestId.value);
     denyRequestIndex.value = rowIndex;
+    console.log('denyRequestIndex.value: ', denyRequestIndex.value);
     denyFormRef.value.show();
 };
 
@@ -255,8 +248,8 @@ onBeforeMount(() => {
                 {{ row.itemDescription }}
             </span>
         </template>
-        <template #column-tableActions="{ row, rowIndex }">
-            <QTd>
+        <template #column-denyOptions="{ row, rowIndex }">
+            <QTd class="sticky no-padding">
                 <QIcon
                     v-if="row.response?.length"
                     name="insert_drive_file"

From 488dc74c1f07c9314dc14618765c7250191ae72d Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Mon, 2 Sep 2024 12:14:23 +0200
Subject: [PATCH 026/150] refs #7283 itemRequest fix reload

---
 src/pages/Item/ItemRequest.vue | 24 +++---------------------
 1 file changed, 3 insertions(+), 21 deletions(-)

diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue
index 8273c5c91..4062f8538 100644
--- a/src/pages/Item/ItemRequest.vue
+++ b/src/pages/Item/ItemRequest.vue
@@ -1,5 +1,5 @@
 <script setup>
-import { ref, computed, onMounted, onBeforeMount, watch } from 'vue';
+import { ref, computed, onMounted, watch } from 'vue';
 import { useI18n } from 'vue-i18n';
 import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
 import { useStateStore } from 'stores/useStateStore';
@@ -11,7 +11,6 @@ import ItemRequestDenyForm from './ItemRequestDenyForm.vue';
 import { toDate } from 'src/filters';
 import VnTable from 'components/VnTable/VnTable.vue';
 import VnInput from 'src/components/common/VnInput.vue';
-import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
 const { t } = useI18n();
 const { notify } = useNotify();
 const stateStore = useStateStore();
@@ -31,6 +30,7 @@ const userParams = {
     state: 'pending',
 };
 
+const tableRef = ref();
 watch(
     () => store.data,
     (value) => (itemRequestsOptions.value = value)
@@ -162,9 +162,7 @@ const getState = (isOk) => {
 
 const showDenyRequestForm = (requestId, rowIndex) => {
     denyRequestId.value = requestId;
-    console.log('denyRequestId.value: ', denyRequestId.value);
     denyRequestIndex.value = rowIndex;
-    console.log('denyRequestIndex.value: ', denyRequestIndex.value);
     denyFormRef.value.show();
 };
 
@@ -172,32 +170,16 @@ const onDenyAccept = (_, responseData) => {
     itemRequestsOptions.value[denyRequestIndex.value].isOk = responseData.isOk;
     itemRequestsOptions.value[denyRequestIndex.value].attenderFk =
         responseData.attenderFk;
-    console.log('itemRequestsOptions: ', itemRequestsOptions.value);
     itemRequestsOptions.value[denyRequestIndex.value].response = responseData.response;
-    console.log('itemRequestsOptions.value', itemRequestsOptions.value);
     denyRequestId.value = null;
     denyRequestIndex.value = null;
+    tableRef.value.reload();
 };
 
 onMounted(async () => {
     await arrayData.fetch({ append: false });
     stateStore.rightDrawer = true;
 });
-
-onBeforeMount(() => {
-    const today = Date.vnNew();
-    today.setHours(0, 0, 0, 0);
-
-    const nextWeek = Date.vnNew();
-    nextWeek.setHours(23, 59, 59, 59);
-    nextWeek.setDate(nextWeek.getDate() + 7);
-
-    filterParams.value = {
-        from: today,
-        to: nextWeek,
-        state: 'pending',
-    };
-});
 </script>
 
 <template>

From 04c8481b51e70f3062d7d0a997bdab2fece7c98a Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Thu, 12 Sep 2024 07:43:12 +0200
Subject: [PATCH 027/150] refs #7283 fix itemFixed

---
 src/pages/Item/ItemFixedPrice.vue | 28 +++++++++++++---------------
 1 file changed, 13 insertions(+), 15 deletions(-)

diff --git a/src/pages/Item/ItemFixedPrice.vue b/src/pages/Item/ItemFixedPrice.vue
index 29b886e25..d91b5189e 100644
--- a/src/pages/Item/ItemFixedPrice.vue
+++ b/src/pages/Item/ItemFixedPrice.vue
@@ -1,6 +1,5 @@
 <script setup>
 import { onMounted, ref, reactive, onUnmounted, nextTick, computed } from 'vue';
-import { ref, reactive, computed, watch } from 'vue';
 import { useI18n } from 'vue-i18n';
 import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
 import FetchedTags from 'components/ui/FetchedTags.vue';
@@ -27,15 +26,12 @@ import { QCheckbox } from 'quasar';
 
 const quasar = useQuasar();
 const stateStore = useStateStore();
-const route = useRoute();
 const { t } = useI18n();
 const { openConfirmationModal } = useVnConfirm();
 const state = useState();
 const { notify } = useNotify();
 const tableRef = ref();
 const editTableCellDialogRef = ref(null);
-const tableRef = ref();
-const quasar = useQuasar();
 const user = state.getUser();
 const fixedPrices = ref([]);
 const warehousesOptions = ref([]);
@@ -129,6 +125,7 @@ const columns = computed(() => [
     },
     {
         label: t('item.fixedPrice.started'),
+        field: 'started',
         name: 'started',
         format: ({ started }) => toDate(started),
         cardVisible: true,
@@ -144,6 +141,7 @@ const columns = computed(() => [
     },
     {
         label: t('item.fixedPrice.ended'),
+        field: 'ended',
         name: 'ended',
         ...defaultColumnAttrs,
         cardVisible: true,
@@ -157,6 +155,7 @@ const columns = computed(() => [
         columnClass: 'expand',
         format: (row) => toDate(row.ended),
     },
+
     {
         label: t('item.fixedPrice.warehouse'),
         field: 'warehouseFk',
@@ -223,18 +222,17 @@ const editTableFieldsOptions = [
         label: t('item.fixedPrice.ended'),
         component: 'date',
     },
-];
-
-function confirmRemove(row) {
-    quasar.dialog({
-        component: VnConfirm,
-        componentProps: {
-            title: t('confirmDeletion'),
-            message: t('confirmDeletionMessage'),
-            promise: () => remove(row),
+    {
+        field: 'warehouseFk',
+        label: t('item.fixedPrice.warehouse'),
+        component: 'select',
+        attrs: {
+            options: [],
+            'option-label': 'name',
+            'option-value': 'id',
         },
-    }),
-};
+    },
+];
 const getRowUpdateInputEvents = (props, resetMinPrice, inputType = 'text') => {
     return inputType === 'text'
         ? {

From 039a8d1d02e1ca30a2dd6701a60b7211b3de01cd Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Thu, 12 Sep 2024 09:32:41 +0200
Subject: [PATCH 028/150] refs #7283 fix items

---
 src/i18n/locale/en.yml           |  2 ++
 src/i18n/locale/es.yml           |  2 ++
 src/pages/Item/Card/ItemCard.vue |  2 +-
 src/pages/Item/ItemList.vue      | 49 ++++++++++++--------------------
 src/pages/Item/ItemRequest.vue   |  2 +-
 5 files changed, 24 insertions(+), 33 deletions(-)

diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index 6546b78c4..37d1e3726 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -1022,6 +1022,8 @@ travel:
         travelFileDescription: 'Travel id { travelId }'
         file: File
 item:
+    searchbar:
+        label: Search item
     descriptor:
         item: Item
         buyer: Buyer
diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml
index 79e66b5f7..0c0447b78 100644
--- a/src/i18n/locale/es.yml
+++ b/src/i18n/locale/es.yml
@@ -1007,6 +1007,8 @@ travel:
         travelFileDescription: 'Id envío { travelId }'
         file: Fichero
 item:
+    searchbar:
+        label: Buscar artículo
     descriptor:
         item: Artículo
         buyer: Comprador
diff --git a/src/pages/Item/Card/ItemCard.vue b/src/pages/Item/Card/ItemCard.vue
index 1162327c1..4cf1372cc 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: 'item.searchbar.label',
             info: 'searchbar.info',
         }"
     />
diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index 2f3d20390..c31302bba 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -44,25 +44,24 @@ const itemFilter = {
     ],
 };
 const columns = computed(() => [
-    // {
-    //     label: '',
-    //     name: 'image',
-    //     align: 'left',
-    //     columnField: {
-    //         component: VnImg,
-    //         attrs: (id) => {
-    //             return {
-    //                 id,
-    //                 width: '50px',
-    //             };
-    //         },
-    //     },
-    //     columnFilter: false,
-    // },
+    {
+        label: '',
+        name: 'image',
+        align: 'left',
+        columnField: {
+            component: VnImg,
+            attrs: (id) => {
+                return {
+                    id,
+                    width: '50px',
+                };
+            },
+        },
+        columnFilter: false,
+    },
     {
         label: t('item.list.id'),
         name: 'id',
-        field: 'id',
         align: 'left',
         isId: true,
         chip: {
@@ -71,38 +70,35 @@ const columns = computed(() => [
     },
     {
         label: t('item.list.grouping'),
-        field: 'grouping',
         name: 'grouping',
         align: 'left',
     },
     {
         label: t('item.list.packing'),
-        field: 'packing',
         name: 'packing',
         align: 'left',
     },
     {
         label: t('globals.description'),
-        field: 'name',
         name: 'description',
         align: 'left',
         create: true,
+        columnFilter: {
+            name: 'description',
+        },
     },
     {
         label: t('item.list.stems'),
-        field: 'stems',
         name: 'stems',
         align: 'left',
     },
     {
         label: t('item.list.size'),
-        field: 'size',
         name: 'size',
         align: 'left',
     },
     {
         label: t('item.list.typeName'),
-        field: 'typeName',
         name: 'typeName',
         align: 'left',
         component: 'select',
@@ -117,7 +113,6 @@ const columns = computed(() => [
     },
     {
         label: t('item.list.category'),
-        field: 'category',
         name: 'category',
         align: 'left',
         component: 'select',
@@ -131,7 +126,6 @@ const columns = computed(() => [
     },
     {
         label: t('item.list.intrastat'),
-        field: 'intrastat',
         name: 'intrastat',
         align: 'left',
         component: 'select',
@@ -146,7 +140,6 @@ const columns = computed(() => [
     },
     {
         label: t('item.list.origin'),
-        field: 'origin',
         name: 'origin',
         align: 'left',
         component: 'select',
@@ -161,13 +154,11 @@ const columns = computed(() => [
     },
     {
         label: t('item.list.userName'),
-        field: 'userName',
         name: 'userName',
         align: 'left',
     },
     {
         label: t('item.list.weightByPiece'),
-        field: 'weightByPiece',
         name: 'weightByPiece',
         align: 'left',
         component: 'input',
@@ -177,7 +168,6 @@ const columns = computed(() => [
     },
     {
         label: t('item.list.stemMultiplier'),
-        field: 'stemMultiplier',
         name: 'stemMultiplier',
         align: 'left',
         component: 'input',
@@ -187,14 +177,12 @@ const columns = computed(() => [
     },
     {
         label: t('item.list.isActive'),
-        field: 'isActive',
         name: 'isActive',
         align: 'left',
         component: 'checkbox',
     },
     {
         label: t('item.list.producer'),
-        field: 'producer',
         name: 'producer',
         align: 'left',
         component: 'select',
@@ -208,7 +196,6 @@ const columns = computed(() => [
     },
     {
         label: t('item.list.landed'),
-        field: 'landed',
         name: 'landed',
         align: 'left',
         component: 'date',
diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue
index be0a9c2e8..4bf45fe4c 100644
--- a/src/pages/Item/ItemRequest.vue
+++ b/src/pages/Item/ItemRequest.vue
@@ -188,7 +188,7 @@ onMounted(async () => {
         ref="tableRef"
         data-key="itemRequest"
         url="ticketRequests/filter"
-        order="shippedDate ASC, isOk ASC"
+        order="shipped ASC, isOk ASC"
         :columns="columns"
         :user-params="userParams"
         :is-editable="true"

From 68a2ac385f6638d0b23168f085984b2d8b40d313 Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Thu, 12 Sep 2024 11:36:02 +0200
Subject: [PATCH 029/150] refs #7283 fix items images

---
 src/pages/Item/ItemList.vue | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index c31302bba..ca00a6502 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -50,9 +50,9 @@ const columns = computed(() => [
         align: 'left',
         columnField: {
             component: VnImg,
-            attrs: (id) => {
+            attrs: ({ row }) => {
                 return {
-                    id,
+                    id: row.id,
                     width: '50px',
                 };
             },
@@ -102,9 +102,11 @@ const columns = computed(() => [
         name: 'typeName',
         align: 'left',
         component: 'select',
-        attrs: {
-            url: 'ItemTypes',
-            fields: ['id', 'name'],
+        columnFilter: {
+            attrs: {
+                url: 'ItemTypes',
+                fields: ['name'],
+            },
         },
         columnField: {
             component: null,

From 050d8ae208329f51e38f3e3acb79fe4c8e8a991a Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Thu, 12 Sep 2024 11:43:37 +0200
Subject: [PATCH 030/150] refs #7283 fix items error get images

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

diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index ca00a6502..bed30725b 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -52,7 +52,7 @@ const columns = computed(() => [
             component: VnImg,
             attrs: ({ row }) => {
                 return {
-                    id: row.id,
+                    id: row?.id,
                     width: '50px',
                 };
             },

From 1916c6e4bf0b3b161b9c1ccaf6d554d5e5c2c83b Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Mon, 16 Sep 2024 14:29:39 +0200
Subject: [PATCH 031/150] refs #7283 itemFilters

---
 src/pages/Item/ItemList.vue    | 50 ++++++++++++++++++++++++----------
 src/pages/Item/ItemRequest.vue | 11 ++++++--
 2 files changed, 45 insertions(+), 16 deletions(-)

diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index bed30725b..b6668edea 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -84,7 +84,7 @@ const columns = computed(() => [
         align: 'left',
         create: true,
         columnFilter: {
-            name: 'description',
+            name: 'search',
         },
     },
     {
@@ -103,9 +103,10 @@ const columns = computed(() => [
         align: 'left',
         component: 'select',
         columnFilter: {
+            name: 'typeFk',
             attrs: {
                 url: 'ItemTypes',
-                fields: ['name'],
+                fields: ['id', 'name'],
             },
         },
         columnField: {
@@ -118,9 +119,12 @@ const columns = computed(() => [
         name: 'category',
         align: 'left',
         component: 'select',
-        attrs: {
-            url: 'ItemCategories',
-            fields: ['id', 'name'],
+        columnFilter: {
+            name: 'categoryFk',
+            attrs: {
+                url: 'ItemCategories',
+                fields: ['id', 'name'],
+            },
         },
         columnField: {
             component: null,
@@ -131,9 +135,15 @@ const columns = computed(() => [
         name: 'intrastat',
         align: 'left',
         component: 'select',
-        attrs: {
-            url: 'Intrastats',
-            fields: ['id', 'description'],
+        columnFilter: {
+            name: 'description',
+            attrs: {
+                url: 'Intrastats',
+                optionValue: 'description',
+                optionLabel: 'description',
+            },
+            inWhere: true,
+            alias: 'intr',
         },
         columnField: {
             component: null,
@@ -145,9 +155,15 @@ const columns = computed(() => [
         name: 'origin',
         align: 'left',
         component: 'select',
-        attrs: {
-            url: 'Origins',
-            fields: ['id', 'name'],
+        columnFilter: {
+            name: 'id',
+            attrs: {
+                url: 'Origins',
+                optionValue: 'id',
+                optionLabel: 'code',
+            },
+            inWhere: true,
+            alias: 'ori',
         },
         columnField: {
             component: null,
@@ -158,6 +174,14 @@ const columns = computed(() => [
         label: t('item.list.userName'),
         name: 'userName',
         align: 'left',
+        columnFilter: {
+            name: 'workerFk',
+            attrs: {
+                url: 'Users',
+                optionValue: 'id',
+                optionLabel: 'userName',
+            },
+        },
     },
     {
         label: t('item.list.weightByPiece'),
@@ -249,8 +273,6 @@ const cloneItem = async (itemFk) => {
         data-key="ItemList"
         url="Items/filter"
         url-create="Items"
-        save-url="Items/crud"
-        :filter="itemFilter"
         :create="{
             urlCreate: 'Items',
             title: 'Create Item',
@@ -264,7 +286,7 @@ const cloneItem = async (itemFk) => {
         auto-load
         redirect="Item"
         :is-editable="false"
-        :use-model="true"
+        :filer="itemFilter"
     >
         <template #column-userName="{ row }">
             <span class="link" @click.stop>
diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue
index 4bf45fe4c..1a1d98211 100644
--- a/src/pages/Item/ItemRequest.vue
+++ b/src/pages/Item/ItemRequest.vue
@@ -57,45 +57,51 @@ const columns = computed(() => [
             component: null,
         },
         format: (row, dashIfEmpty) => dashIfEmpty(toDate(row.shipped)),
+        columnClass: 'shrink',
     },
     {
         label: t('globals.description'),
         field: 'description',
         name: 'description',
         align: 'left',
+        columnClass: 'expand',
     },
     {
         label: t('item.buyRequest.requester'),
         name: 'requesterName',
-        align: 'left',
+        columnClass: 'shrink',
     },
     {
         label: t('item.buyRequest.requested'),
         name: 'quantity',
-        align: 'left',
+        columnClass: 'shrink',
     },
     {
         label: t('item.buyRequest.price'),
         name: 'price',
         align: 'left',
         format: (row) => toCurrency(row.price),
+        columnClass: 'shrink',
     },
     {
         label: t('item.buyRequest.attender'),
         name: 'attenderName',
         align: 'left',
+        columnClass: 'shrink',
     },
     {
         label: t('item.buyRequest.item'),
         name: 'item',
         align: 'left',
         component: 'input',
+        columnClass: 'expand',
     },
     {
         label: t('item.buyRequest.achieved'),
         name: 'achieved',
         align: 'left',
         component: 'input',
+        columnClass: 'shrink',
     },
     {
         label: t('item.buyRequest.concept'),
@@ -103,6 +109,7 @@ const columns = computed(() => [
         align: 'left',
         sortable: true,
         component: 'input',
+        columnClass: 'expand',
     },
     {
         label: t('item.buyRequest.state'),

From db783e15384163fdb7221afae2fc4e509e0c4c52 Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Tue, 17 Sep 2024 15:03:30 +0200
Subject: [PATCH 032/150] refs #7283 item Descriptor

---
 src/composables/useArrayData.js             |   1 +
 src/pages/Item/Card/ItemDescriptor.vue      |  70 ++++++-------
 src/pages/Item/Card/ItemDescriptorImage.vue |   8 +-
 src/pages/Item/Card/ItemDiary.vue           | 110 +++++++++++---------
 4 files changed, 98 insertions(+), 91 deletions(-)

diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js
index 651bcefb0..6671632b3 100644
--- a/src/composables/useArrayData.js
+++ b/src/composables/useArrayData.js
@@ -246,6 +246,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
     }
 
     function updateStateParams() {
+        if (!route) return;
         const newUrl = { path: route.path, query: { ...(route.query ?? {}) } };
         newUrl.query[store.searchUrl] = JSON.stringify(store.currentFilter);
 
diff --git a/src/pages/Item/Card/ItemDescriptor.vue b/src/pages/Item/Card/ItemDescriptor.vue
index baac0c608..a14ee1446 100644
--- a/src/pages/Item/Card/ItemDescriptor.vue
+++ b/src/pages/Item/Card/ItemDescriptor.vue
@@ -14,6 +14,7 @@ import useCardDescription from 'src/composables/useCardDescription';
 import { getUrl } from 'src/composables/getUrl';
 import axios from 'axios';
 import { dashIfEmpty } from 'src/filters';
+import { useArrayData } from 'src/composables/useArrayData';
 
 const $props = defineProps({
     id: {
@@ -49,58 +50,49 @@ const entityId = computed(() => {
 });
 
 const regularizeStockFormDialog = ref(null);
-const available = ref(null);
-const visible = ref(null);
 const salixUrl = ref();
+const mounted = ref();
+
+const arrayDataStock = useArrayData('descriptorStock', {
+    url: `Items/${entityId.value}/getVisibleAvailable`,
+});
 
 onMounted(async () => {
-    salixUrl.value = await getUrl('');
+    salixUrl.value = await getUrl('getVisibleAvailable');
     await getItemConfigs();
-    await updateStock();
+    mounted.value = true;
 });
 
 const data = ref(useCardDescription());
 const setData = async (entity) => {
-    try {
-        if (!entity) return;
-        data.value = useCardDescription(entity.name, entity.id);
-        await updateStock();
-    } catch (err) {
-        console.error('Error item');
-    }
+    if (!entity) return;
+    data.value = useCardDescription(entity.name, entity.id);
+    await updateStock();
 };
 
 const getItemConfigs = async () => {
-    try {
-        const { data } = await axios.get('ItemConfigs/findOne');
-        if (!data) return;
-        return (warehouseConfig.value = data.warehouseFk);
-    } catch (err) {
-        console.error('Error item');
-    }
+    const { data } = await axios.get('ItemConfigs/findOne');
+    if (!data) return;
+    return (warehouseConfig.value = data.warehouseFk);
 };
 const updateStock = async () => {
-    try {
-        available.value = null;
-        visible.value = null;
+    if (!mounted.value) return;
+    await getItemConfigs();
 
-        const params = {
-            warehouseFk: $props.warehouseFk,
-            dated: $props.dated,
-        };
+    const params = {
+        warehouseFk: $props.warehouseFk ?? warehouseConfig.value,
+        dated: $props.dated,
+    };
 
-        await getItemConfigs();
-        if (!params.warehouseFk) {
-            params.warehouseFk = warehouseConfig.value;
-        }
-        const { data } = await axios.get(`Items/${entityId.value}/getVisibleAvailable`, {
-            params,
-        });
-        available.value = data.available;
-        visible.value = data.visible;
-    } catch (err) {
-        console.error('Error updating stock');
-    }
+    if (!params.warehouseFk) return;
+
+    const stock = useArrayData('descriptorStock', {
+        url: `Items/${entityId.value}/getVisibleAvailable`,
+        userParams: params,
+    });
+    const storeData = stock.store.data;
+    if (storeData?.itemFk == entityId.value) return;
+    await stock.fetch({});
 };
 
 const openRegularizeStockForm = () => {
@@ -163,8 +155,8 @@ const openCloneDialog = async () => {
         <template #before>
             <ItemDescriptorImage
                 :entity-id="entityId"
-                :visible="visible"
-                :available="available"
+                :visible="arrayDataStock.store.data?.visible"
+                :available="arrayDataStock.store.data?.available"
             />
         </template>
         <template #body="{ entity }">
diff --git a/src/pages/Item/Card/ItemDescriptorImage.vue b/src/pages/Item/Card/ItemDescriptorImage.vue
index a4ef22ce3..b035a630a 100644
--- a/src/pages/Item/Card/ItemDescriptorImage.vue
+++ b/src/pages/Item/Card/ItemDescriptorImage.vue
@@ -32,6 +32,10 @@ const editPhotoFormDialog = ref(null);
 const showEditPhotoForm = ref(false);
 const warehouseName = ref(null);
 
+onMounted(async () => {
+    getItemConfigs();
+});
+
 const toggleEditPictureForm = () => {
     showEditPhotoForm.value = !showEditPhotoForm.value;
 };
@@ -56,10 +60,6 @@ const getWarehouseName = async (warehouseFk) => {
     warehouseName.value = data.name;
 };
 
-onMounted(async () => {
-    getItemConfigs();
-});
-
 const handlePhotoUpdated = (evt = false) => {
     image.value.reload(evt);
 };
diff --git a/src/pages/Item/Card/ItemDiary.vue b/src/pages/Item/Card/ItemDiary.vue
index 68633caa2..a36b6a246 100644
--- a/src/pages/Item/Card/ItemDiary.vue
+++ b/src/pages/Item/Card/ItemDiary.vue
@@ -17,6 +17,7 @@ import { toDateFormat } from 'src/filters/date.js';
 import { dashIfEmpty } from 'src/filters';
 import { date } from 'quasar';
 import { useState } from 'src/composables/useState';
+import { useArrayData } from 'src/composables/useArrayData';
 import axios from 'axios';
 
 const { t } = useI18n();
@@ -37,6 +38,33 @@ const warehouseFk = ref(null);
 const _showWhatsBeforeInventory = ref(false);
 const inventoriedDate = ref(null);
 
+const originTypeMap = {
+    entry: {
+        descriptor: EntryDescriptorProxy,
+        icon: 'vn:entry',
+        color: 'green',
+    },
+    ticket: {
+        descriptor: TicketDescriptorProxy,
+        icon: 'vn:ticket',
+        color: 'red',
+    },
+    order: {
+        descriptor: OrderDescriptorProxy,
+        icon: 'vn:basket',
+        color: 'yellow',
+    },
+};
+
+const entityTypeMap = {
+    client: {
+        descriptor: CustomerDescriptorProxy,
+    },
+    supplier: {
+        descriptor: SupplierDescriptorProxy,
+    },
+};
+
 const columns = computed(() => [
     {
         name: 'claim',
@@ -105,6 +133,28 @@ const showWhatsBeforeInventory = computed({
     },
 });
 
+onMounted(async () => {
+    today.value.setHours(0, 0, 0, 0);
+    if (route.query.warehouseFk) warehouseFk.value = route.query.warehouseFk;
+    else if (user.value) warehouseFk.value = user.value.warehouseFk;
+    itemsBalanceFilter.where.warehouseFk = warehouseFk.value;
+    const { data } = await axios.get('Configs/findOne');
+    inventoriedDate.value = data.inventoried;
+    await fetchItemBalances();
+    await scrollToToday();
+    await updateWarehouse(warehouseFk.value);
+});
+
+onUnmounted(() => (stateStore.rightDrawer = false));
+
+watch(
+    () => router.currentRoute.value.params.id,
+    (newId) => {
+        itemsBalanceFilter.where.itemFk = newId;
+        itemBalancesRef.value.fetch();
+    }
+);
+
 const fetchItemBalances = async () => await itemBalancesRef.value.fetch();
 
 const getBadgeAttrs = (_date) => {
@@ -131,53 +181,15 @@ const formatDateForAttribute = (dateValue) => {
     return dateValue;
 };
 
-const originTypeMap = {
-    entry: {
-        descriptor: EntryDescriptorProxy,
-        icon: 'vn:entry',
-        color: 'green',
-    },
-    ticket: {
-        descriptor: TicketDescriptorProxy,
-        icon: 'vn:ticket',
-        color: 'red',
-    },
-    order: {
-        descriptor: OrderDescriptorProxy,
-        icon: 'vn:basket',
-        color: 'yellow',
-    },
-};
-
-const entityTypeMap = {
-    client: {
-        descriptor: CustomerDescriptorProxy,
-    },
-    supplier: {
-        descriptor: SupplierDescriptorProxy,
-    },
-};
-
-onMounted(async () => {
-    today.value.setHours(0, 0, 0, 0);
-    if (route.query.warehouseFk) warehouseFk.value = route.query.warehouseFk;
-    else if (user.value) warehouseFk.value = user.value.warehouseFk;
-    itemsBalanceFilter.where.warehouseFk = warehouseFk.value;
-    const { data } = await axios.get('Configs/findOne');
-    inventoriedDate.value = data.inventoried;
-    await fetchItemBalances();
-    await scrollToToday();
-});
-
-onUnmounted(() => (stateStore.rightDrawer = false));
-
-watch(
-    () => router.currentRoute.value.params.id,
-    (newId) => {
-        itemsBalanceFilter.where.itemFk = newId;
-        itemBalancesRef.value.fetch();
-    }
-);
+async function updateWarehouse(warehouseFk) {
+    const stock = useArrayData('descriptorStock', {
+        userParams: {
+            warehouseFk,
+        },
+    });
+    await stock.fetch({});
+    stock.store.data.itemFk = route.params.id
+}
 </script>
 
 <template>
@@ -203,7 +215,9 @@ watch(
                 option-value="id"
                 dense
                 v-model="itemsBalanceFilter.where.warehouseFk"
-                @update:model-value="fetchItemBalances"
+                @update:model-value="
+                    (value) => fetchItemBalances() && updateWarehouse(value)
+                "
                 class="q-mr-lg"
             />
             <QCheckbox

From 716d018121026767ecd2e9b5b1f47dffa349a028 Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Fri, 20 Sep 2024 14:20:26 +0200
Subject: [PATCH 033/150] refs #72983 fix filters

---
 src/pages/Item/ItemList.vue | 17 +++++++++++++++++
 1 file changed, 17 insertions(+)

diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index b6668edea..48fe3280e 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -54,6 +54,7 @@ const columns = computed(() => [
                 return {
                     id: row?.id,
                     width: '50px',
+                    zoomResolution: '1600x900',
                 };
             },
         },
@@ -72,11 +73,19 @@ const columns = computed(() => [
         label: t('item.list.grouping'),
         name: 'grouping',
         align: 'left',
+        columnFilter: {
+            component: 'number',
+            inWhere: true,
+        },
     },
     {
         label: t('item.list.packing'),
         name: 'packing',
         align: 'left',
+        columnFilter: {
+            component: 'number',
+            inWhere: true,
+        },
     },
     {
         label: t('globals.description'),
@@ -91,11 +100,19 @@ const columns = computed(() => [
         label: t('item.list.stems'),
         name: 'stems',
         align: 'left',
+        columnFilter: {
+            component: 'number',
+            inWhere: true,
+        },
     },
     {
         label: t('item.list.size'),
         name: 'size',
         align: 'left',
+        columnFilter: {
+            component: 'number',
+            inWhere: true,
+        },
     },
     {
         label: t('item.list.typeName'),

From 54015fb6bfbf975a7fed3150cf82de904df56d63 Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Tue, 1 Oct 2024 08:44:08 +0200
Subject: [PATCH 034/150] refs #7283 fix itemMigration

---
 src/pages/Item/ItemRequest.vue | 20 ++++++++++++++++++++
 1 file changed, 20 insertions(+)

diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue
index 1a1d98211..7fbeccbf9 100644
--- a/src/pages/Item/ItemRequest.vue
+++ b/src/pages/Item/ItemRequest.vue
@@ -117,6 +117,15 @@ const columns = computed(() => [
         format: (row) => getState(row.isOk),
         align: 'left',
     },
+    {
+        align: 'left',
+        name: 'daysOnward',
+        label: t('item.buyRequest.daysOnward'),
+        visible: false,
+        columnFilter: {
+            inWhere: false,
+        },
+    },
     {
         align: 'right',
         label: '',
@@ -238,6 +247,17 @@ onMounted(async () => {
                 {{ row.itemDescription }}
             </span>
         </template>
+        <template #moreFilterPanel="{ params }">
+            <VnInputNumber
+                :label="t('params.scopeDays')"
+                v-model.number="params.scopeDays"
+                @keyup.enter="(evt) => handleScopeDays(evt.target.value)"
+                @remove="handleScopeDays()"
+                class="q-px-xs q-pr-lg"
+                filled
+                dense
+            />
+        </template>
         <template #column-denyOptions="{ row, rowIndex }">
             <QTd class="sticky no-padding">
                 <QIcon

From 55ddc8644f0450091f49ce96d4ef1ad2d3988a68 Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Wed, 2 Oct 2024 12:51:33 +0200
Subject: [PATCH 035/150] refs #7283 fix itemMigration list filters

---
 src/pages/Item/ItemList.vue    | 8 +++++++-
 src/pages/Item/ItemRequest.vue | 3 ++-
 2 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index 48fe3280e..ad2c2e238 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -208,6 +208,9 @@ const columns = computed(() => [
         columnField: {
             component: null,
         },
+        columnFilter: {
+            inWhere: true,
+        },
     },
     {
         label: t('item.list.stemMultiplier'),
@@ -217,11 +220,14 @@ const columns = computed(() => [
         columnField: {
             component: null,
         },
+        columnFilter: {
+            inWhere: true,
+        },
     },
     {
         label: t('item.list.isActive'),
         name: 'isActive',
-        align: 'left',
+        align: 'center',
         component: 'checkbox',
     },
     {
diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue
index 7fbeccbf9..0eba6f9a4 100644
--- a/src/pages/Item/ItemRequest.vue
+++ b/src/pages/Item/ItemRequest.vue
@@ -29,6 +29,7 @@ const store = arrayData.store;
 
 const userParams = {
     state: 'pending',
+    daysOnward: 7,
 };
 
 const tableRef = ref();
@@ -120,7 +121,7 @@ const columns = computed(() => [
     {
         align: 'left',
         name: 'daysOnward',
-        label: t('item.buyRequest.daysOnward'),
+        label: t('travel.travelList.tableVisibleColumns.daysOnward'),
         visible: false,
         columnFilter: {
             inWhere: false,

From 419d3d2d45b16d268da452d06fbc1aadfeb03707 Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Thu, 10 Oct 2024 07:20:33 +0200
Subject: [PATCH 036/150] fix: refs #6896 fixed module problems

---
 src/components/ui/VnFilterPanel.vue          | 12 ++-
 src/components/ui/VnSearchbar.vue            |  2 +
 src/i18n/locale/en.yml                       |  2 +
 src/i18n/locale/es.yml                       |  4 +-
 src/pages/Order/Card/OrderCatalog.vue        |  6 +-
 src/pages/Order/Card/OrderCatalogFilter.vue  | 88 +++++++-------------
 src/pages/Order/Card/OrderDescriptor.vue     | 10 +--
 src/pages/Order/Card/OrderDescriptorMenu.vue |  7 +-
 src/pages/Order/Card/OrderLines.vue          |  3 +-
 src/pages/Order/Card/OrderSummary.vue        | 27 ++++++
 10 files changed, 87 insertions(+), 74 deletions(-)

diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue
index 43d634ad9..242c4220d 100644
--- a/src/components/ui/VnFilterPanel.vue
+++ b/src/components/ui/VnFilterPanel.vue
@@ -185,6 +185,9 @@ async function remove(key) {
 }
 
 function formatValue(value) {
+    if (typeof value === 'object') {
+        return value;
+    }
     if (typeof value === 'boolean') return value ? t('Yes') : t('No');
     if (isNaN(value) && !isNaN(Date.parse(value))) return toDate(value);
 
@@ -193,6 +196,13 @@ function formatValue(value) {
 
 function sanitizer(params) {
     for (const [key, value] of Object.entries(params)) {
+        if (key == 'and') {
+            value.forEach((andValue) => {
+                params = { ...params, ...andValue };
+            });
+            delete params[key];
+        }
+
         if (value && typeof value === 'object') {
             const param = Object.values(value)[0];
             if (typeof param == 'string') params[key] = param.replaceAll('%', '');
@@ -211,7 +221,7 @@ function sanitizer(params) {
         icon="search"
         @click="search()"
     ></QBtn>
-    <QForm @submit="search" id="filterPanelForm" @keyup.enter="search()">
+    <QForm @submit="search(true)" id="filterPanelForm" @keyup.enter="search(true)">
         <QList dense>
             <QItem class="q-mt-xs">
                 <QItemSection top>
diff --git a/src/components/ui/VnSearchbar.vue b/src/components/ui/VnSearchbar.vue
index dc6d4751c..fc292e980 100644
--- a/src/components/ui/VnSearchbar.vue
+++ b/src/components/ui/VnSearchbar.vue
@@ -9,6 +9,7 @@ import { useStateStore } from 'src/stores/useStateStore';
 const quasar = useQuasar();
 const { t } = useI18n();
 const state = useStateStore();
+const emit = defineEmits(['on-search']);
 
 const props = defineProps({
     dataKey: {
@@ -118,6 +119,7 @@ async function search() {
         delete filter.params.search;
     }
     await arrayData.applyFilter(filter);
+    emit('on-search', store.data);
 }
 </script>
 <template>
diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index 6235447fe..e24926092 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -705,6 +705,8 @@ order:
         quantity: Quantity
         price: Price
         amount: Amount
+        confirm: Confirm
+        confirmLines: Confirm lines
 department:
     pageTitles:
         basicData: Basic data
diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml
index 6076af5d9..648868fcb 100644
--- a/src/i18n/locale/es.yml
+++ b/src/i18n/locale/es.yml
@@ -679,13 +679,15 @@ order:
         vat: IVA
         state: Estado
         alias: Alias
-        items: Items
+        items: Artículos
         orderTicketList: Tickets del pedido
         details: Detalles
         item: Item
         quantity: Cantidad
         price: Precio
         amount: Monto
+        confirm: Confirmar
+        confirmLines: Confirmar lineas
 shelving:
     list:
         parking: Parking
diff --git a/src/pages/Order/Card/OrderCatalog.vue b/src/pages/Order/Card/OrderCatalog.vue
index b13e8661d..110ea48af 100644
--- a/src/pages/Order/Card/OrderCatalog.vue
+++ b/src/pages/Order/Card/OrderCatalog.vue
@@ -86,11 +86,8 @@ function extractValueTags(items) {
         <div class="full-width">
             <VnPaginate
                 data-key="OrderCatalogList"
-                url="Orders/CatalogFilter"
-                :limit="50"
-                :user-params="catalogParams"
-                @on-fetch="extractTags"
                 :update-router="false"
+                @on-change="extractTags"
             >
                 <template #body="{ rows }">
                     <div class="catalog-list">
@@ -102,6 +99,7 @@ function extractValueTags(items) {
                             :key="row.id"
                             :item="row"
                             is-catalog
+                            class="fill-icon"
                         />
                     </div>
                 </template>
diff --git a/src/pages/Order/Card/OrderCatalogFilter.vue b/src/pages/Order/Card/OrderCatalogFilter.vue
index 6de43e86a..9c713e77e 100644
--- a/src/pages/Order/Card/OrderCatalogFilter.vue
+++ b/src/pages/Order/Card/OrderCatalogFilter.vue
@@ -1,12 +1,11 @@
 <script setup>
-import { computed, ref } from 'vue';
+import { computed, ref, onMounted } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useRoute } from 'vue-router';
 import axios from 'axios';
 import FetchData from 'components/FetchData.vue';
 import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
 import VnSelect from 'components/common/VnSelect.vue';
-import VnFilterPanelChip from 'components/ui/VnFilterPanelChip.vue';
 import VnInput from 'src/components/common/VnInput.vue';
 import getParamWhere from 'src/filters/getParamWhere';
 
@@ -48,6 +47,15 @@ const orderWayList = ref([
 const orderBySelected = ref('relevancy DESC, name');
 const orderWaySelected = ref('ASC');
 
+const routeQuery = JSON.parse(route?.query.params ?? '{}');
+const params = ref({});
+
+onMounted(() => {
+    const filter = routeQuery.filter;
+    params.value = JSON.parse(filter ?? '{}')?.where ?? {};
+    if (Object.keys(params.value).length > 0) vnFilterPanelRef.value.search();
+});
+
 const createValue = (val, done) => {
     if (val.length > 2) {
         if (!tagOptions.value.includes(val)) {
@@ -61,19 +69,15 @@ const resetCategory = () => {
     typeList.value = null;
 };
 
-const clearFilter = (key) => {
-    if (key === 'categoryFk') {
+const selectCategory = (category, search) => {
+    if (!params.value?.filter) params.value.filter = { where: {} };
+    const where = params.value.filter.where;
+    if (where.categoryFk === category?.id) {
         resetCategory();
-    }
-};
-
-const selectCategory = (params, category, search) => {
-    if (params.categoryFk === category?.id) {
-        resetCategory();
-        params.categoryFk = null;
+        where.categoryFk = null;
     } else {
         selectedCategoryFk.value = category?.id;
-        params.categoryFk = category?.id;
+        where.categoryFk = category?.id;
         loadTypes(category?.id);
     }
     search();
@@ -103,17 +107,6 @@ const selectedType = computed(() => {
     return (typeList.value || []).find((type) => type?.id === selectedTypeFk.value);
 });
 
-function exprBuilder(param, value) {
-    switch (param) {
-        case 'categoryFk':
-        case 'typeFk':
-            return { [param]: value };
-        case 'search':
-            if (/^\d+$/.test(value)) return { 'i.id': value };
-            else return { 'i.name': { like: `%${value}%` } };
-    }
-}
-
 const applyTagFilter = (params, search) => {
     if (!tagValues.value?.length) {
         params.tagGroups = null;
@@ -138,15 +131,6 @@ const applyTagFilter = (params, search) => {
     tagValues.value = [{}];
 };
 
-const removeTagChip = (selection, params, search) => {
-    if (params.tagGroups) {
-        params.tagGroups = (params.tagGroups || []).filter(
-            (value) => value !== selection
-        );
-    }
-    search();
-};
-
 const setCategoryList = (data) => {
     categoryList.value = (data || [])
         .filter((category) => category.display)
@@ -178,12 +162,11 @@ function addOrder(value, field, params) {
     <VnFilterPanel
         ref="vnFilterPanelRef"
         :data-key="props.dataKey"
-        :hidden-tags="['orderFk', 'orderBy']"
-        :un-removable-params="['orderFk', 'orderBy']"
-        :expr-builder="exprBuilder"
-        :custom-tags="['tagGroups']"
-        @remove="clearFilter"
+        v-model="params"
         :redirect="false"
+        :hidden-tags="['orderFk', 'orderBy', 'filter', 'search', 'or', 'and']"
+        :un-removable-params="['orderFk', 'orderBy']"
+        :disable-submit-event="true"
     >
         <template #tags="{ tag, formatFn }">
             <strong v-if="tag.label === 'categoryFk'">
@@ -192,30 +175,17 @@ function addOrder(value, field, params) {
             <strong v-else-if="tag.label === 'typeFk'">
                 {{ t(selectedType?.name || '') }}
             </strong>
+            <div v-else-if="tag.label === 'tagGroups'" class="q-gutter-x-xs">
+                <strong v-if="JSON.parse(tag.value).tagSelection.name"
+                    >{{ JSON.parse(tag.value).tagSelection?.name }}:
+                </strong>
+                <span>{{ JSON.parse(tag.value).values[0].value }}</span>
+            </div>
             <div v-else class="q-gutter-x-xs">
                 <strong>{{ t(`params.${tag.label}`) }}: </strong>
                 <span>{{ formatFn(tag.value) }}</span>
             </div>
         </template>
-        <template #customTags="{ tags: customTags, params, searchFn }">
-            <template v-for="tag in customTags" :key="tag.label">
-                <template v-if="tag.label === 'tagGroups'">
-                    <VnFilterPanelChip
-                        v-for="chip in tag.value"
-                        :key="chip"
-                        removable
-                        @remove="removeTagChip(chip, params, searchFn)"
-                    >
-                        <strong> {{ JSON.parse(chip).tagSelection?.name }}: </strong>
-                        <span>{{
-                            (JSON.parse(chip).values || [])
-                                .map((item) => item.value)
-                                .join(' | ')
-                        }}</span>
-                    </VnFilterPanelChip>
-                </template>
-            </template>
-        </template>
         <template #body="{ params, searchFn }">
             <QItem class="category-filter q-mt-md">
                 <div
@@ -226,7 +196,7 @@ function addOrder(value, field, params) {
                     <QIcon
                         :name="category.icon"
                         class="category-icon"
-                        @click="selectCategory(params, category, searchFn)"
+                        @click="selectCategory(category, searchFn)"
                     >
                         <QTooltip>
                             {{ t(category.name) }}
@@ -320,7 +290,7 @@ function addOrder(value, field, params) {
             >
                 <FetchData
                     v-if="selectedTag"
-                    :url="`Tags/${selectedTag}/filterValue`"
+                    :url="`Tags/${selectedTag.id}/filterValue`"
                     limit="30"
                     auto-load
                     @on-fetch="(data) => (tagOptions = data)"
@@ -343,7 +313,7 @@ function addOrder(value, field, params) {
                     @update:model-value="applyTagFilter(params, searchFn)"
                 />
                 <VnSelect
-                    v-else-if="selectedTag === 1"
+                    v-else-if="selectedTag.id === 1"
                     :label="t('params.value')"
                     v-model="value.value"
                     :options="tagOptions || []"
diff --git a/src/pages/Order/Card/OrderDescriptor.vue b/src/pages/Order/Card/OrderDescriptor.vue
index a035971b0..74c8d8bed 100644
--- a/src/pages/Order/Card/OrderDescriptor.vue
+++ b/src/pages/Order/Card/OrderDescriptor.vue
@@ -71,10 +71,6 @@ const getConfirmationValue = (isConfirmed) => {
 };
 
 const total = ref(null);
-
-function ticketFilter(order) {
-    return JSON.stringify({ id: order.id });
-}
 </script>
 
 <template>
@@ -126,7 +122,11 @@ function ticketFilter(order) {
                     color="primary"
                     :to="{
                         name: 'TicketList',
-                        query: { table: ticketFilter(entity) },
+                        query: {
+                            table: JSON.stringify({
+                                orderFk: entity.id,
+                            }),
+                        },
                     }"
                 >
                     <QTooltip>{{ t('order.summary.orderTicketList') }}</QTooltip>
diff --git a/src/pages/Order/Card/OrderDescriptorMenu.vue b/src/pages/Order/Card/OrderDescriptorMenu.vue
index 2695da4e5..461efa22f 100644
--- a/src/pages/Order/Card/OrderDescriptorMenu.vue
+++ b/src/pages/Order/Card/OrderDescriptorMenu.vue
@@ -23,8 +23,8 @@ function confirmRemove() {
         .dialog({
             component: VnConfirm,
             componentProps: {
-                title: t('globals.confirmDeletion'),
-                message: t('confirmDeletionMessage'),
+                title: t('You are going to delete this order'),
+                message: t('Continue anyway?'),
                 promise: remove,
             },
         })
@@ -57,5 +57,6 @@ en:
 es:
     deleteOrder: Eliminar pedido
     confirmDeletionMessage: Seguro que quieres eliminar este pedido?
-
+    You are going to delete this order: El pedido se eliminará
+    Continue anyway?: ¿Continuar de todos modos?
 </i18n>
diff --git a/src/pages/Order/Card/OrderLines.vue b/src/pages/Order/Card/OrderLines.vue
index 175c956e1..0d00e04ed 100644
--- a/src/pages/Order/Card/OrderLines.vue
+++ b/src/pages/Order/Card/OrderLines.vue
@@ -168,7 +168,7 @@ const columns = computed(() => [
         name: 'tableActions',
         actions: [
             {
-                title: t('Delete'),
+                title: t('Remove item'),
                 icon: 'delete',
                 show: (row) => !row.order.isConfirmed,
                 action: (row) => confirmRemove(row),
@@ -397,4 +397,5 @@ es:
     confirmDeletion: Confirmar eliminación,
     confirmDeletionMessage: Seguro que quieres eliminar este artículo?
     confirm: Confirmar
+    Remove item: Eliminar artículo
 </i18n>
diff --git a/src/pages/Order/Card/OrderSummary.vue b/src/pages/Order/Card/OrderSummary.vue
index 60358f744..9bc89ad28 100644
--- a/src/pages/Order/Card/OrderSummary.vue
+++ b/src/pages/Order/Card/OrderSummary.vue
@@ -2,7 +2,10 @@
 import { computed, ref } from 'vue';
 import { useRoute } from 'vue-router';
 import { useI18n } from 'vue-i18n';
+import { useQuasar } from 'quasar';
+import axios from 'axios';
 import { dashIfEmpty, toCurrency, toDateHourMinSec } from 'src/filters';
+import { useArrayData } from 'composables/useArrayData';
 import VnLv from 'components/ui/VnLv.vue';
 import CardSummary from 'components/ui/CardSummary.vue';
 import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
@@ -21,6 +24,9 @@ const $props = defineProps({
 });
 
 const entityId = computed(() => $props.id || route.params.id);
+const summary = ref();
+const quasar = useQuasar();
+const descriptorData = useArrayData('orderData');
 const detailsColumns = ref([
     {
         name: 'item',
@@ -49,6 +55,16 @@ const detailsColumns = ref([
         field: (row) => toCurrency(row?.quantity * row?.price),
     },
 ]);
+
+async function confirmOrder() {
+    await axios.post(`Orders/${route.params.id}/confirm`);
+    quasar.notify({
+        message: t('globals.confirm'),
+        type: 'positive',
+    });
+    summary.value.fetch({});
+    descriptorData.fetch({});
+}
 </script>
 
 <template>
@@ -62,6 +78,17 @@ const detailsColumns = ref([
                 {{ t('order.summary.basket') }} #{{ entity?.id }} -
                 {{ entity?.client?.name }} ({{ entity?.clientFk }})
             </template>
+            <template #header-right>
+                <QBtn
+                    flat
+                    text-color="white"
+                    :disabled="isConfirmed"
+                    :label="t('order.summary.confirm')"
+                    @click="confirmOrder()"
+                >
+                    <QTooltip>{{ t('order.summary.confirmLines') }}</QTooltip>
+                </QBtn>
+            </template>
             <template #body="{ entity }">
                 <QCard class="vn-one">
                     <VnTitle

From d7210837959ca94189a78071a8145d5509af419d Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Thu, 10 Oct 2024 08:43:33 +0200
Subject: [PATCH 037/150] fix: filter panel

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

diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue
index 242c4220d..f042c6481 100644
--- a/src/components/ui/VnFilterPanel.vue
+++ b/src/components/ui/VnFilterPanel.vue
@@ -221,7 +221,7 @@ function sanitizer(params) {
         icon="search"
         @click="search()"
     ></QBtn>
-    <QForm @submit="search(true)" id="filterPanelForm" @keyup.enter="search(true)">
+    <QForm @submit="search()" id="filterPanelForm" @keyup.enter="search(true)">
         <QList dense>
             <QItem class="q-mt-xs">
                 <QItemSection top>

From f16707756e99cd64e798e6190bf03845b82520a5 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Wed, 9 Oct 2024 00:40:21 +0200
Subject: [PATCH 038/150] fix: change type vnput

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

diff --git a/src/pages/Ticket/Card/TicketCreateRequest.vue b/src/pages/Ticket/Card/TicketCreateRequest.vue
index ffe009516..d6e757afe 100644
--- a/src/pages/Ticket/Card/TicketCreateRequest.vue
+++ b/src/pages/Ticket/Card/TicketCreateRequest.vue
@@ -56,6 +56,7 @@ const attendersOptions = ref([]);
                     v-model="data.price"
                     :label="t('purchaseRequest.price')"
                     type="number"
+                    step="0.01"
                     min="0"
                 />
             </VnRow>

From 3d5d2673452f4aa1e584fa333f12bcfc83e1f52f Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 10 Oct 2024 09:29:12 +0200
Subject: [PATCH 039/150] revert: commit

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

diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue
index f042c6481..66e9df5f5 100644
--- a/src/components/ui/VnFilterPanel.vue
+++ b/src/components/ui/VnFilterPanel.vue
@@ -221,7 +221,7 @@ function sanitizer(params) {
         icon="search"
         @click="search()"
     ></QBtn>
-    <QForm @submit="search()" id="filterPanelForm" @keyup.enter="search(true)">
+    <QForm @submit="search" id="filterPanelForm" @keyup.enter.prevent="search(true)">
         <QList dense>
             <QItem class="q-mt-xs">
                 <QItemSection top>

From 787ce2fc48d28728fcc27aa1df73705d02508188 Mon Sep 17 00:00:00 2001
From: guillermo <guillermo@verdnatura.es>
Date: Mon, 14 Oct 2024 09:33:00 +0200
Subject: [PATCH 040/150] feat: refs #7006 itemTypeLog added

---
 src/pages/ItemType/Card/ItemTypeBasicData.vue |  1 +
 src/pages/ItemType/Card/ItemTypeLog.vue       |  7 +++++++
 src/pages/ItemType/locale/en.yml              |  1 +
 src/pages/ItemType/locale/es.yml              |  1 +
 src/router/modules/itemType.js                | 11 ++++++++++-
 5 files changed, 20 insertions(+), 1 deletion(-)
 create mode 100644 src/pages/ItemType/Card/ItemTypeLog.vue

diff --git a/src/pages/ItemType/Card/ItemTypeBasicData.vue b/src/pages/ItemType/Card/ItemTypeBasicData.vue
index 51e24272d..deaf4a5fe 100644
--- a/src/pages/ItemType/Card/ItemTypeBasicData.vue
+++ b/src/pages/ItemType/Card/ItemTypeBasicData.vue
@@ -73,6 +73,7 @@ const temperaturesOptions = ref([]);
                     option-label="name"
                     hide-selected
                 />
+                <VnInput v-model="data.life" :label="t('shared.life')" />
             </VnRow>
         </template>
     </FormModel>
diff --git a/src/pages/ItemType/Card/ItemTypeLog.vue b/src/pages/ItemType/Card/ItemTypeLog.vue
new file mode 100644
index 000000000..b3f7a6597
--- /dev/null
+++ b/src/pages/ItemType/Card/ItemTypeLog.vue
@@ -0,0 +1,7 @@
+<script setup>
+import VnLog from 'src/components/common/VnLog.vue';
+</script>
+
+<template>
+    <VnLog model="ItemType" url="/ItemTypeLogs"></VnLog>
+</template>
diff --git a/src/pages/ItemType/locale/en.yml b/src/pages/ItemType/locale/en.yml
index 7889418ea..4b203bd68 100644
--- a/src/pages/ItemType/locale/en.yml
+++ b/src/pages/ItemType/locale/en.yml
@@ -4,6 +4,7 @@ shared:
     worker: Worker
     category: Category
     temperature: Temperature
+    life: Life
 summary:
     id: id
     life: Life
diff --git a/src/pages/ItemType/locale/es.yml b/src/pages/ItemType/locale/es.yml
index 9a94dceb6..43699c332 100644
--- a/src/pages/ItemType/locale/es.yml
+++ b/src/pages/ItemType/locale/es.yml
@@ -4,6 +4,7 @@ shared:
     worker: Trabajador
     category: Reino
     temperature: Temperatura
+    life: Vida
 summary:
     id: id
     code: Código
diff --git a/src/router/modules/itemType.js b/src/router/modules/itemType.js
index 8064c41ff..0fd3797e6 100644
--- a/src/router/modules/itemType.js
+++ b/src/router/modules/itemType.js
@@ -12,7 +12,7 @@ export default {
     redirect: { name: 'ItemTypeList' },
     menus: {
         main: [],
-        card: ['ItemTypeBasicData'],
+        card: ['ItemTypeBasicData', 'ItemTypeLog'],
     },
     children: [
         {
@@ -40,6 +40,15 @@ export default {
                     component: () =>
                         import('src/pages/ItemType/Card/ItemTypeBasicData.vue'),
                 },
+                {
+                    path: 'log',
+                    name: 'ItemTypeLog',
+                    meta: {
+                        title: 'log',
+                        icon: 'vn:History',
+                    },
+                    component: () => import('src/pages/ItemType/Card/ItemTypeLog.vue'),
+                },
             ],
         },
     ],

From f2cb0111eb6a5ceb1232da91f5190b5610c2e81d Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Wed, 16 Oct 2024 14:45:33 +0200
Subject: [PATCH 041/150] fix: refs #7283 fix image

---
 src/pages/Item/ItemList.vue | 23 +++++++++++++++++------
 1 file changed, 17 insertions(+), 6 deletions(-)

diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index ad2c2e238..6c7b7eaed 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -1,10 +1,9 @@
 <script setup>
-import { ref, computed, onUnmounted } from 'vue';
+import { ref, computed } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useRouter, useRoute } from 'vue-router';
 import VnImg from 'src/components/ui/VnImg.vue';
 import VnTable from 'components/VnTable/VnTable.vue';
-import { useStateStore } from 'stores/useStateStore';
 import { toDate } from 'src/filters';
 import axios from 'axios';
 import FetchedTags from 'src/components/ui/FetchedTags.vue';
@@ -16,7 +15,6 @@ const entityId = computed(() => route.params.id);
 
 const { viewSummary } = useSummaryDialog();
 const router = useRouter();
-const stateStore = useStateStore();
 const { t } = useI18n();
 const tableRef = ref();
 const route = useRoute();
@@ -53,8 +51,8 @@ const columns = computed(() => [
             attrs: ({ row }) => {
                 return {
                     id: row?.id,
-                    width: '50px',
                     zoomResolution: '1600x900',
+                    zoom: true,
                 };
             },
         },
@@ -116,9 +114,13 @@ const columns = computed(() => [
     },
     {
         label: t('item.list.typeName'),
-        name: 'typeName',
+        name: 'typeFk',
         align: 'left',
         component: 'select',
+        attrs: {
+            url: 'ItemTypes',
+            fields: ['id', 'name'],
+        },
         columnFilter: {
             name: 'typeFk',
             attrs: {
@@ -152,6 +154,11 @@ const columns = computed(() => [
         name: 'intrastat',
         align: 'left',
         component: 'select',
+        attrs: {
+            url: 'Intrastats',
+            optionValue: 'description',
+            optionLabel: 'description',
+        },
         columnFilter: {
             name: 'description',
             attrs: {
@@ -159,7 +166,6 @@ const columns = computed(() => [
                 optionValue: 'description',
                 optionLabel: 'description',
             },
-            inWhere: true,
             alias: 'intr',
         },
         columnField: {
@@ -172,6 +178,11 @@ const columns = computed(() => [
         name: 'origin',
         align: 'left',
         component: 'select',
+        attrs: {
+            url: 'Origins',
+            optionValue: 'id',
+            optionLabel: 'code',
+        },
         columnFilter: {
             name: 'id',
             attrs: {

From 11bf70d76ce3aa42d7c5ae2d94e4ebeb0a281082 Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Fri, 18 Oct 2024 14:12:23 +0200
Subject: [PATCH 042/150] fix: category and tags filters

---
 src/components/ui/VnSearchbar.vue           |   4 +-
 src/pages/Order/Card/OrderCatalogFilter.vue | 111 +++++++++++++++-----
 2 files changed, 88 insertions(+), 27 deletions(-)

diff --git a/src/components/ui/VnSearchbar.vue b/src/components/ui/VnSearchbar.vue
index fc292e980..569e0ed18 100644
--- a/src/components/ui/VnSearchbar.vue
+++ b/src/components/ui/VnSearchbar.vue
@@ -9,7 +9,7 @@ import { useStateStore } from 'src/stores/useStateStore';
 const quasar = useQuasar();
 const { t } = useI18n();
 const state = useStateStore();
-const emit = defineEmits(['on-search']);
+const emit = defineEmits(['onSearch']);
 
 const props = defineProps({
     dataKey: {
@@ -119,7 +119,7 @@ async function search() {
         delete filter.params.search;
     }
     await arrayData.applyFilter(filter);
-    emit('on-search', store.data);
+    emit('onSearch', store.data);
 }
 </script>
 <template>
diff --git a/src/pages/Order/Card/OrderCatalogFilter.vue b/src/pages/Order/Card/OrderCatalogFilter.vue
index 9c713e77e..850212a62 100644
--- a/src/pages/Order/Card/OrderCatalogFilter.vue
+++ b/src/pages/Order/Card/OrderCatalogFilter.vue
@@ -1,12 +1,12 @@
 <script setup>
-import { computed, ref, onMounted } from 'vue';
+import { computed, ref, onMounted, watch } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useRoute } from 'vue-router';
 import axios from 'axios';
 import FetchData from 'components/FetchData.vue';
 import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
 import VnSelect from 'components/common/VnSelect.vue';
-import VnInput from 'src/components/common/VnInput.vue';
+// import VnInput from 'src/components/common/VnInput.vue';
 import getParamWhere from 'src/filters/getParamWhere';
 
 const { t } = useI18n();
@@ -48,12 +48,15 @@ const orderBySelected = ref('relevancy DESC, name');
 const orderWaySelected = ref('ASC');
 
 const routeQuery = JSON.parse(route?.query.params ?? '{}');
-const params = ref({});
+const paramsSearch = ref({});
 
 onMounted(() => {
-    const filter = routeQuery.filter;
-    params.value = JSON.parse(filter ?? '{}')?.where ?? {};
-    if (Object.keys(params.value).length > 0) vnFilterPanelRef.value.search();
+    paramsSearch.value = JSON.parse(routeQuery.filter ?? '{}')?.where ?? {};
+    if (Object.keys(paramsSearch.value).length > 0) vnFilterPanelRef.value.search();
+    if (routeQuery.categoryFk && routeQuery.typeFk) {
+        selectedCategoryFk.value = routeQuery.categoryFk;
+        selectedTypeFk.value = routeQuery.typeFk;
+    }
 });
 
 const createValue = (val, done) => {
@@ -70,12 +73,18 @@ const resetCategory = () => {
 };
 
 const selectCategory = (category, search) => {
-    if (!params.value?.filter) params.value.filter = { where: {} };
-    const where = params.value.filter.where;
+    if (!paramsSearch.value?.filter) paramsSearch.value.filter = { where: {} };
+    const where = paramsSearch.value.filter.where;
     if (where.categoryFk === category?.id) {
         resetCategory();
         where.categoryFk = null;
     } else {
+        if (where.categoryFk && where.categoryFk !== category?.id) {
+            paramsSearch.value.typeFk = null;
+            selectedTypeFk.value = null;
+            typeList.value = [];
+        }
+
         selectedCategoryFk.value = category?.id;
         where.categoryFk = category?.id;
         loadTypes(category?.id);
@@ -90,6 +99,10 @@ const loadTypes = async (categoryFk = selectedCategoryFk.value) => {
     typeList.value = data;
 };
 
+watch(selectedTypeFk, (newValue) => {
+    paramsSearch.value.typeFk = newValue;
+});
+
 const selectedCategory = computed(() =>
     (categoryList.value || []).find(
         (category) => category?.id === selectedCategoryFk.value
@@ -107,25 +120,55 @@ const selectedType = computed(() => {
     return (typeList.value || []).find((type) => type?.id === selectedTypeFk.value);
 });
 
+// const applyTagFilter = (params, search) => {
+
+//     if (params.tagGroups) params.tagGroups = null;
+//     if (!tagValues.value?.length) {
+//         params.tagGroups = null;
+//         search();
+//         return;
+//     }
+//     if (!params.tagGroups) {
+//         params.tagGroups = [];
+//     }
+//     params.tagGroups.push(
+//         JSON.stringify({
+//             values: tagValues.value.filter((obj) => Object.keys(obj).length > 0),
+//             tagSelection: {
+//                 ...selectedTag.value,
+//                 orgShowField: selectedTag?.value?.name,
+//             },
+//             tagFk: selectedTag?.value?.tagFk,
+//         })
+//     );
+//     search();
+//     selectedTag.value = null;
+//     tagValues.value = [{}];
+// };
+
 const applyTagFilter = (params, search) => {
     if (!tagValues.value?.length) {
         params.tagGroups = null;
         search();
         return;
     }
+
     if (!params.tagGroups) {
         params.tagGroups = [];
+    } else if (typeof params.tagGroups === 'string') {
+        params.tagGroups = [params.tagGroups];
     }
-    params.tagGroups.push(
-        JSON.stringify({
-            values: tagValues.value.filter((obj) => Object.keys(obj).length > 0),
-            tagSelection: {
-                ...selectedTag.value,
-                orgShowField: selectedTag?.value?.name,
-            },
-            tagFk: selectedTag?.value?.tagFk,
-        })
-    );
+
+    const newTagGroup = JSON.stringify({
+        values: tagValues.value.filter((obj) => Object.keys(obj).length > 0),
+        tagSelection: {
+            ...selectedTag.value,
+            orgShowField: selectedTag?.value?.name,
+        },
+        tagFk: selectedTag?.value?.tagFk,
+    });
+    params.tagGroups.push(newTagGroup);
+
     search();
     selectedTag.value = null;
     tagValues.value = [{}];
@@ -155,6 +198,22 @@ function addOrder(value, field, params) {
     params.orderBy = JSON.stringify(orderBy);
     vnFilterPanelRef.value.search();
 }
+
+const data = ref([]);
+
+function getTagData(value) {
+    if (Array.isArray(value) && value.length >= 1) {
+        const parsedTags = value.map((obj) => JSON.parse(obj));
+
+        if (parsedTags.length === 1) {
+            data.value = parsedTags[0];
+        } else {
+            data.value = parsedTags;
+        }
+    } else {
+        data.value = JSON.parse(value);
+    }
+}
 </script>
 
 <template>
@@ -162,13 +221,14 @@ function addOrder(value, field, params) {
     <VnFilterPanel
         ref="vnFilterPanelRef"
         :data-key="props.dataKey"
-        v-model="params"
+        v-model="paramsSearch"
         :redirect="false"
         :hidden-tags="['orderFk', 'orderBy', 'filter', 'search', 'or', 'and']"
-        :un-removable-params="['orderFk', 'orderBy']"
+        :unremovable-params="['orderFk', 'orderBy']"
         :disable-submit-event="true"
     >
         <template #tags="{ tag, formatFn }">
+            {{ console.log('tag.label: ', tag.label) }}
             <strong v-if="tag.label === 'categoryFk'">
                 {{ t(selectedCategory?.name || '') }}
             </strong>
@@ -176,10 +236,11 @@ function addOrder(value, field, params) {
                 {{ t(selectedType?.name || '') }}
             </strong>
             <div v-else-if="tag.label === 'tagGroups'" class="q-gutter-x-xs">
-                <strong v-if="JSON.parse(tag.value).tagSelection.name"
-                    >{{ JSON.parse(tag.value).tagSelection?.name }}:
-                </strong>
-                <span>{{ JSON.parse(tag.value).values[0].value }}</span>
+                {{ getTagData(tag.value) }}
+                <strong> {{ data.tagSelection?.name }}: </strong>
+                <span>
+                    {{ console.log('data', data.values) }}
+                </span>
             </div>
             <div v-else class="q-gutter-x-xs">
                 <strong>{{ t(`params.${tag.label}`) }}: </strong>
@@ -328,7 +389,7 @@ function addOrder(value, field, params) {
                     @new-value="createValue"
                     @update:model-value="applyTagFilter(params, searchFn)"
                 />
-                <VnInput
+                <QInput
                     v-else
                     :label="t('params.value')"
                     v-model="value.value"

From 52981953f7e9efbc1a8875c321376e02a50c3b33 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Mon, 21 Oct 2024 13:23:45 +0200
Subject: [PATCH 043/150] feat(): refs #8039 canceledError not notify

---
 src/boot/axios.js  | 5 +++--
 src/boot/quasar.js | 3 ++-
 2 files changed, 5 insertions(+), 3 deletions(-)

diff --git a/src/boot/axios.js b/src/boot/axios.js
index 3bd80f487..d3e981adb 100644
--- a/src/boot/axios.js
+++ b/src/boot/axios.js
@@ -3,6 +3,7 @@ import { useSession } from 'src/composables/useSession';
 import { Router } from 'src/router';
 import useNotify from 'src/composables/useNotify.js';
 import { useStateQueryStore } from 'src/stores/useStateQueryStore';
+import { CanceledError } from 'axios';
 
 const session = useSession();
 const { notify } = useNotify();
@@ -42,7 +43,7 @@ const onResponseError = (error) => {
     let message = '';
 
     const response = error.response;
-    const responseData = response && response.data;
+    const responseData = response?.data;
     const responseError = responseData && response.data.error;
     if (responseError) {
         message = responseError.message;
@@ -78,7 +79,7 @@ const onResponseError = (error) => {
         return Promise.reject(error);
     }
 
-    notify(message, 'negative');
+    if (!(error instanceof CanceledError)) notify(message, 'negative');
 
     return Promise.reject(error);
 };
diff --git a/src/boot/quasar.js b/src/boot/quasar.js
index 5db6edd24..41a7990c7 100644
--- a/src/boot/quasar.js
+++ b/src/boot/quasar.js
@@ -3,6 +3,7 @@ import qFormMixin from './qformMixin';
 import mainShortcutMixin from './mainShortcutMixin';
 import keyShortcut from './keyShortcut';
 import useNotify from 'src/composables/useNotify.js';
+import { CanceledError } from 'axios';
 const { notify } = useNotify();
 
 export default boot(({ app }) => {
@@ -11,6 +12,6 @@ export default boot(({ app }) => {
     app.directive('shortcut', keyShortcut);
     app.config.errorHandler = function (err) {
         console.error(err);
-        notify('globals.error', 'negative', 'error');
+        if (!(err instanceof CanceledError)) notify('globals.error', 'negative', 'error');
     };
 });

From 9780fe596fa0af4212f308a3150d1066a3275517 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Mon, 21 Oct 2024 13:33:12 +0200
Subject: [PATCH 044/150] feat: refs #8039 notify error unify

---
 src/boot/axios.js  | 34 +---------------------------------
 src/boot/quasar.js | 37 ++++++++++++++++++++++++++++++++++---
 2 files changed, 35 insertions(+), 36 deletions(-)

diff --git a/src/boot/axios.js b/src/boot/axios.js
index d3e981adb..b084b835d 100644
--- a/src/boot/axios.js
+++ b/src/boot/axios.js
@@ -40,37 +40,7 @@ const onResponse = (response) => {
 const onResponseError = (error) => {
     stateQuery.remove(error.config);
 
-    let message = '';
-
-    const response = error.response;
-    const responseData = response?.data;
-    const responseError = responseData && response.data.error;
-    if (responseError) {
-        message = responseError.message;
-    }
-
-    switch (response?.status) {
-        case 422:
-            if (error.name == 'ValidationError')
-                message +=
-                    ' "' +
-                    responseError.details.context +
-                    '.' +
-                    Object.keys(responseError.details.codes).join(',') +
-                    '"';
-            break;
-        case 500:
-            message = 'errors.statusInternalServerError';
-            break;
-        case 502:
-            message = 'errors.statusBadGateway';
-            break;
-        case 504:
-            message = 'errors.statusGatewayTimeout';
-            break;
-    }
-
-    if (session.isLoggedIn() && response?.status === 401) {
+    if (session.isLoggedIn() && error.response?.status === 401) {
         session.destroy(false);
         const hash = window.location.hash;
         const url = hash.slice(1);
@@ -79,8 +49,6 @@ const onResponseError = (error) => {
         return Promise.reject(error);
     }
 
-    if (!(error instanceof CanceledError)) notify(message, 'negative');
-
     return Promise.reject(error);
 };
 
diff --git a/src/boot/quasar.js b/src/boot/quasar.js
index 41a7990c7..bf5175ee6 100644
--- a/src/boot/quasar.js
+++ b/src/boot/quasar.js
@@ -10,8 +10,39 @@ export default boot(({ app }) => {
     app.mixin(qFormMixin);
     app.mixin(mainShortcutMixin);
     app.directive('shortcut', keyShortcut);
-    app.config.errorHandler = function (err) {
-        console.error(err);
-        if (!(err instanceof CanceledError)) notify('globals.error', 'negative', 'error');
+    app.config.errorHandler = (error) => {
+        let message;
+        const response = error.response;
+        const responseData = response?.data;
+        const responseError = responseData && response.data.error;
+        if (responseError) {
+            message = responseError.message;
+        }
+
+        switch (response?.status) {
+            case 422:
+                if (error.name == 'ValidationError')
+                    message +=
+                        ' "' +
+                        responseError.details.context +
+                        '.' +
+                        Object.keys(responseError.details.codes).join(',') +
+                        '"';
+                break;
+            case 500:
+                message = 'errors.statusInternalServerError';
+                break;
+            case 502:
+                message = 'errors.statusBadGateway';
+                break;
+            case 504:
+                message = 'errors.statusGatewayTimeout';
+                break;
+        }
+
+        console.error(error);
+        if (error instanceof CanceledError) return;
+
+        notify(message ?? 'globals.error', 'negative', 'error');
     };
 });

From c69f7af3906e5d88f321350813ec42cc692bd6f4 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Mon, 21 Oct 2024 13:38:35 +0200
Subject: [PATCH 045/150] test: refs #8039 axios not notify

---
 test/vitest/__tests__/boot/axios.spec.js | 18 ------------------
 1 file changed, 18 deletions(-)

diff --git a/test/vitest/__tests__/boot/axios.spec.js b/test/vitest/__tests__/boot/axios.spec.js
index 7a802b4d2..19d396ec5 100644
--- a/test/vitest/__tests__/boot/axios.spec.js
+++ b/test/vitest/__tests__/boot/axios.spec.js
@@ -36,8 +36,6 @@ describe('Axios boot', () => {
 
     describe('onResponseError()', async () => {
         it('should call to the Notify plugin with a message error for an status code "500"', async () => {
-            Notify.create = vi.fn();
-
             const error = {
                 response: {
                     status: 500,
@@ -45,19 +43,10 @@ describe('Axios boot', () => {
             };
 
             const result = onResponseError(error);
-
             expect(result).rejects.toEqual(expect.objectContaining(error));
-            expect(Notify.create).toHaveBeenCalledWith(
-                expect.objectContaining({
-                    message: 'An internal server error has ocurred',
-                    type: 'negative',
-                })
-            );
         });
 
         it('should call to the Notify plugin with a message from the response property', async () => {
-            Notify.create = vi.fn();
-
             const error = {
                 response: {
                     status: 401,
@@ -70,14 +59,7 @@ describe('Axios boot', () => {
             };
 
             const result = onResponseError(error);
-
             expect(result).rejects.toEqual(expect.objectContaining(error));
-            expect(Notify.create).toHaveBeenCalledWith(
-                expect.objectContaining({
-                    message: 'Invalid user or password',
-                    type: 'negative',
-                })
-            );
         });
     });
 });

From 29a7f3b2fed499111e620941fd17baa55611a388 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Mon, 21 Oct 2024 13:48:43 +0200
Subject: [PATCH 046/150] fix: refs #7283 #7283 ItemDiary subToolbar

---
 src/pages/Item/Card/ItemDiary.vue | 67 ++++++++++++++++---------------
 1 file changed, 34 insertions(+), 33 deletions(-)

diff --git a/src/pages/Item/Card/ItemDiary.vue b/src/pages/Item/Card/ItemDiary.vue
index a36b6a246..23dc6214e 100644
--- a/src/pages/Item/Card/ItemDiary.vue
+++ b/src/pages/Item/Card/ItemDiary.vue
@@ -188,7 +188,7 @@ async function updateWarehouse(warehouseFk) {
         },
     });
     await stock.fetch({});
-    stock.store.data.itemFk = route.params.id
+    stock.store.data.itemFk = route.params.id;
 }
 </script>
 
@@ -205,38 +205,39 @@ async function updateWarehouse(warehouseFk) {
         auto-load
         @on-fetch="(data) => (warehousesOptions = data)"
     />
-    <QToolbar class="justify-end">
-        <div id="st-data" class="row">
-            <VnSelect
-                :label="t('itemDiary.warehouse')"
-                :options="warehousesOptions"
-                hide-selected
-                option-label="name"
-                option-value="id"
-                dense
-                v-model="itemsBalanceFilter.where.warehouseFk"
-                @update:model-value="
-                    (value) => fetchItemBalances() && updateWarehouse(value)
-                "
-                class="q-mr-lg"
-            />
-            <QCheckbox
-                :label="t('itemDiary.showBefore')"
-                v-model="showWhatsBeforeInventory"
-                @update:model-value="fetchItemBalances"
-                class="q-mr-lg"
-            />
-            <VnInputDate
-                v-if="showWhatsBeforeInventory"
-                :label="t('itemDiary.since')"
-                dense
-                v-model="itemsBalanceFilter.where.date"
-                @update:model-value="fetchItemBalances"
-            />
-        </div>
-        <QSpace />
-        <div id="st-actions"></div>
-    </QToolbar>
+    <template v-if="stateStore.isHeaderMounted()">
+        <Teleport to="#st-data">
+            <div class="row">
+                <VnSelect
+                    :label="t('itemDiary.warehouse')"
+                    :options="warehousesOptions"
+                    hide-selected
+                    option-label="name"
+                    option-value="id"
+                    dense
+                    v-model="itemsBalanceFilter.where.warehouseFk"
+                    @update:model-value="
+                        (value) => fetchItemBalances() && updateWarehouse(value)
+                    "
+                    class="q-mr-lg"
+                />
+                <QCheckbox
+                    :label="t('itemDiary.showBefore')"
+                    v-model="showWhatsBeforeInventory"
+                    @update:model-value="fetchItemBalances"
+                    class="q-mr-lg"
+                />
+                <VnInputDate
+                    v-if="showWhatsBeforeInventory"
+                    :label="t('itemDiary.since')"
+                    dense
+                    v-model="itemsBalanceFilter.where.date"
+                    @update:model-value="fetchItemBalances"
+                />
+            </div>
+        </Teleport>
+        <Teleport to="#st-actions"> </Teleport>
+    </template>
     <QPage class="column items-center q-pa-md">
         <QTable
             :rows="itemBalances"

From f4a7e20c574ab8603d6f4b2dabd222556a9a31ea Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Mon, 21 Oct 2024 14:31:31 +0200
Subject: [PATCH 047/150] test: refs #6943 #6943 add comands to solve tests

---
 .../integration/client/clientBalance.spec.js  |  2 +-
 .../integration/client/clientCredits.spec.js  |  2 +-
 .../integration/client/clientList.spec.js     |  6 +--
 .../client/clientRecoveries.spec.js           |  2 +-
 .../clientCreditOpinion.spec.js               |  2 +-
 .../client/others/clientConsumption.spec.js   |  2 +-
 .../client/others/clientMandates.spec.js      |  2 +-
 .../client/others/clientSamples.spec.js       |  2 +-
 .../client/others/clientWebPayments.spec.js   |  4 +-
 test/cypress/support/commands.js              | 42 +++++++++++++++++++
 10 files changed, 54 insertions(+), 12 deletions(-)

diff --git a/test/cypress/integration/client/clientBalance.spec.js b/test/cypress/integration/client/clientBalance.spec.js
index 4a666bdb1..dfba56b16 100644
--- a/test/cypress/integration/client/clientBalance.spec.js
+++ b/test/cypress/integration/client/clientBalance.spec.js
@@ -8,6 +8,6 @@ describe('Client balance', () => {
         });
     });
     it('Should load layout', () => {
-        cy.get('.q-card').should('be.visible');
+        cy.get('.q-page').should('be.visible');
     });
 });
diff --git a/test/cypress/integration/client/clientCredits.spec.js b/test/cypress/integration/client/clientCredits.spec.js
index f81bf987d..5f303b40d 100644
--- a/test/cypress/integration/client/clientCredits.spec.js
+++ b/test/cypress/integration/client/clientCredits.spec.js
@@ -8,6 +8,6 @@ describe('Client credits', () => {
         });
     });
     it('Should load layout', () => {
-        cy.get('.q-card').should('be.visible');
+        cy.get('.q-page').should('be.visible');
     });
 });
diff --git a/test/cypress/integration/client/clientList.spec.js b/test/cypress/integration/client/clientList.spec.js
index 93e53b9f6..909bd3933 100644
--- a/test/cypress/integration/client/clientList.spec.js
+++ b/test/cypress/integration/client/clientList.spec.js
@@ -44,7 +44,7 @@ describe('Client list', () => {
         });
     });
 
-    it('Client founded create ticket', () => {
+    it.skip('Client founded create ticket', () => {
         const search = 'Jessica Jones';
         cy.searchByLabel('Name', search);
         cy.clickButtonsDescriptor(2);
@@ -52,10 +52,10 @@ describe('Client list', () => {
         cy.waitForElement('.q-form');
         cy.checkValueForm(1, search);
     });
-    it('Client founded create order', () => {
+    it.skip('Client founded create order', () => {
         const search = 'Jessica Jones';
         cy.searchByLabel('Name', search);
-        cy.clickButtonsDescriptor(4);
+        cy.clickButtonsDescriptor(3);
         cy.waitForElement('#formModel');
         cy.waitForElement('.q-form');
         cy.checkValueForm(2, search);
diff --git a/test/cypress/integration/client/clientRecoveries.spec.js b/test/cypress/integration/client/clientRecoveries.spec.js
index a4e220008..ea6f14407 100644
--- a/test/cypress/integration/client/clientRecoveries.spec.js
+++ b/test/cypress/integration/client/clientRecoveries.spec.js
@@ -8,6 +8,6 @@ describe('Client recoveries', () => {
         });
     });
     it('Should load layout', () => {
-        cy.get('.q-card').should('be.visible');
+        cy.get('.q-page').should('be.visible');
     });
 });
diff --git a/test/cypress/integration/client/credit-management/clientCreditOpinion.spec.js b/test/cypress/integration/client/credit-management/clientCreditOpinion.spec.js
index 7d9c0fa77..c32215f01 100644
--- a/test/cypress/integration/client/credit-management/clientCreditOpinion.spec.js
+++ b/test/cypress/integration/client/credit-management/clientCreditOpinion.spec.js
@@ -8,6 +8,6 @@ describe('Client credit opinion', () => {
         });
     });
     it('Should load layout', () => {
-        cy.get('.q-card').should('be.visible');
+        cy.get('.q-page').should('be.visible');
     });
 });
diff --git a/test/cypress/integration/client/others/clientConsumption.spec.js b/test/cypress/integration/client/others/clientConsumption.spec.js
index 179a37707..bbc11998e 100644
--- a/test/cypress/integration/client/others/clientConsumption.spec.js
+++ b/test/cypress/integration/client/others/clientConsumption.spec.js
@@ -8,6 +8,6 @@ describe('Client consumption', () => {
         });
     });
     it('Should load layout', () => {
-        cy.get('.q-card').should('be.visible');
+        cy.get('.q-page').should('be.visible');
     });
 });
diff --git a/test/cypress/integration/client/others/clientMandates.spec.js b/test/cypress/integration/client/others/clientMandates.spec.js
index aaeb7f930..055eda2d0 100644
--- a/test/cypress/integration/client/others/clientMandates.spec.js
+++ b/test/cypress/integration/client/others/clientMandates.spec.js
@@ -8,6 +8,6 @@ describe('Client mandates', () => {
         });
     });
     it('Should load layout', () => {
-        cy.get('.q-card').should('be.visible');
+        cy.get('.q-page').should('be.visible');
     });
 });
diff --git a/test/cypress/integration/client/others/clientSamples.spec.js b/test/cypress/integration/client/others/clientSamples.spec.js
index 03b7238f4..a50120402 100644
--- a/test/cypress/integration/client/others/clientSamples.spec.js
+++ b/test/cypress/integration/client/others/clientSamples.spec.js
@@ -8,6 +8,6 @@ describe('Client samples', () => {
         });
     });
     it('Should load layout', () => {
-        cy.get('.q-card').should('be.visible');
+        cy.get('.q-page').should('be.visible');
     });
 });
diff --git a/test/cypress/integration/client/others/clientWebPayments.spec.js b/test/cypress/integration/client/others/clientWebPayments.spec.js
index 5f7087d21..f35b12612 100644
--- a/test/cypress/integration/client/others/clientWebPayments.spec.js
+++ b/test/cypress/integration/client/others/clientWebPayments.spec.js
@@ -3,11 +3,11 @@ describe('Client web payments', () => {
     beforeEach(() => {
         cy.viewport(1280, 720);
         cy.login('developer');
-        cy.visit('#/customer/1101/others/web-payments', {
+        cy.visit('#/customer/1101/others/web-payment', {
             timeout: 5000,
         });
     });
     it('Should load layout', () => {
-        cy.get('.q-card').should('be.visible');
+        cy.get('.q-page').should('be.visible');
     });
 });
diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js
index 83f45b721..9106a64cd 100755
--- a/test/cypress/support/commands.js
+++ b/test/cypress/support/commands.js
@@ -245,6 +245,13 @@ Cypress.Commands.add('validateContent', (selector, expectedValue) => {
     cy.get(selector).should('have.text', expectedValue);
 });
 
+Cypress.Commands.add('openActionDescriptor', (opt) => {
+    cy.openActionsDescriptor();
+    const listItem = '[role="menu"] .q-list .q-item';
+    cy.contains(listItem, opt).click();
+    1;
+});
+
 Cypress.Commands.add('openActionsDescriptor', () => {
     cy.get('.header > :nth-child(3) > .q-btn__content > .q-icon').click();
 });
@@ -254,3 +261,38 @@ Cypress.Commands.add('openUserPanel', () => {
         '.column > .q-avatar > .q-avatar__content > .q-img > .q-img__container > .q-img__image'
     ).click();
 });
+
+Cypress.Commands.add('clickButtonsDescriptor', (id) => {
+    cy.get(`.actions > .q-card__actions> .q-btn:nth-child(${id})`)
+        .invoke('removeAttr', 'target')
+        .click();
+});
+
+Cypress.Commands.add('openActions', (row) => {
+    cy.get('tbody > tr').eq(row).find('.actions > .q-btn').click();
+});
+
+Cypress.Commands.add('checkNotification', (type) => {
+    const values = {
+        created: 'Data created',
+        updated: 'Data saved',
+        deleted: 'Data deleted',
+    };
+    cy.get('.q-notification__message').should('have.text', values[type]);
+});
+
+Cypress.Commands.add('checkValueForm', (id, search) => {
+    cy.get(
+        `.grid-create > :nth-child(${id}) > .q-field__inner>.q-field__control> .q-field__control-container>.q-field__native >.q-field__input`
+    ).should('have.value', search);
+});
+
+Cypress.Commands.add('checkValueSelectForm', (id, search) => {
+    cy.get(
+        `.grid-create > :nth-child(${id}) > .q-field > .q-field__inner > .q-field__control > .q-field__control-container>.q-field__native>.q-field__input`
+    ).should('have.value', search);
+});
+
+Cypress.Commands.add('searchByLabel', (label, value) => {
+    cy.get(`[label="${label}"] > .q-field > .q-field__inner`).type(`${value}{enter}`);
+});

From f3a62091893009b75b4ef3a701f7db1b43f151e1 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Tue, 22 Oct 2024 12:00:11 +0200
Subject: [PATCH 048/150] fix: refs #7283 #7283 bugs

---
 src/pages/Item/Card/ItemDescriptorImage.vue |  4 +-
 src/pages/Item/ItemList.vue                 | 55 ++++++++++++++++-----
 2 files changed, 46 insertions(+), 13 deletions(-)

diff --git a/src/pages/Item/Card/ItemDescriptorImage.vue b/src/pages/Item/Card/ItemDescriptorImage.vue
index b035a630a..3cd51758b 100644
--- a/src/pages/Item/Card/ItemDescriptorImage.vue
+++ b/src/pages/Item/Card/ItemDescriptorImage.vue
@@ -134,10 +134,10 @@ es:
     Regularize stock: Regularizar stock
     All it's properties will be copied: Todas sus propiedades serán copiadas
     Do you want to clone this item?: ¿Desea clonar este artículo?
-    warehouseText: Calculated on the warehouse of { warehouseName }
+    warehouseText: Calculado sobre el almacén de { warehouseName }
 
 en:
-    warehouseText: Calculado sobre el almacén de { warehouseName }
+    warehouseText: Calculated on the warehouse of { warehouseName }
 </i18n>
 
 <style lang="scss" scoped>
diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index 6c7b7eaed..92c9d188b 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -11,6 +11,9 @@ import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
 import { useSummaryDialog } from 'src/composables/useSummaryDialog';
 import ItemSummary from '../Item/Card/ItemSummary.vue';
 import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
+import ItemDescriptorProxy from './Card/ItemDescriptorProxy.vue';
+import VnConfirm from 'src/components/ui/VnConfirm.vue';
+import { useQuasar } from 'quasar';
 const entityId = computed(() => route.params.id);
 
 const { viewSummary } = useSummaryDialog();
@@ -18,6 +21,7 @@ const router = useRouter();
 const { t } = useI18n();
 const tableRef = ref();
 const route = useRoute();
+const quasar = useQuasar();
 
 const itemFilter = {
     include: [
@@ -53,10 +57,12 @@ const columns = computed(() => [
                     id: row?.id,
                     zoomResolution: '1600x900',
                     zoom: true,
+                    class: 'rounded',
                 };
             },
         },
         columnFilter: false,
+        cardVisible: true,
     },
     {
         label: t('item.list.id'),
@@ -66,6 +72,7 @@ const columns = computed(() => [
         chip: {
             condition: () => true,
         },
+        cardVisible: true,
     },
     {
         label: t('item.list.grouping'),
@@ -93,6 +100,7 @@ const columns = computed(() => [
         columnFilter: {
             name: 'search',
         },
+        cardVisible: true,
     },
     {
         label: t('item.list.stems'),
@@ -102,6 +110,7 @@ const columns = computed(() => [
             component: 'number',
             inWhere: true,
         },
+        cardVisible: true,
     },
     {
         label: t('item.list.size'),
@@ -111,6 +120,7 @@ const columns = computed(() => [
             component: 'number',
             inWhere: true,
         },
+        cardVisible: true,
     },
     {
         label: t('item.list.typeName'),
@@ -172,6 +182,7 @@ const columns = computed(() => [
             component: null,
         },
         create: true,
+        cardVisible: true,
     },
     {
         label: t('item.list.origin'),
@@ -197,6 +208,7 @@ const columns = computed(() => [
             component: null,
         },
         create: true,
+        cardVisible: true,
     },
     {
         label: t('item.list.userName'),
@@ -270,13 +282,14 @@ const columns = computed(() => [
         name: 'tableActions',
         actions: [
             {
-                title: t('Clone item'),
+                title: t('globals.clone'),
+
                 icon: 'vn:clone',
-                action: cloneItem,
+                action: openCloneDialog,
                 isPrimary: true,
             },
             {
-                title: t('view Summary'),
+                title: t('components.smartCard.viewSummary'),
                 icon: 'preview',
                 action: (row) => viewSummary(row.id, ItemSummary),
                 isPrimary: true,
@@ -285,21 +298,34 @@ const columns = computed(() => [
     },
 ]);
 
-const cloneItem = async (itemFk) => {
+const cloneItem = async () => {
     try {
-        const { data } = await axios.post(`Items/${itemFk.id}/clone`);
-        if (!data) return;
+        const { data } = await axios.post(`Items/${entityId.value}/clone`);
         router.push({ name: 'ItemTags', params: { id: data.id } });
     } catch (err) {
-        console.error('Error cloning item', err);
+        console.error('Error cloning item');
     }
 };
+
+const openCloneDialog = async () => {
+    quasar
+        .dialog({
+            component: VnConfirm,
+            componentProps: {
+                title: t('All its properties will be copied'),
+                message: t('Do you want to clone this item?'),
+            },
+        })
+        .onOk(async () => {
+            await cloneItem();
+        });
+};
 </script>
 
 <template>
     <VnSearchbar
         data-key="ItemList"
-        :label="t('Search Item')"
+        :label="t('item.searchbar.label')"
         :info="t('You can search by id')"
     />
     <VnTable
@@ -309,19 +335,25 @@ const cloneItem = async (itemFk) => {
         url-create="Items"
         :create="{
             urlCreate: 'Items',
-            title: 'Create Item',
+            title: t('Create Item'),
             onDataSaved: () => tableRef.redirect(),
             formInitialData: {
                 editorFk: entityId,
             },
         }"
-        order="id ASC"
+        :order="['isActive DESC', 'name', 'id']"
         :columns="columns"
         auto-load
         redirect="Item"
         :is-editable="false"
         :filer="itemFilter"
     >
+        <template #column-id="{ row }">
+            <span class="link" @click.stop>
+                {{ row.id }}
+                <ItemDescriptorProxy :id="row.id" />
+            </span>
+        </template>
         <template #column-userName="{ row }">
             <span class="link" @click.stop>
                 {{ row.userName }}
@@ -349,7 +381,8 @@ const cloneItem = async (itemFk) => {
 <i18n>
 es:
     New item: Nuevo artículo
-    All it's properties will be copied: Todas sus propiedades serán copiadas
+    All its properties will be copied: Todas sus propiedades serán copiadas
     Do you want to clone this item?: ¿Desea clonar este artículo?
     Preview: Vista previa
+    Regularize stock: Regularizar stock
 </i18n>

From 1346a2e7fd52f8811ac0b2efbd0c03676becca1f Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Tue, 22 Oct 2024 12:05:44 +0200
Subject: [PATCH 049/150] fix: tag chips

---
 src/pages/Order/Card/OrderCatalogFilter.vue | 113 +++++++++-----------
 1 file changed, 50 insertions(+), 63 deletions(-)

diff --git a/src/pages/Order/Card/OrderCatalogFilter.vue b/src/pages/Order/Card/OrderCatalogFilter.vue
index 850212a62..0fe8c4d64 100644
--- a/src/pages/Order/Card/OrderCatalogFilter.vue
+++ b/src/pages/Order/Card/OrderCatalogFilter.vue
@@ -6,8 +6,8 @@ import axios from 'axios';
 import FetchData from 'components/FetchData.vue';
 import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
 import VnSelect from 'components/common/VnSelect.vue';
-// import VnInput from 'src/components/common/VnInput.vue';
 import getParamWhere from 'src/filters/getParamWhere';
+import VnFilterPanelChip from 'src/components/ui/VnFilterPanelChip.vue';
 
 const { t } = useI18n();
 
@@ -49,6 +49,7 @@ const orderWaySelected = ref('ASC');
 
 const routeQuery = JSON.parse(route?.query.params ?? '{}');
 const paramsSearch = ref({});
+const tagData = ref([]);
 
 onMounted(() => {
     paramsSearch.value = JSON.parse(routeQuery.filter ?? '{}')?.where ?? {};
@@ -120,32 +121,6 @@ const selectedType = computed(() => {
     return (typeList.value || []).find((type) => type?.id === selectedTypeFk.value);
 });
 
-// const applyTagFilter = (params, search) => {
-
-//     if (params.tagGroups) params.tagGroups = null;
-//     if (!tagValues.value?.length) {
-//         params.tagGroups = null;
-//         search();
-//         return;
-//     }
-//     if (!params.tagGroups) {
-//         params.tagGroups = [];
-//     }
-//     params.tagGroups.push(
-//         JSON.stringify({
-//             values: tagValues.value.filter((obj) => Object.keys(obj).length > 0),
-//             tagSelection: {
-//                 ...selectedTag.value,
-//                 orgShowField: selectedTag?.value?.name,
-//             },
-//             tagFk: selectedTag?.value?.tagFk,
-//         })
-//     );
-//     search();
-//     selectedTag.value = null;
-//     tagValues.value = [{}];
-// };
-
 const applyTagFilter = (params, search) => {
     if (!tagValues.value?.length) {
         params.tagGroups = null;
@@ -153,27 +128,46 @@ const applyTagFilter = (params, search) => {
         return;
     }
 
-    if (!params.tagGroups) {
+    if (!params.tagGroups || typeof params.tagGroups === 'string') {
         params.tagGroups = [];
-    } else if (typeof params.tagGroups === 'string') {
-        params.tagGroups = [params.tagGroups];
     }
 
-    const newTagGroup = JSON.stringify({
-        values: tagValues.value.filter((obj) => Object.keys(obj).length > 0),
-        tagSelection: {
-            ...selectedTag.value,
-            orgShowField: selectedTag?.value?.name,
-        },
-        tagFk: selectedTag?.value?.tagFk,
-    });
-    params.tagGroups.push(newTagGroup);
+    params.tagGroups.push(
+        JSON.stringify({
+            values: tagValues.value.filter((obj) => Object.keys(obj).length > 0),
+            tagSelection: {
+                ...selectedTag.value,
+                orgShowField: selectedTag?.value?.name,
+            },
+            tagFk: selectedTag?.value?.tagFk,
+        })
+    );
 
+    tagData.value.push(JSON.parse(params.tagGroups[params.tagGroups.length - 1]));
     search();
     selectedTag.value = null;
     tagValues.value = [{}];
 };
 
+const removeTagChip = (selection, params, search) => {
+    if (typeof params.tagGroups === 'string') {
+        try {
+            params.tagGroups = JSON.parse(params.tagGroups);
+        } catch (error) {
+            console.error('Error parsing tagGroups:', error);
+            params.tagGroups = [];
+        }
+    }
+    console.log('params.tagGroups: ', params.tagGroups);
+    console.log('selection: ', selection);
+
+    if (Array.isArray(params.tagGroups)) {
+        params.tagGroups = params.tagGroups.filter((value) => value !== selection);
+    }
+
+    search();
+};
+
 const setCategoryList = (data) => {
     categoryList.value = (data || [])
         .filter((category) => category.display)
@@ -198,22 +192,6 @@ function addOrder(value, field, params) {
     params.orderBy = JSON.stringify(orderBy);
     vnFilterPanelRef.value.search();
 }
-
-const data = ref([]);
-
-function getTagData(value) {
-    if (Array.isArray(value) && value.length >= 1) {
-        const parsedTags = value.map((obj) => JSON.parse(obj));
-
-        if (parsedTags.length === 1) {
-            data.value = parsedTags[0];
-        } else {
-            data.value = parsedTags;
-        }
-    } else {
-        data.value = JSON.parse(value);
-    }
-}
 </script>
 
 <template>
@@ -224,29 +202,37 @@ function getTagData(value) {
         v-model="paramsSearch"
         :redirect="false"
         :hidden-tags="['orderFk', 'orderBy', 'filter', 'search', 'or', 'and']"
+        :custom-tags="['tagGroups']"
         :unremovable-params="['orderFk', 'orderBy']"
         :disable-submit-event="true"
     >
         <template #tags="{ tag, formatFn }">
-            {{ console.log('tag.label: ', tag.label) }}
             <strong v-if="tag.label === 'categoryFk'">
                 {{ t(selectedCategory?.name || '') }}
             </strong>
             <strong v-else-if="tag.label === 'typeFk'">
                 {{ t(selectedType?.name || '') }}
             </strong>
-            <div v-else-if="tag.label === 'tagGroups'" class="q-gutter-x-xs">
-                {{ getTagData(tag.value) }}
-                <strong> {{ data.tagSelection?.name }}: </strong>
-                <span>
-                    {{ console.log('data', data.values) }}
-                </span>
-            </div>
             <div v-else class="q-gutter-x-xs">
                 <strong>{{ t(`params.${tag.label}`) }}: </strong>
                 <span>{{ formatFn(tag.value) }}</span>
             </div>
         </template>
+        <template #customTags="{ tags: customTags, params, searchFn }">
+            <template v-for="tag in customTags" :key="tag.label">
+                <template v-if="tag.label === 'tagGroups'">
+                    <VnFilterPanelChip
+                        v-for="chip in tagData"
+                        :key="chip"
+                        removable
+                        @remove="removeTagChip(chip, params, searchFn)"
+                    >
+                        <strong>{{ chip.tagSelection?.name }}: </strong>
+                        <span>{{ chip.values[0].value }}</span>
+                    </VnFilterPanelChip>
+                </template>
+            </template>
+        </template>
         <template #body="{ params, searchFn }">
             <QItem class="category-filter q-mt-md">
                 <div
@@ -410,6 +396,7 @@ function getTagData(value) {
                     icon="add_circle"
                     shortcut="+"
                     flat
+                    size="md"
                     class="filter-icon"
                     @click="tagValues.push({})"
                 />

From d6b8d41b6c4cf5144018754d8bc7cdee1e73f5ba Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Tue, 22 Oct 2024 12:09:19 +0200
Subject: [PATCH 050/150] fix: refs #7283 #7283 ItemSummary bugs

---
 src/pages/Item/Card/ItemSummary.vue | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/pages/Item/Card/ItemSummary.vue b/src/pages/Item/Card/ItemSummary.vue
index 7b6015c30..071203038 100644
--- a/src/pages/Item/Card/ItemSummary.vue
+++ b/src/pages/Item/Card/ItemSummary.vue
@@ -135,7 +135,7 @@ const getUrl = (id, param) => `#/Item/${id}/${param}`;
                 <VnLv
                     v-for="(tax, index) in item.taxes"
                     :key="index"
-                    :label="tax.country.country"
+                    :label="tax.country.name"
                     :value="tax.taxClass.description"
                 />
             </QCard>
@@ -155,7 +155,8 @@ const getUrl = (id, param) => `#/Item/${id}/${param}`;
                     :url="getUrl(entityId, 'barcode')"
                     :text="t('item.summary.barcode')"
                 />
-                <p
+                <div
+                    class="text-bold"
                     v-for="(barcode, index) in item.itemBarcode"
                     :key="index"
                     v-text="barcode.code"

From fff3310658db3eb3a80d8a259e94ac57a02a3d48 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Tue, 22 Oct 2024 12:09:32 +0200
Subject: [PATCH 051/150] fix: refs #7283 tooltips !Item

---
 src/pages/Account/AccountList.vue       | 2 +-
 src/pages/Account/Role/AccountRoles.vue | 2 +-
 src/pages/Route/RouteAutonomous.vue     | 2 +-
 src/pages/Route/RouteExtendedList.vue   | 2 +-
 src/pages/Ticket/TicketList.vue         | 2 +-
 src/pages/Zone/ZoneList.vue             | 2 +-
 6 files changed, 6 insertions(+), 6 deletions(-)

diff --git a/src/pages/Account/AccountList.vue b/src/pages/Account/AccountList.vue
index 72c445fa9..9e7f1b10a 100644
--- a/src/pages/Account/AccountList.vue
+++ b/src/pages/Account/AccountList.vue
@@ -74,7 +74,7 @@ const columns = computed(() => [
         name: 'tableActions',
         actions: [
             {
-                title: t('View Summary'),
+                title: t('components.smartCard.viewSummary'),
                 icon: 'preview',
                 action: (row) => viewSummary(row.id, AccountSummary),
                 isPrimary: true,
diff --git a/src/pages/Account/Role/AccountRoles.vue b/src/pages/Account/Role/AccountRoles.vue
index ea175d913..5398485e3 100644
--- a/src/pages/Account/Role/AccountRoles.vue
+++ b/src/pages/Account/Role/AccountRoles.vue
@@ -54,7 +54,7 @@ const columns = computed(() => [
         name: 'tableActions',
         actions: [
             {
-                title: t('View Summary'),
+                title: t('components.smartCard.viewSummary'),
                 icon: 'preview',
                 action: (row) => viewSummary(row.id, RoleSummary),
                 isPrimary: true,
diff --git a/src/pages/Route/RouteAutonomous.vue b/src/pages/Route/RouteAutonomous.vue
index 5ad349942..4a691dbef 100644
--- a/src/pages/Route/RouteAutonomous.vue
+++ b/src/pages/Route/RouteAutonomous.vue
@@ -126,7 +126,7 @@ const columns = computed(() => [
         name: 'tableActions',
         actions: [
             {
-                title: t('Preview'),
+                title: t('components.smartCard.viewSummary'),
                 icon: 'preview',
                 isPrimary: true,
                 action: (row) => viewSummary(row?.routeFk, RouteSummary),
diff --git a/src/pages/Route/RouteExtendedList.vue b/src/pages/Route/RouteExtendedList.vue
index 51da4ec12..dbf646935 100644
--- a/src/pages/Route/RouteExtendedList.vue
+++ b/src/pages/Route/RouteExtendedList.vue
@@ -204,7 +204,7 @@ const columns = computed(() => [
                 isPrimary: true,
             },
             {
-                title: t('route.components.smartCard.viewSummary'),
+                title: t('components.smartCard.viewSummary'),
                 icon: 'preview',
                 action: (row) => viewSummary(row?.id, RouteSummary),
                 isPrimary: true,
diff --git a/src/pages/Ticket/TicketList.vue b/src/pages/Ticket/TicketList.vue
index ad97e75c1..272d3a666 100644
--- a/src/pages/Ticket/TicketList.vue
+++ b/src/pages/Ticket/TicketList.vue
@@ -202,7 +202,7 @@ const columns = computed(() => [
                 action: (row) => redirectToLines(row.id),
             },
             {
-                title: t('ticketList.summary'),
+                title: t('components.smartCard.viewSummary'),
                 icon: 'preview',
                 isPrimary: true,
                 action: (row) => viewSummary(row.id, TicketSummary),
diff --git a/src/pages/Zone/ZoneList.vue b/src/pages/Zone/ZoneList.vue
index d160ea6b5..89f2dd42c 100644
--- a/src/pages/Zone/ZoneList.vue
+++ b/src/pages/Zone/ZoneList.vue
@@ -103,7 +103,7 @@ const columns = computed(() => [
         name: 'tableActions',
         actions: [
             {
-                title: t('list.zoneSummary'),
+                title: t('components.smartCard.viewSummary'),
                 icon: 'preview',
                 action: (row) => viewSummary(row.id, ZoneSummary),
                 isPrimary: true,

From 49c0d64c07e27e2c469499d85dacba8a611184e3 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Tue, 22 Oct 2024 12:13:43 +0200
Subject: [PATCH 052/150] fix: refs #7283 #7283 ItemSummary bugs

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

diff --git a/src/pages/Item/Card/ItemSummary.vue b/src/pages/Item/Card/ItemSummary.vue
index 071203038..6ad09ac64 100644
--- a/src/pages/Item/Card/ItemSummary.vue
+++ b/src/pages/Item/Card/ItemSummary.vue
@@ -119,7 +119,7 @@ const getUrl = (id, param) => `#/Item/${id}/${param}`;
                 <VnLv
                     v-for="(tag, index) in tags"
                     :key="index"
-                    :label="`${tag.priority} ${tag.tag.name}`"
+                    :label="`${tag.priority} ${tag.tag.name}:`"
                     :value="tag.value"
                 />
             </QCard>

From 9db1c4f721a3a0d5f3f3a103060a65b78bca4fa4 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Tue, 22 Oct 2024 12:29:33 +0200
Subject: [PATCH 053/150] fix: refs #8039 bad tests

---
 src/components/FormModel.vue                     | 3 ---
 test/cypress/integration/outLogin/logout.spec.js | 7 ++++---
 2 files changed, 4 insertions(+), 6 deletions(-)

diff --git a/src/components/FormModel.vue b/src/components/FormModel.vue
index 05f947cf3..9ac2d38a5 100644
--- a/src/components/FormModel.vue
+++ b/src/components/FormModel.vue
@@ -217,9 +217,6 @@ async function save() {
         updateAndEmit('onDataSaved', formData.value, response?.data);
         if ($props.reload) await arrayData.fetch({});
         hasChanges.value = false;
-    } catch (err) {
-        console.error(err);
-        notify('errors.writeRequest', 'negative');
     } finally {
         isLoading.value = false;
     }
diff --git a/test/cypress/integration/outLogin/logout.spec.js b/test/cypress/integration/outLogin/logout.spec.js
index 423189908..8d4e90aac 100644
--- a/test/cypress/integration/outLogin/logout.spec.js
+++ b/test/cypress/integration/outLogin/logout.spec.js
@@ -13,7 +13,7 @@ describe('Logout', () => {
     });
     describe('not user', () => {
         beforeEach(() => {
-            cy.intercept('GET', '**/VnUsers/acl', {
+            cy.intercept('GET', '**DefaultViewConfigs**', {
                 statusCode: 401,
                 body: {
                     error: {
@@ -24,10 +24,11 @@ describe('Logout', () => {
                     },
                 },
                 statusMessage: 'AUTHORIZATION_REQUIRED',
-            }).as('someRoute');
+            });
         });
+
         it('when token not exists', () => {
-            cy.reload();
+            cy.get('.q-list > [href="#/item"]').click();
             cy.get('.q-notification__message').should(
                 'have.text',
                 'Authorization Required'

From d25cdbf34e808838eec6cddbc0f3347af84c60f0 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Tue, 22 Oct 2024 12:43:58 +0200
Subject: [PATCH 054/150] test: refs #6943 #6943 use command correctly

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

diff --git a/test/cypress/integration/client/clientList.spec.js b/test/cypress/integration/client/clientList.spec.js
index 909bd3933..1b41e55cd 100644
--- a/test/cypress/integration/client/clientList.spec.js
+++ b/test/cypress/integration/client/clientList.spec.js
@@ -50,7 +50,7 @@ describe('Client list', () => {
         cy.clickButtonsDescriptor(2);
         cy.waitForElement('#formModel');
         cy.waitForElement('.q-form');
-        cy.checkValueForm(1, search);
+        cy.checkValueSelectForm(1, search);
     });
     it.skip('Client founded create order', () => {
         const search = 'Jessica Jones';

From a732ec05fbe6ba834d46c8eaf3d348c9dbcc0b01 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Tue, 22 Oct 2024 13:21:16 +0200
Subject: [PATCH 055/150] test: refs #8039 add hasNotify and, refactor:
 agencyWorkCenter test

---
 src/components/FormModelPopup.vue             |  2 ++
 .../route/agency/agencyWorkCenter.spec.js     | 33 +++++++------------
 test/cypress/support/commands.js              | 11 +++++++
 3 files changed, 24 insertions(+), 22 deletions(-)

diff --git a/src/components/FormModelPopup.vue b/src/components/FormModelPopup.vue
index 118c8f5f4..d91f07535 100644
--- a/src/components/FormModelPopup.vue
+++ b/src/components/FormModelPopup.vue
@@ -61,6 +61,7 @@ defineExpose({
                     :loading="isLoading"
                     @click="emit('onDataCanceled')"
                     v-close-popup
+                    data-cy="FormModelPopup_cancel"
                 />
                 <QBtn
                     :label="t('globals.save')"
@@ -70,6 +71,7 @@ defineExpose({
                     class="q-ml-sm"
                     :disabled="isLoading"
                     :loading="isLoading"
+                    data-cy="FormModelPopup_save"
                 />
             </div>
         </template>
diff --git a/test/cypress/integration/route/agency/agencyWorkCenter.spec.js b/test/cypress/integration/route/agency/agencyWorkCenter.spec.js
index 353c5805b..6a3cab664 100644
--- a/test/cypress/integration/route/agency/agencyWorkCenter.spec.js
+++ b/test/cypress/integration/route/agency/agencyWorkCenter.spec.js
@@ -7,31 +7,20 @@ describe('AgencyWorkCenter', () => {
     const createButton = '.q-page-sticky > div > .q-btn > .q-btn__content > .q-icon';
     const workCenterCombobox = 'input[role="combobox"]';
 
-    it('assign workCenter', () => {
+    it('check workCenter crud', () => {
+        // create
         cy.get(createButton).click();
         cy.get(workCenterCombobox).type('workCenterOne{enter}');
-        cy.get('.q-notification__message').should('have.text', 'Data created');
-    });
+        cy.hasNotify('Data created');
 
-    it('delete workCenter', () => {
+        // expect error when duplicate
+        cy.get(createButton).click();
+        cy.get('[data-cy="FormModelPopup_save"]').click();
+        cy.hasNotify('This workCenter is already assigned to this agency');
+        cy.get('[data-cy="FormModelPopup_cancel"]').click();
+
+        // delete
         cy.get('.q-item__section--side > .q-btn > .q-btn__content > .q-icon').click();
-        cy.get('.q-notification__message').should(
-            'have.text',
-            'WorkCenter removed successfully'
-        );
-    });
-
-    it('error on duplicate workCenter', () => {
-        cy.get(createButton).click();
-        cy.get(workCenterCombobox).type('workCenterOne{enter}');
-        cy.get('.q-notification__message').should('have.text', 'Data created');
-        cy.get(createButton).click();
-        cy.get(
-            '.vn-row > .q-field > .q-field__inner > .q-field__control > .q-field__control-container'
-        ).type('workCenterOne{enter}');
-
-        cy.get(
-            ':nth-child(2) > .q-notification__wrapper > .q-notification__content > .q-notification__message'
-        ).should('have.text', 'This workCenter is already assigned to this agency');
+        cy.hasNotify('WorkCenter removed successfully');
     });
 });
diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js
index 83f45b721..c7b36cd3a 100755
--- a/test/cypress/support/commands.js
+++ b/test/cypress/support/commands.js
@@ -254,3 +254,14 @@ Cypress.Commands.add('openUserPanel', () => {
         '.column > .q-avatar > .q-avatar__content > .q-img > .q-img__container > .q-img__image'
     ).click();
 });
+
+Cypress.Commands.add('hasNotify', (text) => {
+    //last
+    cy.get('.q-notification')
+        .should('be.visible')
+        .last()
+        .then(($lastNotification) => {
+            if (!Cypress.$($lastNotification).text().includes(text))
+                throw new Error(`Notification not found: "${text}"`);
+        });
+});

From 2d81cffb3329bb874f32e41b3fe38c2eef6eb55e Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Tue, 22 Oct 2024 13:34:20 +0200
Subject: [PATCH 056/150] feat: refs #8039 show duplicate request in local

---
 src/boot/quasar.js | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/boot/quasar.js b/src/boot/quasar.js
index bf5175ee6..7845719fe 100644
--- a/src/boot/quasar.js
+++ b/src/boot/quasar.js
@@ -4,6 +4,7 @@ import mainShortcutMixin from './mainShortcutMixin';
 import keyShortcut from './keyShortcut';
 import useNotify from 'src/composables/useNotify.js';
 import { CanceledError } from 'axios';
+
 const { notify } = useNotify();
 
 export default boot(({ app }) => {
@@ -41,7 +42,11 @@ export default boot(({ app }) => {
         }
 
         console.error(error);
-        if (error instanceof CanceledError) return;
+        if (error instanceof CanceledError) {
+            const env = process.env.NODE_ENV;
+            if (env && env !== 'development') return;
+            message = 'Duplicate request';
+        }
 
         notify(message ?? 'globals.error', 'negative', 'error');
     };

From 0c9c01b6e9f2ea6fa3674301039eed2f6501e65d Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Tue, 22 Oct 2024 13:53:01 +0200
Subject: [PATCH 057/150] test: refs #8039 fix ZoneWarehouse e2e

---
 src/pages/Zone/Card/ZoneWarehouses.vue           | 16 ++++------------
 .../worker/workerNotificationsManager.spec.js    |  5 +----
 2 files changed, 5 insertions(+), 16 deletions(-)

diff --git a/src/pages/Zone/Card/ZoneWarehouses.vue b/src/pages/Zone/Card/ZoneWarehouses.vue
index 6b2933224..98e446797 100644
--- a/src/pages/Zone/Card/ZoneWarehouses.vue
+++ b/src/pages/Zone/Card/ZoneWarehouses.vue
@@ -34,21 +34,13 @@ const columns = computed(() => [
 ]);
 
 const deleteWarehouse = async (row) => {
-    try {
-        await axios.delete(`${urlPath.value}/${row.id}`);
-        fetchWarehouses();
-    } catch (error) {
-        console.error(error);
-    }
+    await axios.delete(`${urlPath.value}/${row.id}`);
+    fetchWarehouses();
 };
 
 const createZoneWarehouse = async (ZoneWarehouseFormData) => {
-    try {
-        await axios.post(urlPath.value, ZoneWarehouseFormData);
-        fetchWarehouses();
-    } catch (error) {
-        console.error(error);
-    }
+    await axios.post(urlPath.value, ZoneWarehouseFormData);
+    fetchWarehouses();
 };
 
 watch(
diff --git a/test/cypress/integration/worker/workerNotificationsManager.spec.js b/test/cypress/integration/worker/workerNotificationsManager.spec.js
index ac452c4ff..367287a5a 100644
--- a/test/cypress/integration/worker/workerNotificationsManager.spec.js
+++ b/test/cypress/integration/worker/workerNotificationsManager.spec.js
@@ -17,10 +17,7 @@ describe('WorkerNotificationsManager', () => {
         cy.login('developer');
         cy.visit(`/#/worker/${salesPersonId}/notifications`);
         cy.get(firstAvailableNotification).click();
-        cy.notificationHas(
-            '.q-notification__message',
-            'The notification subscription of this worker cant be modified'
-        );
+        cy.hasNotify('The notification subscription of this worker cant be modified');
     });
 
     it('should active a notification that is yours', () => {

From cd00a3c67f0fe72a4437f3a2eae3e7eae5266ebf Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Tue, 22 Oct 2024 13:55:35 +0200
Subject: [PATCH 058/150] test: refs #8039 fix WorkerNotification e2e

---
 src/pages/Worker/Card/WorkerNotificationsManager.vue | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/pages/Worker/Card/WorkerNotificationsManager.vue b/src/pages/Worker/Card/WorkerNotificationsManager.vue
index 731e073cd..53571fb93 100644
--- a/src/pages/Worker/Card/WorkerNotificationsManager.vue
+++ b/src/pages/Worker/Card/WorkerNotificationsManager.vue
@@ -44,8 +44,9 @@ async function toggleNotification(notification) {
                 `worker.notificationsManager.${notification.active ? '' : 'un'}subscribed`
             ),
         });
-    } catch {
+    } catch (e) {
         notification.active = !notification.active;
+        throw e;
     }
 }
 

From dd2dc86eea3d035aa6ee6845a1dd0047fb653db0 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Tue, 22 Oct 2024 14:05:15 +0200
Subject: [PATCH 059/150] fix: refs #8039 o not handle unnecessary errors

---
 src/pages/Account/Alias/Card/AliasUsers.vue   |  10 +-
 src/pages/Account/Card/AccountMailAlias.vue   |  20 +--
 src/pages/Account/Role/Card/SubRoles.vue      |  26 +--
 .../Card/BasicData/TicketBasicDataForm.vue    |  28 +--
 .../Card/BasicData/TicketBasicDataView.vue    |  85 ++++-----
 src/pages/Ticket/Card/ExpeditionNewTicket.vue |  32 ++--
 src/pages/Ticket/Card/TicketComponents.vue    |  26 +--
 src/pages/Ticket/Card/TicketExpedition.vue    |  58 +++----
 .../Ticket/Card/TicketSaleMoreActions.vue     |  12 +-
 src/pages/Ticket/Card/TicketSaleTracking.vue  | 164 +++++++-----------
 src/pages/Ticket/Card/TicketService.vue       |  52 +++---
 src/pages/Ticket/Card/TicketVolume.vue        |  22 +--
 src/pages/Ticket/TicketAdvanceFilter.vue      |  24 ++-
 src/pages/Ticket/TicketFutureFilter.vue       |  40 ++---
 14 files changed, 228 insertions(+), 371 deletions(-)

diff --git a/src/pages/Account/Alias/Card/AliasUsers.vue b/src/pages/Account/Alias/Card/AliasUsers.vue
index 4a9c449e4..4aad68f1a 100644
--- a/src/pages/Account/Alias/Card/AliasUsers.vue
+++ b/src/pages/Account/Alias/Card/AliasUsers.vue
@@ -46,13 +46,9 @@ const columns = computed(() => [
 ]);
 
 const deleteAlias = async (row) => {
-    try {
-        await axios.delete(`${urlPath.value}/${row.id}`);
-        notify(t('User removed'), 'positive');
-        fetchAliases();
-    } catch (error) {
-        console.error(error);
-    }
+    await axios.delete(`${urlPath.value}/${row.id}`);
+    notify(t('User removed'), 'positive');
+    fetchAliases();
 };
 
 watch(
diff --git a/src/pages/Account/Card/AccountMailAlias.vue b/src/pages/Account/Card/AccountMailAlias.vue
index 15d03c665..8d3bd3b67 100644
--- a/src/pages/Account/Card/AccountMailAlias.vue
+++ b/src/pages/Account/Card/AccountMailAlias.vue
@@ -61,23 +61,15 @@ const fetchAccountExistence = async () => {
 };
 
 const deleteMailAlias = async (row) => {
-    try {
-        await axios.delete(`${urlPath}/${row.id}`);
-        fetchMailAliases();
-        notify(t('Unsubscribed from alias!'), 'positive');
-    } catch (error) {
-        console.error(error);
-    }
+    await axios.delete(`${urlPath}/${row.id}`);
+    fetchMailAliases();
+    notify(t('Unsubscribed from alias!'), 'positive');
 };
 
 const createMailAlias = async (mailAliasFormData) => {
-    try {
-        await axios.post(urlPath, mailAliasFormData);
-        notify(t('Subscribed to alias!'), 'positive');
-        fetchMailAliases();
-    } catch (error) {
-        console.error(error);
-    }
+    await axios.post(urlPath, mailAliasFormData);
+    notify(t('Subscribed to alias!'), 'positive');
+    fetchMailAliases();
 };
 
 const fetchMailAliases = async () => {
diff --git a/src/pages/Account/Role/Card/SubRoles.vue b/src/pages/Account/Role/Card/SubRoles.vue
index d17f96dd8..6cac94667 100644
--- a/src/pages/Account/Role/Card/SubRoles.vue
+++ b/src/pages/Account/Role/Card/SubRoles.vue
@@ -46,29 +46,15 @@ const columns = computed(() => [
 ]);
 
 const deleteSubRole = async (row) => {
-    try {
-        await axios.delete(`${urlPath.value}/${row.id}`);
-        fetchSubRoles();
-        notify(
-            t('Role removed. Changes will take a while to fully propagate.'),
-            'positive'
-        );
-    } catch (error) {
-        console.error(error);
-    }
+    await axios.delete(`${urlPath.value}/${row.id}`);
+    fetchSubRoles();
+    notify(t('Role removed. Changes will take a while to fully propagate.'), 'positive');
 };
 
 const createSubRole = async (subRoleFormData) => {
-    try {
-        await axios.post(urlPath.value, subRoleFormData);
-        notify(
-            t('Role added! Changes will take a while to fully propagate.'),
-            'positive'
-        );
-        fetchSubRoles();
-    } catch (error) {
-        console.error(error);
-    }
+    await axios.post(urlPath.value, subRoleFormData);
+    notify(t('Role added! Changes will take a while to fully propagate.'), 'positive');
+    fetchSubRoles();
 };
 
 watch(
diff --git a/src/pages/Ticket/Card/BasicData/TicketBasicDataForm.vue b/src/pages/Ticket/Card/BasicData/TicketBasicDataForm.vue
index f5ce8a0f3..f6c20c514 100644
--- a/src/pages/Ticket/Card/BasicData/TicketBasicDataForm.vue
+++ b/src/pages/Ticket/Card/BasicData/TicketBasicDataForm.vue
@@ -112,32 +112,20 @@ const getShipped = async (params) => {
 };
 
 const onChangeZone = async (zoneId) => {
-    try {
-        formData.value.agencyModeFk = null;
-        const { data } = await axios.get(`Zones/${zoneId}`);
-        formData.value.agencyModeFk = data.agencyModeFk;
-    } catch (error) {
-        console.error(error);
-    }
+    formData.value.agencyModeFk = null;
+    const { data } = await axios.get(`Zones/${zoneId}`);
+    formData.value.agencyModeFk = data.agencyModeFk;
 };
 
 const onChangeAddress = async (addressId) => {
-    try {
-        formData.value.nickname = null;
-        const { data } = await axios.get(`Addresses/${addressId}`);
-        formData.value.nickname = data.nickname;
-    } catch (error) {
-        console.error(error);
-    }
+    formData.value.nickname = null;
+    const { data } = await axios.get(`Addresses/${addressId}`);
+    formData.value.nickname = data.nickname;
 };
 
 const getClientDefaultAddress = async (clientId) => {
-    try {
-        const { data } = await axios.get(`Clients/${clientId}`);
-        if (data) addressId.value = data.defaultAddressFk;
-    } catch (error) {
-        console.error(error);
-    }
+    const { data } = await axios.get(`Clients/${clientId}`);
+    if (data) addressId.value = data.defaultAddressFk;
 };
 
 const clientAddressesList = async (value) => {
diff --git a/src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue b/src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue
index 92640f898..fb7881403 100644
--- a/src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue
+++ b/src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue
@@ -70,60 +70,51 @@ const isFormInvalid = () => {
 };
 
 const getPriceDifference = async () => {
-    try {
-        const params = {
-            landed: formData.value.landed,
-            addressId: formData.value.addressFk,
-            agencyModeId: formData.value.agencyModeFk,
-            zoneId: formData.value.zoneFk,
-            warehouseId: formData.value.warehouseFk,
-            shipped: formData.value.shipped,
-        };
-        const { data } = await axios.post(
-            `tickets/${formData.value.id}/priceDifference`,
-            params
-        );
-        formData.value.sale = data;
-    } catch (error) {
-        console.error(error);
-    }
+    const params = {
+        landed: formData.value.landed,
+        addressId: formData.value.addressFk,
+        agencyModeId: formData.value.agencyModeFk,
+        zoneId: formData.value.zoneFk,
+        warehouseId: formData.value.warehouseFk,
+        shipped: formData.value.shipped,
+    };
+    const { data } = await axios.post(
+        `tickets/${formData.value.id}/priceDifference`,
+        params
+    );
+    formData.value.sale = data;
 };
 
 const submit = async () => {
-    try {
-        if (!formData.value.option)
-            return notify(t('basicData.chooseAnOption'), 'negative');
+    if (!formData.value.option) return notify(t('basicData.chooseAnOption'), 'negative');
 
-        const params = {
-            clientFk: formData.value.clientFk,
-            nickname: formData.value.nickname,
-            agencyModeFk: formData.value.agencyModeFk,
-            addressFk: formData.value.addressFk,
-            zoneFk: formData.value.zoneFk,
-            warehouseFk: formData.value.warehouseFk,
-            companyFk: formData.value.companyFk,
-            shipped: formData.value.shipped,
-            landed: formData.value.landed,
-            isDeleted: formData.value.isDeleted,
-            option: formData.value.option,
-            isWithoutNegatives: formData.value.withoutNegatives,
-            withWarningAccept: formData.value.withWarningAccept,
-            keepPrice: false,
-        };
+    const params = {
+        clientFk: formData.value.clientFk,
+        nickname: formData.value.nickname,
+        agencyModeFk: formData.value.agencyModeFk,
+        addressFk: formData.value.addressFk,
+        zoneFk: formData.value.zoneFk,
+        warehouseFk: formData.value.warehouseFk,
+        companyFk: formData.value.companyFk,
+        shipped: formData.value.shipped,
+        landed: formData.value.landed,
+        isDeleted: formData.value.isDeleted,
+        option: formData.value.option,
+        isWithoutNegatives: formData.value.withoutNegatives,
+        withWarningAccept: formData.value.withWarningAccept,
+        keepPrice: false,
+    };
 
-        const { data } = await axios.post(
-            `tickets/${formData.value.id}/componentUpdate`,
-            params
-        );
+    const { data } = await axios.post(
+        `tickets/${formData.value.id}/componentUpdate`,
+        params
+    );
 
-        if (!data) return;
+    if (!data) return;
 
-        const ticketToMove = data.id;
-        notify(t('basicData.unroutedTicket'), 'positive');
-        router.push({ name: 'TicketSummary', params: { id: ticketToMove } });
-    } catch (error) {
-        console.error(error);
-    }
+    const ticketToMove = data.id;
+    notify(t('basicData.unroutedTicket'), 'positive');
+    router.push({ name: 'TicketSummary', params: { id: ticketToMove } });
 };
 
 const submitWithNegatives = async () => {
diff --git a/src/pages/Ticket/Card/ExpeditionNewTicket.vue b/src/pages/Ticket/Card/ExpeditionNewTicket.vue
index 9183ae405..c288f6cc2 100644
--- a/src/pages/Ticket/Card/ExpeditionNewTicket.vue
+++ b/src/pages/Ticket/Card/ExpeditionNewTicket.vue
@@ -34,26 +34,20 @@ const newTicketFormData = reactive({});
 const date = new Date();
 
 const createTicket = async () => {
-    try {
-        const expeditionIds = $props.selectedExpeditions.map(
-            (expedition) => expedition.id
-        );
-        const params = {
-            clientId: $props.ticket.clientFk,
-            landed: newTicketFormData.landed,
-            warehouseId: $props.ticket.warehouseFk,
-            addressId: $props.ticket.addressFk,
-            agencyModeId: $props.ticket.agencyModeFk,
-            routeId: newTicketFormData.routeFk,
-            expeditionIds: expeditionIds,
-        };
+    const expeditionIds = $props.selectedExpeditions.map((expedition) => expedition.id);
+    const params = {
+        clientId: $props.ticket.clientFk,
+        landed: newTicketFormData.landed,
+        warehouseId: $props.ticket.warehouseFk,
+        addressId: $props.ticket.addressFk,
+        agencyModeId: $props.ticket.agencyModeFk,
+        routeId: newTicketFormData.routeFk,
+        expeditionIds: expeditionIds,
+    };
 
-        const { data } = await axios.post('Expeditions/moveExpeditions', params);
-        notify(t('globals.dataSaved'), 'positive');
-        router.push({ name: 'TicketSummary', params: { id: data.id } });
-    } catch (error) {
-        console.error(error);
-    }
+    const { data } = await axios.post('Expeditions/moveExpeditions', params);
+    notify(t('globals.dataSaved'), 'positive');
+    router.push({ name: 'TicketSummary', params: { id: data.id } });
 };
 </script>
 
diff --git a/src/pages/Ticket/Card/TicketComponents.vue b/src/pages/Ticket/Card/TicketComponents.vue
index 0bccdaacd..b5b3c430c 100644
--- a/src/pages/Ticket/Card/TicketComponents.vue
+++ b/src/pages/Ticket/Card/TicketComponents.vue
@@ -150,31 +150,19 @@ const getTotal = computed(() => {
 });
 
 const getComponentsSum = async () => {
-    try {
-        const { data } = await axios.get(`Tickets/${route.params.id}/getComponentsSum`);
-        componentsList.value = data;
-    } catch (error) {
-        console.error(error);
-    }
+    const { data } = await axios.get(`Tickets/${route.params.id}/getComponentsSum`);
+    componentsList.value = data;
 };
 
 const getTheoricalCost = async () => {
-    try {
-        const { data } = await axios.get(`Tickets/${route.params.id}/freightCost`);
-        theoricalCost.value = data;
-    } catch (error) {
-        console.error(error);
-    }
+    const { data } = await axios.get(`Tickets/${route.params.id}/freightCost`);
+    theoricalCost.value = data;
 };
 
 const getTicketVolume = async () => {
-    try {
-        if (!ticketData.value) return;
-        const { data } = await axios.get(`Tickets/${ticketData.value.id}/getVolume`);
-        ticketVolume.value = data[0].volume;
-    } catch (error) {
-        console.error(error);
-    }
+    if (!ticketData.value) return;
+    const { data } = await axios.get(`Tickets/${ticketData.value.id}/getVolume`);
+    ticketVolume.value = data[0].volume;
 };
 
 onMounted(() => {
diff --git a/src/pages/Ticket/Card/TicketExpedition.vue b/src/pages/Ticket/Card/TicketExpedition.vue
index 4becb3db3..b4a2ca732 100644
--- a/src/pages/Ticket/Card/TicketExpedition.vue
+++ b/src/pages/Ticket/Card/TicketExpedition.vue
@@ -187,18 +187,12 @@ const showNewTicketDialog = (withRoute = false) => {
 };
 
 const deleteExpedition = async () => {
-    try {
-        const expeditionIds = selectedExpeditions.value.map(
-            (expedition) => expedition.id
-        );
-        const params = { expeditionIds };
-        await axios.post('Expeditions/deleteExpeditions', params);
-        await refetchExpeditions();
-        selectedExpeditions.value = [];
-        notify(t('expedition.expeditionRemoved'), 'positive');
-    } catch (error) {
-        console.error(error);
-    }
+    const expeditionIds = selectedExpeditions.value.map((expedition) => expedition.id);
+    const params = { expeditionIds };
+    await axios.post('Expeditions/deleteExpeditions', params);
+    await refetchExpeditions();
+    selectedExpeditions.value = [];
+    notify(t('expedition.expeditionRemoved'), 'positive');
 };
 
 const showLog = async (expedition) => {
@@ -207,29 +201,25 @@ const showLog = async (expedition) => {
 };
 
 const getExpeditionState = async (expedition) => {
-    try {
-        const filter = {
-            where: { expeditionFk: expedition.id },
-            order: ['created DESC'],
+    const filter = {
+        where: { expeditionFk: expedition.id },
+        order: ['created DESC'],
+    };
+
+    const { data: expeditionStates } = await axios.get(`ExpeditionStates/filter`, {
+        params: { filter: JSON.stringify(filter) },
+    });
+    const { data: scannedStates } = await axios.get(`ExpeditionStates`, {
+        params: { filter: JSON.stringify(filter), fields: ['id', 'isScanned'] },
+    });
+
+    expeditionsLogsData.value = expeditionStates.map((state) => {
+        const scannedState = scannedStates.find((s) => s.id === state.id);
+        return {
+            ...state,
+            isScanned: scannedState ? scannedState.isScanned : false,
         };
-
-        const { data: expeditionStates } = await axios.get(`ExpeditionStates/filter`, {
-            params: { filter: JSON.stringify(filter) },
-        });
-        const { data: scannedStates } = await axios.get(`ExpeditionStates`, {
-            params: { filter: JSON.stringify(filter), fields: ['id', 'isScanned'] },
-        });
-
-        expeditionsLogsData.value = expeditionStates.map((state) => {
-            const scannedState = scannedStates.find((s) => s.id === state.id);
-            return {
-                ...state,
-                isScanned: scannedState ? scannedState.isScanned : false,
-            };
-        });
-    } catch (error) {
-        console.error(error);
-    }
+    });
 };
 
 onMounted(async () => {
diff --git a/src/pages/Ticket/Card/TicketSaleMoreActions.vue b/src/pages/Ticket/Card/TicketSaleMoreActions.vue
index 2ec519d2d..588f78a7b 100644
--- a/src/pages/Ticket/Card/TicketSaleMoreActions.vue
+++ b/src/pages/Ticket/Card/TicketSaleMoreActions.vue
@@ -165,14 +165,10 @@ const createRefund = async (withWarehouse) => {
         negative: true,
     };
 
-    try {
-        const { data } = await axios.post('Tickets/cloneAll', params);
-        const [refundTicket] = data;
-        notify(t('refundTicketCreated', { ticketId: refundTicket.id }), 'positive');
-        push({ name: 'TicketSale', params: { id: refundTicket.id } });
-    } catch (error) {
-        console.error(error);
-    }
+    const { data } = await axios.post('Tickets/cloneAll', params);
+    const [refundTicket] = data;
+    notify(t('refundTicketCreated', { ticketId: refundTicket.id }), 'positive');
+    push({ name: 'TicketSale', params: { id: refundTicket.id } });
 };
 </script>
 
diff --git a/src/pages/Ticket/Card/TicketSaleTracking.vue b/src/pages/Ticket/Card/TicketSaleTracking.vue
index e7830bf37..1083393c4 100644
--- a/src/pages/Ticket/Card/TicketSaleTracking.vue
+++ b/src/pages/Ticket/Card/TicketSaleTracking.vue
@@ -150,18 +150,14 @@ const shelvingsTableColumns = computed(() => [
 ]);
 
 const getSaleTrackings = async (sale) => {
-    try {
-        const filter = {
-            where: { saleFk: sale.saleFk },
-            order: ['itemFk DESC'],
-        };
-        const { data } = await axios.get(`SaleTrackings/listSaleTracking`, {
-            params: { filter: JSON.stringify(filter) },
-        });
-        saleTrackings.value = data;
-    } catch (error) {
-        console.error(error);
-    }
+    const filter = {
+        where: { saleFk: sale.saleFk },
+        order: ['itemFk DESC'],
+    };
+    const { data } = await axios.get(`SaleTrackings/listSaleTracking`, {
+        params: { filter: JSON.stringify(filter) },
+    });
+    saleTrackings.value = data;
 };
 
 const showLog = async (sale) => {
@@ -170,17 +166,13 @@ const showLog = async (sale) => {
 };
 
 const getItemShelvingSales = async (sale) => {
-    try {
-        const filter = {
-            where: { saleFk: sale.saleFk },
-        };
-        const { data } = await axios.get(`ItemShelvingSales/filter`, {
-            params: { filter: JSON.stringify(filter) },
-        });
-        itemShelvingsSales.value = data;
-    } catch (error) {
-        console.error(error);
-    }
+    const filter = {
+        where: { saleFk: sale.saleFk },
+    };
+    const { data } = await axios.get(`ItemShelvingSales/filter`, {
+        params: { filter: JSON.stringify(filter) },
+    });
+    itemShelvingsSales.value = data;
 };
 
 const showShelving = async (sale) => {
@@ -189,36 +181,28 @@ const showShelving = async (sale) => {
 };
 
 const updateQuantity = async (sale) => {
-    try {
-        if (oldQuantity.value === sale.quantity) return;
-        const params = {
-            quantity: sale.quantity,
-        };
-        await axios.patch(`ItemShelvingSales/${sale.id}`, params);
-        oldQuantity.value = null;
-    } catch (error) {
-        console.error(error);
-    }
+    if (oldQuantity.value === sale.quantity) return;
+    const params = {
+        quantity: sale.quantity,
+    };
+    await axios.patch(`ItemShelvingSales/${sale.id}`, params);
+    oldQuantity.value = null;
 };
 
 const updateParking = async (sale) => {
-    try {
-        const filter = {
-            fields: ['id'],
-            where: {
-                code: sale.shelvingFk,
-            },
-        };
-        const { data } = await axios.get(`Shelvings/findOne`, {
-            params: { filter: JSON.stringify(filter) },
-        });
-        const params = {
-            parkingFk: sale.parkingFk,
-        };
-        await axios.patch(`Shelvings/${data.id}`, params);
-    } catch (error) {
-        console.error(error);
-    }
+    const filter = {
+        fields: ['id'],
+        where: {
+            code: sale.shelvingFk,
+        },
+    };
+    const { data } = await axios.get(`Shelvings/findOne`, {
+        params: { filter: JSON.stringify(filter) },
+    });
+    const params = {
+        parkingFk: sale.parkingFk,
+    };
+    await axios.patch(`Shelvings/${data.id}`, params);
 };
 
 const updateShelving = async (sale) => {
@@ -241,61 +225,41 @@ const updateShelving = async (sale) => {
 };
 
 const saleTrackingNew = async (sale, stateCode, isChecked) => {
-    try {
-        const params = {
-            saleFk: sale.saleFk,
-            isChecked,
-            quantity: sale.quantity,
-            stateCode,
-        };
-        await axios.post(`SaleTrackings/new`, params);
-        notify(t('globals.dataSaved'), 'positive');
-    } catch (error) {
-        console.error(error);
-    }
+    const params = {
+        saleFk: sale.saleFk,
+        isChecked,
+        quantity: sale.quantity,
+        stateCode,
+    };
+    await axios.post(`SaleTrackings/new`, params);
+    notify(t('globals.dataSaved'), 'positive');
 };
 
 const saleTrackingDel = async ({ saleFk }, stateCode) => {
-    try {
-        const params = {
-            saleFk,
-            stateCodes: [stateCode],
-        };
-        await axios.post(`SaleTrackings/delete`, params);
-        notify(t('globals.dataSaved'), 'positive');
-    } catch (error) {
-        console.error(error);
-    }
+    const params = {
+        saleFk,
+        stateCodes: [stateCode],
+    };
+    await axios.post(`SaleTrackings/delete`, params);
+    notify(t('globals.dataSaved'), 'positive');
 };
 
 const clickSaleGroupDetail = async (sale) => {
-    try {
-        if (!sale.saleGroupDetailFk) return;
+    if (!sale.saleGroupDetailFk) return;
 
-        await axios.delete(`SaleGroupDetails/${sale.saleGroupDetailFk}`);
-        sale.hasSaleGroupDetail = false;
-        notify(t('globals.dataSaved'), 'positive');
-    } catch (error) {
-        console.error(error);
-    }
+    await axios.delete(`SaleGroupDetails/${sale.saleGroupDetailFk}`);
+    sale.hasSaleGroupDetail = false;
+    notify(t('globals.dataSaved'), 'positive');
 };
 
 const clickPreviousSelected = (sale) => {
-    try {
-        qCheckBoxController(sale, 'isPreviousSelected');
-        if (!sale.isPreviousSelected) sale.isPrevious = false;
-    } catch (error) {
-        console.error(error);
-    }
+    qCheckBoxController(sale, 'isPreviousSelected');
+    if (!sale.isPreviousSelected) sale.isPrevious = false;
 };
 
 const clickPrevious = (sale) => {
-    try {
-        qCheckBoxController(sale, 'isPrevious');
-        if (sale.isPrevious) sale.isPreviousSelected = true;
-    } catch (error) {
-        console.error(error);
-    }
+    qCheckBoxController(sale, 'isPrevious');
+    if (sale.isPrevious) sale.isPreviousSelected = true;
 };
 
 const qCheckBoxController = (sale, action) => {
@@ -306,16 +270,12 @@ const qCheckBoxController = (sale, action) => {
         isPreviousSelected: 'PREVIOUS_PREPARATION',
     };
     const stateCode = STATE_CODES[action];
-    try {
-        if (!sale[action]) {
-            saleTrackingNew(sale, stateCode, true);
-            sale[action] = true;
-        } else {
-            saleTrackingDel(sale, stateCode);
-            sale[action] = false;
-        }
-    } catch (error) {
-        console.error(error);
+    if (!sale[action]) {
+        saleTrackingNew(sale, stateCode, true);
+        sale[action] = true;
+    } else {
+        saleTrackingDel(sale, stateCode);
+        sale[action] = false;
     }
 };
 </script>
diff --git a/src/pages/Ticket/Card/TicketService.vue b/src/pages/Ticket/Card/TicketService.vue
index 45a870f7f..47c28a422 100644
--- a/src/pages/Ticket/Card/TicketService.vue
+++ b/src/pages/Ticket/Card/TicketService.vue
@@ -46,40 +46,32 @@ watch(
 onMounted(async () => await getDefaultTaxClass());
 
 const createRefund = async () => {
-    try {
-        if (!selected.value.length) return;
+    if (!selected.value.length) return;
 
-        const params = {
-            servicesIds: selected.value.map((s) => +s.id),
-            withWarehouse: false,
-            negative: true,
-        };
-        const { data } = await axios.post('Sales/clone', params);
-        const [refundTicket] = data;
-        notify(
-            t('service.createRefundSuccess', {
-                ticketId: refundTicket.id,
-            }),
-            'positive'
-        );
-        router.push({ name: 'TicketSale', params: { id: refundTicket.id } });
-    } catch (error) {
-        console.error(error);
-    }
+    const params = {
+        servicesIds: selected.value.map((s) => +s.id),
+        withWarehouse: false,
+        negative: true,
+    };
+    const { data } = await axios.post('Sales/clone', params);
+    const [refundTicket] = data;
+    notify(
+        t('service.createRefundSuccess', {
+            ticketId: refundTicket.id,
+        }),
+        'positive'
+    );
+    router.push({ name: 'TicketSale', params: { id: refundTicket.id } });
 };
 
 const getDefaultTaxClass = async () => {
-    try {
-        let filter = {
-            where: { code: 'G' },
-        };
-        const { data } = await axios.get('TaxClasses/findOne', {
-            params: { filter: JSON.stringify(filter) },
-        });
-        defaultTaxClass.value = data;
-    } catch (error) {
-        console.error(error);
-    }
+    let filter = {
+        where: { code: 'G' },
+    };
+    const { data } = await axios.get('TaxClasses/findOne', {
+        params: { filter: JSON.stringify(filter) },
+    });
+    defaultTaxClass.value = data;
 };
 
 const columns = computed(() => [
diff --git a/src/pages/Ticket/Card/TicketVolume.vue b/src/pages/Ticket/Card/TicketVolume.vue
index 2cf7ffc42..edfe489d9 100644
--- a/src/pages/Ticket/Card/TicketVolume.vue
+++ b/src/pages/Ticket/Card/TicketVolume.vue
@@ -75,22 +75,18 @@ const columns = computed(() => [
 ]);
 
 const applyVolumes = async (salesData) => {
-    try {
-        if (!salesData.length) return;
+    if (!salesData.length) return;
 
-        sales.value = salesData;
-        const ticket = sales.value[0].ticketFk;
-        const { data } = await axios.get(`Tickets/${ticket}/getVolume`);
-        const volumes = new Map(data.saleVolume.map((volume) => [volume.saleFk, volume]));
+    sales.value = salesData;
+    const ticket = sales.value[0].ticketFk;
+    const { data } = await axios.get(`Tickets/${ticket}/getVolume`);
+    const volumes = new Map(data.saleVolume.map((volume) => [volume.saleFk, volume]));
 
-        sales.value.forEach((sale) => {
-            sale.saleVolume = volumes.get(sale.id);
-        });
+    sales.value.forEach((sale) => {
+        sale.saleVolume = volumes.get(sale.id);
+    });
 
-        packingTypeVolume.value = data.packingTypeVolume;
-    } catch (error) {
-        console.error(error);
-    }
+    packingTypeVolume.value = data.packingTypeVolume;
 };
 
 onMounted(() => (stateStore.rightDrawer = true));
diff --git a/src/pages/Ticket/TicketAdvanceFilter.vue b/src/pages/Ticket/TicketAdvanceFilter.vue
index b25ebdea6..27cacd80a 100644
--- a/src/pages/Ticket/TicketAdvanceFilter.vue
+++ b/src/pages/Ticket/TicketAdvanceFilter.vue
@@ -27,20 +27,16 @@ const warehousesOptions = ref([]);
 const itemPackingTypes = ref([]);
 
 const getItemPackingTypes = async () => {
-    try {
-        const filter = {
-            where: { isActive: true },
-        };
-        const { data } = await axios.get('ItemPackingTypes', {
-            params: { filter: JSON.stringify(filter) },
-        });
-        itemPackingTypes.value = data.map((ipt) => ({
-            description: t(ipt.description),
-            code: ipt.code,
-        }));
-    } catch (error) {
-        console.error(error);
-    }
+    const filter = {
+        where: { isActive: true },
+    };
+    const { data } = await axios.get('ItemPackingTypes', {
+        params: { filter: JSON.stringify(filter) },
+    });
+    itemPackingTypes.value = data.map((ipt) => ({
+        description: t(ipt.description),
+        code: ipt.code,
+    }));
 };
 
 onMounted(async () => await getItemPackingTypes());
diff --git a/src/pages/Ticket/TicketFutureFilter.vue b/src/pages/Ticket/TicketFutureFilter.vue
index 6345f62b3..ffe967272 100644
--- a/src/pages/Ticket/TicketFutureFilter.vue
+++ b/src/pages/Ticket/TicketFutureFilter.vue
@@ -24,33 +24,25 @@ const itemPackingTypes = ref([]);
 const stateOptions = ref([]);
 
 const getItemPackingTypes = async () => {
-    try {
-        const filter = {
-            where: { isActive: true },
-        };
-        const { data } = await axios.get('ItemPackingTypes', {
-            params: { filter: JSON.stringify(filter) },
-        });
-        itemPackingTypes.value = data.map((ipt) => ({
-            description: t(ipt.description),
-            code: ipt.code,
-        }));
-    } catch (error) {
-        console.error(error);
-    }
+    const filter = {
+        where: { isActive: true },
+    };
+    const { data } = await axios.get('ItemPackingTypes', {
+        params: { filter: JSON.stringify(filter) },
+    });
+    itemPackingTypes.value = data.map((ipt) => ({
+        description: t(ipt.description),
+        code: ipt.code,
+    }));
 };
 
 const getGroupedStates = async () => {
-    try {
-        const { data } = await axios.get('AlertLevels');
-        stateOptions.value = data.map((state) => ({
-            id: state.id,
-            name: t(`futureTickets.${state.code}`),
-            code: state.code,
-        }));
-    } catch (error) {
-        console.error(error);
-    }
+    const { data } = await axios.get('AlertLevels');
+    stateOptions.value = data.map((state) => ({
+        id: state.id,
+        name: t(`futureTickets.${state.code}`),
+        code: state.code,
+    }));
 };
 
 onMounted(async () => {

From ee31bc8262d511bc659316d7046b5f9d0228034d Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Tue, 22 Oct 2024 14:07:27 +0200
Subject: [PATCH 060/150] chore: refs #8039 not required

---
 src/boot/axios.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/boot/axios.js b/src/boot/axios.js
index b084b835d..aee38e887 100644
--- a/src/boot/axios.js
+++ b/src/boot/axios.js
@@ -3,7 +3,6 @@ import { useSession } from 'src/composables/useSession';
 import { Router } from 'src/router';
 import useNotify from 'src/composables/useNotify.js';
 import { useStateQueryStore } from 'src/stores/useStateQueryStore';
-import { CanceledError } from 'axios';
 
 const session = useSession();
 const { notify } = useNotify();

From 4bde0dffa734f0588db077708cb5a098d40c9a97 Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Wed, 23 Oct 2024 09:17:14 +0200
Subject: [PATCH 061/150] fix: fixed item packaging type field showing
 repetitive values

---
 .../components/CustomerSummaryTable.vue       | 19 ++++++++++++-------
 1 file changed, 12 insertions(+), 7 deletions(-)

diff --git a/src/pages/Customer/components/CustomerSummaryTable.vue b/src/pages/Customer/components/CustomerSummaryTable.vue
index f6013dea9..ff1474fb9 100644
--- a/src/pages/Customer/components/CustomerSummaryTable.vue
+++ b/src/pages/Customer/components/CustomerSummaryTable.vue
@@ -101,7 +101,7 @@ const columns = computed(() => [
         align: 'left',
         name: 'itemPackingTypeFk',
         label: t('ticketSale.packaging'),
-        format: (row) => getItemPackagingType(row),
+        format: (row) => getItemPackagingType(row.ticketSales),
     },
     {
         align: 'right',
@@ -151,13 +151,18 @@ const setShippedColor = (date) => {
     if (difference < 0) return 'success';
 };
 
-const getItemPackagingType = (row) => {
-    const packagingType = row?.ticketSales
-        .map((sale) => sale.item?.itemPackingTypeFk || '-')
-        .filter((value) => value !== '-')
-        .join(', ');
+const getItemPackagingType = (ticketSales) => {
+    if (!ticketSales || ticketSales.length === 0) return '-';
 
-    return dashIfEmpty(packagingType);
+    const packagingTypes = ticketSales
+        .map((sale) => sale.item?.itemPackingTypeFk)
+        .filter((type) => type !== undefined && type !== null);
+
+    const uniquePackagingTypes = [...new Set(packagingTypes)];
+
+    return dashIfEmpty(
+        uniquePackagingTypes.length > 0 ? uniquePackagingTypes.join(', ') : '-'
+    );
 };
 </script>
 

From b40af0ce7bb73660464db2b2b945261920528ece Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Wed, 23 Oct 2024 09:49:48 +0200
Subject: [PATCH 062/150] fix: refs #7283 fix pr

---
 src/pages/Item/Card/ItemBasicData.vue  |  8 ++-
 src/pages/Item/Card/ItemBotanical.vue  |  7 ---
 src/pages/Item/Card/ItemDescriptor.vue | 13 ++++
 src/pages/Item/Card/ItemTax.vue        |  4 +-
 src/pages/Item/ItemRequest.vue         | 34 ++++++++++-
 src/pages/Item/ItemTypeList.vue        | 85 +++++++++-----------------
 6 files changed, 84 insertions(+), 67 deletions(-)

diff --git a/src/pages/Item/Card/ItemBasicData.vue b/src/pages/Item/Card/ItemBasicData.vue
index 6b6d89c6d..1b0342668 100644
--- a/src/pages/Item/Card/ItemBasicData.vue
+++ b/src/pages/Item/Card/ItemBasicData.vue
@@ -85,13 +85,19 @@ const onIntrastatCreated = (response, formData) => {
                 <VnInput :label="t('item.basicData.reference')" v-model="data.comment" />
                 <VnInput
                     :label="t('item.basicData.relevancy')"
+                    type="number"
                     v-model="data.relevancy"
                 />
             </VnRow>
             <VnRow class="row q-gutter-md q-mb-md">
-                <VnInput :label="t('item.basicData.stems')" v-model="data.stems" />
+                <VnInput
+                    :label="t('item.basicData.stems')"
+                    type="number"
+                    v-model="data.stems"
+                />
                 <VnInput
                     :label="t('item.basicData.multiplier')"
+                    type="number"
                     v-model="data.stemMultiplier"
                 />
                 <VnSelectDialog
diff --git a/src/pages/Item/Card/ItemBotanical.vue b/src/pages/Item/Card/ItemBotanical.vue
index 5f7f3a5e4..1aa6faed1 100644
--- a/src/pages/Item/Card/ItemBotanical.vue
+++ b/src/pages/Item/Card/ItemBotanical.vue
@@ -20,15 +20,8 @@ let itemBotanicalsForm = reactive({ itemFk: null });
 const entityId = computed(() => {
     return route.params.id;
 });
-// onMounted(async () => {
-//     itemBotanicalsForm.itemFk = entityId.value;
-//     // itemBotanicals.value = await itemBotanicalsRef.value.fetch();
-//     if (itemBotanicals.value.length > 0)
-//         Object.assign(itemBotanicalsForm, itemBotanicals.value[0]);
-// });
 async function handleItemBotanical(data) {
     itemBotanicalsForm = data;
-    // if (data.length > 0) Object.assign(itemBotanicalsForm, itemBotanicals.value[0]);
 }
 </script>
 <template>
diff --git a/src/pages/Item/Card/ItemDescriptor.vue b/src/pages/Item/Card/ItemDescriptor.vue
index 09d6a8a4f..aa9795a74 100644
--- a/src/pages/Item/Card/ItemDescriptor.vue
+++ b/src/pages/Item/Card/ItemDescriptor.vue
@@ -189,6 +189,18 @@ const openCloneDialog = async () => {
                 :value="entity.value7"
             />
         </template>
+        <template #icons="{ entity }">
+            <QCardActions v-if="entity" class="q-gutter-x-md">
+                <QIcon
+                    v-if="!entity.isActive"
+                    name="vn:unavailable"
+                    color="primary"
+                    size="xs"
+                >
+                    <QTooltip>{{ t('Inactive article') }}</QTooltip>
+                </QIcon>
+            </QCardActions>
+        </template>
         <template #actions="{}">
             <QCardActions class="row justify-center">
                 <QBtn
@@ -213,6 +225,7 @@ es:
     Regularize stock: Regularizar stock
     All its properties will be copied: Todas sus propiedades serán copiadas
     Do you want to clone this item?: ¿Desea clonar este artículo?
+    Inactive article: Artículo inactivo
 </i18n>
 
 <style lang="scss" scoped>
diff --git a/src/pages/Item/Card/ItemTax.vue b/src/pages/Item/Card/ItemTax.vue
index 489a2c7b2..9050db42e 100644
--- a/src/pages/Item/Card/ItemTax.vue
+++ b/src/pages/Item/Card/ItemTax.vue
@@ -22,7 +22,7 @@ const taxesFilter = {
         {
             relation: 'country',
             scope: {
-                fields: ['country'],
+                fields: ['name'],
             },
         },
     ],
@@ -73,7 +73,7 @@ const submitTaxes = async (data) => {
                 >
                     <VnInput
                         :label="t('tax.country')"
-                        v-model="row.country.country"
+                        v-model="row.country.name"
                         disable
                     />
                     <VnSelect
diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue
index 0eba6f9a4..ea265e706 100644
--- a/src/pages/Item/ItemRequest.vue
+++ b/src/pages/Item/ItemRequest.vue
@@ -5,7 +5,7 @@ import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.v
 import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
 import { useStateStore } from 'stores/useStateStore';
 import { useArrayData } from 'composables/useArrayData';
-import { toCurrency } from 'filters/index';
+import { dashIfEmpty, toCurrency } from 'filters/index';
 import useNotify from 'src/composables/useNotify.js';
 import axios from 'axios';
 import ItemRequestDenyForm from './ItemRequestDenyForm.vue';
@@ -134,6 +134,20 @@ const columns = computed(() => [
     },
 ]);
 
+const getBadgeColor = (date) => {
+    const today = Date.vnNew();
+    today.setHours(0, 0, 0, 0);
+
+    const orderLanded = new Date(date);
+    orderLanded.setHours(0, 0, 0, 0);
+
+    const difference = today - orderLanded;
+
+    if (difference == 0) return 'warning';
+    if (difference < 0) return 'success';
+    if (difference > 0) return 'alert';
+};
+
 const changeQuantity = async (request) => {
     try {
         if (request.saleFk) {
@@ -212,6 +226,24 @@ onMounted(async () => {
         auto-load
         :disable-option="{ card: true }"
     >
+        <template #column-ticketFk="{ row }">
+            <span class="link">
+                {{ row.ticketFk }}
+                <TicketDescriptorProxy :id="row.ticketFk" />
+            </span>
+        </template>
+        <template #column-shipped="{ row }">
+            <QTd>
+                <QBadge
+                    :color="getBadgeColor(row.shipped)"
+                    text-color="black"
+                    class="q-pa-sm"
+                    style="font-size: 14px"
+                >
+                    {{ toDate(row.shipped) }}
+                </QBadge>
+            </QTd>
+        </template>
         <template #column-attenderName="{ row }">
             <span class="link" @click.stop>
                 {{ row.attenderName }}
diff --git a/src/pages/Item/ItemTypeList.vue b/src/pages/Item/ItemTypeList.vue
index 5ebbec62b..9981a0d68 100644
--- a/src/pages/Item/ItemTypeList.vue
+++ b/src/pages/Item/ItemTypeList.vue
@@ -1,54 +1,14 @@
 <script setup>
 import { useI18n } from 'vue-i18n';
-import { useRouter } from 'vue-router';
+import { useRoute } from 'vue-router';
 import { ref, computed } from 'vue';
 import ItemTypeSearchbar from '../ItemType/ItemTypeSearchbar.vue';
 import VnTable from 'components/VnTable/VnTable.vue';
 
-const router = useRouter();
+const route = useRoute();
 const { t } = useI18n();
 const tableRef = ref();
-const redirectToItemTypeSummary = (id) => {
-    router.push({ name: 'ItemTypeSummary', params: { id } });
-};
-
-const redirectToCreateView = () => {
-    router.push({ name: 'ItemTypeCreate' });
-};
-
-const exprBuilder = (param, value) => {
-    switch (param) {
-        case 'name':
-            return {
-                name: { like: `%${value}%` },
-            };
-        case 'code':
-            return {
-                code: { like: `%${value}%` },
-            };
-        case 'search':
-            if (value) {
-                if (!isNaN(value)) {
-                    return { id: value };
-                } else {
-                    return {
-                        or: [
-                            {
-                                name: {
-                                    like: `%${value}%`,
-                                },
-                            },
-                            {
-                                code: {
-                                    like: `%${value}%`,
-                                },
-                            },
-                        ],
-                    };
-                }
-            }
-    }
-};
+const entityId = computed(() => route.params.id);
 
 const columns = computed(() => [
     {
@@ -75,20 +35,21 @@ const columns = computed(() => [
     },
     {
         align: 'left',
-        name: 'worker',
+        name: 'workerFk',
         label: t('worker'),
         create: true,
         component: 'select',
         attrs: {
             url: 'Workers',
-            fields: ['id', 'firstName'],
+            optionLabel: 'firstName',
+            optionValue: 'id',
         },
-        cardVisible: true,
+        cardVisible: false,
         visible: false,
     },
     {
         align: 'left',
-        name: 'ItemCategory',
+        name: 'categoryFk',
         label: t('ItemCategory'),
         create: true,
         component: 'select',
@@ -96,7 +57,7 @@ const columns = computed(() => [
             url: 'ItemCategories',
             fields: ['id', 'name'],
         },
-        cardVisible: true,
+        cardVisible: false,
         visible: false,
     },
     {
@@ -109,7 +70,7 @@ const columns = computed(() => [
             url: 'Temperatures',
             fields: ['id', 'name'],
         },
-        cardVisible: true,
+        cardVisible: false,
         visible: false,
     },
 ]);
@@ -121,18 +82,13 @@ const columns = computed(() => [
         ref="tableRef"
         data-key="ItemTypeList"
         :url="`ItemTypes`"
-        :url-create="`ItemTypes`"
-        save-url="ItemTypes/crud"
-        :filter="courseFilter"
         :create="{
             urlCreate: 'ItemTypes',
             title: 'Create ItemTypes',
             onDataSaved: () => tableRef.reload(),
-            formInitialData: {
-                workerFk: entityId,
-            },
+            formInitialData: {},
         }"
-        order="id DESC"
+        order="code ASC"
         :columns="columns"
         auto-load
         :right-search="false"
@@ -140,3 +96,20 @@ const columns = computed(() => [
         :use-model="true"
     />
 </template>
+
+<i18n>
+    es:
+        id: Id
+        code: Código
+        name: Nombre
+        worker: Encargado
+        ItemCategory: Categoría
+        Temperature: Temperatura
+        Create ItemTypes: Crear familia
+    en:
+        code: Code
+        name: Name
+        worker: Worker
+        ItemCategory: ItemCategory
+        Temperature: Temperature
+</i18n>

From d8b80cfa6d31087bee361d9b48a999b3f199e59a Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Wed, 23 Oct 2024 12:30:04 +0200
Subject: [PATCH 063/150] fix: refs #7283 fix pr

---
 src/pages/Item/Card/ItemBotanical.vue  | 5 +----
 src/pages/Item/Card/ItemDescriptor.vue | 2 --
 2 files changed, 1 insertion(+), 6 deletions(-)

diff --git a/src/pages/Item/Card/ItemBotanical.vue b/src/pages/Item/Card/ItemBotanical.vue
index 1aa6faed1..c4b561772 100644
--- a/src/pages/Item/Card/ItemBotanical.vue
+++ b/src/pages/Item/Card/ItemBotanical.vue
@@ -20,9 +20,6 @@ let itemBotanicalsForm = reactive({ itemFk: null });
 const entityId = computed(() => {
     return route.params.id;
 });
-async function handleItemBotanical(data) {
-    itemBotanicalsForm = data;
-}
 </script>
 <template>
     <FetchData
@@ -41,7 +38,7 @@ async function handleItemBotanical(data) {
         :filter="{
             where: { itemFk: entityId },
         }"
-        @on-fetch="handleItemBotanical"
+        @on-fetch="(data) => (itemBotanicalsForm = data)"
     >
         <template #form="{ data }">
             <VnRow>
diff --git a/src/pages/Item/Card/ItemDescriptor.vue b/src/pages/Item/Card/ItemDescriptor.vue
index aa9795a74..635dd17c8 100644
--- a/src/pages/Item/Card/ItemDescriptor.vue
+++ b/src/pages/Item/Card/ItemDescriptor.vue
@@ -50,7 +50,6 @@ const entityId = computed(() => {
 });
 
 const regularizeStockFormDialog = ref(null);
-const salixUrl = ref();
 const mounted = ref();
 
 const arrayDataStock = useArrayData('descriptorStock', {
@@ -58,7 +57,6 @@ const arrayDataStock = useArrayData('descriptorStock', {
 });
 
 onMounted(async () => {
-    salixUrl.value = await getUrl('getVisibleAvailable');
     await getItemConfigs();
     mounted.value = true;
 });

From 37b3affdbfc2c406b72116ac89657218e38f7adc Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Wed, 23 Oct 2024 13:02:28 +0200
Subject: [PATCH 064/150] refactor: revert catalog changes

---
 src/components/ui/VnFilterPanel.vue         |  12 +--
 src/components/ui/VnSearchbar.vue           |   2 -
 src/pages/Order/Card/OrderCatalog.vue       |   5 +-
 src/pages/Order/Card/OrderCatalogFilter.vue | 108 ++++++++------------
 4 files changed, 50 insertions(+), 77 deletions(-)

diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue
index 66e9df5f5..43d634ad9 100644
--- a/src/components/ui/VnFilterPanel.vue
+++ b/src/components/ui/VnFilterPanel.vue
@@ -185,9 +185,6 @@ async function remove(key) {
 }
 
 function formatValue(value) {
-    if (typeof value === 'object') {
-        return value;
-    }
     if (typeof value === 'boolean') return value ? t('Yes') : t('No');
     if (isNaN(value) && !isNaN(Date.parse(value))) return toDate(value);
 
@@ -196,13 +193,6 @@ function formatValue(value) {
 
 function sanitizer(params) {
     for (const [key, value] of Object.entries(params)) {
-        if (key == 'and') {
-            value.forEach((andValue) => {
-                params = { ...params, ...andValue };
-            });
-            delete params[key];
-        }
-
         if (value && typeof value === 'object') {
             const param = Object.values(value)[0];
             if (typeof param == 'string') params[key] = param.replaceAll('%', '');
@@ -221,7 +211,7 @@ function sanitizer(params) {
         icon="search"
         @click="search()"
     ></QBtn>
-    <QForm @submit="search" id="filterPanelForm" @keyup.enter.prevent="search(true)">
+    <QForm @submit="search" id="filterPanelForm" @keyup.enter="search()">
         <QList dense>
             <QItem class="q-mt-xs">
                 <QItemSection top>
diff --git a/src/components/ui/VnSearchbar.vue b/src/components/ui/VnSearchbar.vue
index 569e0ed18..dc6d4751c 100644
--- a/src/components/ui/VnSearchbar.vue
+++ b/src/components/ui/VnSearchbar.vue
@@ -9,7 +9,6 @@ import { useStateStore } from 'src/stores/useStateStore';
 const quasar = useQuasar();
 const { t } = useI18n();
 const state = useStateStore();
-const emit = defineEmits(['onSearch']);
 
 const props = defineProps({
     dataKey: {
@@ -119,7 +118,6 @@ async function search() {
         delete filter.params.search;
     }
     await arrayData.applyFilter(filter);
-    emit('onSearch', store.data);
 }
 </script>
 <template>
diff --git a/src/pages/Order/Card/OrderCatalog.vue b/src/pages/Order/Card/OrderCatalog.vue
index 110ea48af..f9bcca94c 100644
--- a/src/pages/Order/Card/OrderCatalog.vue
+++ b/src/pages/Order/Card/OrderCatalog.vue
@@ -86,8 +86,11 @@ function extractValueTags(items) {
         <div class="full-width">
             <VnPaginate
                 data-key="OrderCatalogList"
+                url="Orders/CatalogFilter"
+                :limit="50"
+                :user-params="catalogParams"
+                @on-fetch="extractTags"
                 :update-router="false"
-                @on-change="extractTags"
             >
                 <template #body="{ rows }">
                     <div class="catalog-list">
diff --git a/src/pages/Order/Card/OrderCatalogFilter.vue b/src/pages/Order/Card/OrderCatalogFilter.vue
index 0fe8c4d64..6de43e86a 100644
--- a/src/pages/Order/Card/OrderCatalogFilter.vue
+++ b/src/pages/Order/Card/OrderCatalogFilter.vue
@@ -1,13 +1,14 @@
 <script setup>
-import { computed, ref, onMounted, watch } from 'vue';
+import { computed, ref } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useRoute } from 'vue-router';
 import axios from 'axios';
 import FetchData from 'components/FetchData.vue';
 import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue';
 import VnSelect from 'components/common/VnSelect.vue';
+import VnFilterPanelChip from 'components/ui/VnFilterPanelChip.vue';
+import VnInput from 'src/components/common/VnInput.vue';
 import getParamWhere from 'src/filters/getParamWhere';
-import VnFilterPanelChip from 'src/components/ui/VnFilterPanelChip.vue';
 
 const { t } = useI18n();
 
@@ -47,19 +48,6 @@ const orderWayList = ref([
 const orderBySelected = ref('relevancy DESC, name');
 const orderWaySelected = ref('ASC');
 
-const routeQuery = JSON.parse(route?.query.params ?? '{}');
-const paramsSearch = ref({});
-const tagData = ref([]);
-
-onMounted(() => {
-    paramsSearch.value = JSON.parse(routeQuery.filter ?? '{}')?.where ?? {};
-    if (Object.keys(paramsSearch.value).length > 0) vnFilterPanelRef.value.search();
-    if (routeQuery.categoryFk && routeQuery.typeFk) {
-        selectedCategoryFk.value = routeQuery.categoryFk;
-        selectedTypeFk.value = routeQuery.typeFk;
-    }
-});
-
 const createValue = (val, done) => {
     if (val.length > 2) {
         if (!tagOptions.value.includes(val)) {
@@ -73,21 +61,19 @@ const resetCategory = () => {
     typeList.value = null;
 };
 
-const selectCategory = (category, search) => {
-    if (!paramsSearch.value?.filter) paramsSearch.value.filter = { where: {} };
-    const where = paramsSearch.value.filter.where;
-    if (where.categoryFk === category?.id) {
+const clearFilter = (key) => {
+    if (key === 'categoryFk') {
         resetCategory();
-        where.categoryFk = null;
-    } else {
-        if (where.categoryFk && where.categoryFk !== category?.id) {
-            paramsSearch.value.typeFk = null;
-            selectedTypeFk.value = null;
-            typeList.value = [];
-        }
+    }
+};
 
+const selectCategory = (params, category, search) => {
+    if (params.categoryFk === category?.id) {
+        resetCategory();
+        params.categoryFk = null;
+    } else {
         selectedCategoryFk.value = category?.id;
-        where.categoryFk = category?.id;
+        params.categoryFk = category?.id;
         loadTypes(category?.id);
     }
     search();
@@ -100,10 +86,6 @@ const loadTypes = async (categoryFk = selectedCategoryFk.value) => {
     typeList.value = data;
 };
 
-watch(selectedTypeFk, (newValue) => {
-    paramsSearch.value.typeFk = newValue;
-});
-
 const selectedCategory = computed(() =>
     (categoryList.value || []).find(
         (category) => category?.id === selectedCategoryFk.value
@@ -121,17 +103,26 @@ const selectedType = computed(() => {
     return (typeList.value || []).find((type) => type?.id === selectedTypeFk.value);
 });
 
+function exprBuilder(param, value) {
+    switch (param) {
+        case 'categoryFk':
+        case 'typeFk':
+            return { [param]: value };
+        case 'search':
+            if (/^\d+$/.test(value)) return { 'i.id': value };
+            else return { 'i.name': { like: `%${value}%` } };
+    }
+}
+
 const applyTagFilter = (params, search) => {
     if (!tagValues.value?.length) {
         params.tagGroups = null;
         search();
         return;
     }
-
-    if (!params.tagGroups || typeof params.tagGroups === 'string') {
+    if (!params.tagGroups) {
         params.tagGroups = [];
     }
-
     params.tagGroups.push(
         JSON.stringify({
             values: tagValues.value.filter((obj) => Object.keys(obj).length > 0),
@@ -142,29 +133,17 @@ const applyTagFilter = (params, search) => {
             tagFk: selectedTag?.value?.tagFk,
         })
     );
-
-    tagData.value.push(JSON.parse(params.tagGroups[params.tagGroups.length - 1]));
     search();
     selectedTag.value = null;
     tagValues.value = [{}];
 };
 
 const removeTagChip = (selection, params, search) => {
-    if (typeof params.tagGroups === 'string') {
-        try {
-            params.tagGroups = JSON.parse(params.tagGroups);
-        } catch (error) {
-            console.error('Error parsing tagGroups:', error);
-            params.tagGroups = [];
-        }
+    if (params.tagGroups) {
+        params.tagGroups = (params.tagGroups || []).filter(
+            (value) => value !== selection
+        );
     }
-    console.log('params.tagGroups: ', params.tagGroups);
-    console.log('selection: ', selection);
-
-    if (Array.isArray(params.tagGroups)) {
-        params.tagGroups = params.tagGroups.filter((value) => value !== selection);
-    }
-
     search();
 };
 
@@ -199,12 +178,12 @@ function addOrder(value, field, params) {
     <VnFilterPanel
         ref="vnFilterPanelRef"
         :data-key="props.dataKey"
-        v-model="paramsSearch"
-        :redirect="false"
-        :hidden-tags="['orderFk', 'orderBy', 'filter', 'search', 'or', 'and']"
+        :hidden-tags="['orderFk', 'orderBy']"
+        :un-removable-params="['orderFk', 'orderBy']"
+        :expr-builder="exprBuilder"
         :custom-tags="['tagGroups']"
-        :unremovable-params="['orderFk', 'orderBy']"
-        :disable-submit-event="true"
+        @remove="clearFilter"
+        :redirect="false"
     >
         <template #tags="{ tag, formatFn }">
             <strong v-if="tag.label === 'categoryFk'">
@@ -222,13 +201,17 @@ function addOrder(value, field, params) {
             <template v-for="tag in customTags" :key="tag.label">
                 <template v-if="tag.label === 'tagGroups'">
                     <VnFilterPanelChip
-                        v-for="chip in tagData"
+                        v-for="chip in tag.value"
                         :key="chip"
                         removable
                         @remove="removeTagChip(chip, params, searchFn)"
                     >
-                        <strong>{{ chip.tagSelection?.name }}: </strong>
-                        <span>{{ chip.values[0].value }}</span>
+                        <strong> {{ JSON.parse(chip).tagSelection?.name }}: </strong>
+                        <span>{{
+                            (JSON.parse(chip).values || [])
+                                .map((item) => item.value)
+                                .join(' | ')
+                        }}</span>
                     </VnFilterPanelChip>
                 </template>
             </template>
@@ -243,7 +226,7 @@ function addOrder(value, field, params) {
                     <QIcon
                         :name="category.icon"
                         class="category-icon"
-                        @click="selectCategory(category, searchFn)"
+                        @click="selectCategory(params, category, searchFn)"
                     >
                         <QTooltip>
                             {{ t(category.name) }}
@@ -337,7 +320,7 @@ function addOrder(value, field, params) {
             >
                 <FetchData
                     v-if="selectedTag"
-                    :url="`Tags/${selectedTag.id}/filterValue`"
+                    :url="`Tags/${selectedTag}/filterValue`"
                     limit="30"
                     auto-load
                     @on-fetch="(data) => (tagOptions = data)"
@@ -360,7 +343,7 @@ function addOrder(value, field, params) {
                     @update:model-value="applyTagFilter(params, searchFn)"
                 />
                 <VnSelect
-                    v-else-if="selectedTag.id === 1"
+                    v-else-if="selectedTag === 1"
                     :label="t('params.value')"
                     v-model="value.value"
                     :options="tagOptions || []"
@@ -375,7 +358,7 @@ function addOrder(value, field, params) {
                     @new-value="createValue"
                     @update:model-value="applyTagFilter(params, searchFn)"
                 />
-                <QInput
+                <VnInput
                     v-else
                     :label="t('params.value')"
                     v-model="value.value"
@@ -396,7 +379,6 @@ function addOrder(value, field, params) {
                     icon="add_circle"
                     shortcut="+"
                     flat
-                    size="md"
                     class="filter-icon"
                     @click="tagValues.push({})"
                 />

From 1c8eabe2936e8a109a3580d3b3552b51d5f1712e Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 24 Oct 2024 00:39:35 +0200
Subject: [PATCH 065/150] perf: refs #7283 #7283 declare composable inst4ead
 code duplicated

---
 src/pages/Item/Card/ItemDescriptor.vue  | 34 +++-------------------
 src/pages/Item/ItemList.vue             | 34 +++-------------------
 src/pages/Item/composables/cloneItem.js | 38 +++++++++++++++++++++++++
 3 files changed, 46 insertions(+), 60 deletions(-)
 create mode 100644 src/pages/Item/composables/cloneItem.js

diff --git a/src/pages/Item/Card/ItemDescriptor.vue b/src/pages/Item/Card/ItemDescriptor.vue
index 635dd17c8..6fbc5a4bd 100644
--- a/src/pages/Item/Card/ItemDescriptor.vue
+++ b/src/pages/Item/Card/ItemDescriptor.vue
@@ -1,20 +1,18 @@
 <script setup>
 import { computed, ref, onMounted } from 'vue';
-import { useRoute, useRouter } from 'vue-router';
+import { useRoute } from 'vue-router';
 import { useI18n } from 'vue-i18n';
-import { useQuasar } from 'quasar';
 
 import CardDescriptor from 'src/components/ui/CardDescriptor.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
 import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
-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 axios from 'axios';
-import { getUrl } from 'src/composables/getUrl';
 import { dashIfEmpty } from 'src/filters';
 import { useArrayData } from 'src/composables/useArrayData';
+import { cloneItem } from 'src/pages/Item/composables/cloneItem';
 
 const $props = defineProps({
     id: {
@@ -40,9 +38,8 @@ const $props = defineProps({
     },
 });
 
-const quasar = useQuasar();
+const { openCloneDialog } = cloneItem();
 const route = useRoute();
-const router = useRouter();
 const { t } = useI18n();
 const warehouseConfig = ref(null);
 const entityId = computed(() => {
@@ -96,29 +93,6 @@ const updateStock = async () => {
 const openRegularizeStockForm = () => {
     regularizeStockFormDialog.value.show();
 };
-
-const cloneItem = async () => {
-    try {
-        const { data } = await axios.post(`Items/${entityId.value}/clone`);
-        router.push({ name: 'ItemTags', params: { id: data.id } });
-    } catch (err) {
-        console.error('Error cloning item');
-    }
-};
-
-const openCloneDialog = async () => {
-    quasar
-        .dialog({
-            component: VnConfirm,
-            componentProps: {
-                title: t('All its properties will be copied'),
-                message: t('Do you want to clone this item?'),
-            },
-        })
-        .onOk(async () => {
-            await cloneItem();
-        });
-};
 </script>
 
 <template>
@@ -144,7 +118,7 @@ const openCloneDialog = async () => {
                     </QDialog>
                 </QItemSection>
             </QItem>
-            <QItem v-ripple clickable @click="openCloneDialog()">
+            <QItem v-ripple clickable @click="openCloneDialog(entityId)">
                 <QItemSection>
                     {{ t('globals.clone') }}
                 </QItemSection>
diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index 92c9d188b..30b86bc11 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -1,27 +1,24 @@
 <script setup>
 import { ref, computed } from 'vue';
 import { useI18n } from 'vue-i18n';
-import { useRouter, useRoute } from 'vue-router';
+import { useRoute } from 'vue-router';
 import VnImg from 'src/components/ui/VnImg.vue';
 import VnTable from 'components/VnTable/VnTable.vue';
 import { toDate } from 'src/filters';
-import axios from 'axios';
 import FetchedTags from 'src/components/ui/FetchedTags.vue';
 import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
 import { useSummaryDialog } from 'src/composables/useSummaryDialog';
 import ItemSummary from '../Item/Card/ItemSummary.vue';
 import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
 import ItemDescriptorProxy from './Card/ItemDescriptorProxy.vue';
-import VnConfirm from 'src/components/ui/VnConfirm.vue';
-import { useQuasar } from 'quasar';
-const entityId = computed(() => route.params.id);
+import { cloneItem } from 'src/pages/Item/composables/cloneItem';
 
+const entityId = computed(() => route.params.id);
+const { openCloneDialog } = cloneItem();
 const { viewSummary } = useSummaryDialog();
-const router = useRouter();
 const { t } = useI18n();
 const tableRef = ref();
 const route = useRoute();
-const quasar = useQuasar();
 
 const itemFilter = {
     include: [
@@ -297,29 +294,6 @@ const columns = computed(() => [
         ],
     },
 ]);
-
-const cloneItem = async () => {
-    try {
-        const { data } = await axios.post(`Items/${entityId.value}/clone`);
-        router.push({ name: 'ItemTags', params: { id: data.id } });
-    } catch (err) {
-        console.error('Error cloning item');
-    }
-};
-
-const openCloneDialog = async () => {
-    quasar
-        .dialog({
-            component: VnConfirm,
-            componentProps: {
-                title: t('All its properties will be copied'),
-                message: t('Do you want to clone this item?'),
-            },
-        })
-        .onOk(async () => {
-            await cloneItem();
-        });
-};
 </script>
 
 <template>
diff --git a/src/pages/Item/composables/cloneItem.js b/src/pages/Item/composables/cloneItem.js
new file mode 100644
index 000000000..f1114c779
--- /dev/null
+++ b/src/pages/Item/composables/cloneItem.js
@@ -0,0 +1,38 @@
+import axios from 'axios';
+import { computed, ref, onMounted } from 'vue';
+import { useRoute, useRouter } from 'vue-router';
+import { useI18n } from 'vue-i18n';
+import { useQuasar } from 'quasar';
+import VnConfirm from 'components/ui/VnConfirm.vue';
+
+export function cloneItem() {
+    const { t } = useI18n();
+
+    const quasar = useQuasar();
+    const route = useRoute();
+    const router = useRouter();
+    const cloneItem = async (entityId) => {
+        const { id } = entityId;
+        try {
+            const { data } = await axios.post(`Items/${id ?? entityId}/clone`);
+            router.push({ name: 'ItemTags', params: { id: data.id } });
+        } catch (err) {
+            console.error('Error cloning item');
+        }
+    };
+
+    const openCloneDialog = async (entityId) => {
+        quasar
+            .dialog({
+                component: VnConfirm,
+                componentProps: {
+                    title: t('All its properties will be copied'),
+                    message: t('Do you want to clone this item?'),
+                },
+            })
+            .onOk(async () => {
+                await cloneItem(entityId);
+            });
+    };
+    return { openCloneDialog };
+}

From 56ef811584970ae0c53f04f35604be941cd5a3ef Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Thu, 24 Oct 2024 07:47:26 +0200
Subject: [PATCH 066/150] fix: refs #7283 order translation

---
 src/pages/Item/ItemTypeList.vue |  4 +--
 src/router/modules/item.js      | 45 +++++++++++++++++----------------
 2 files changed, 25 insertions(+), 24 deletions(-)

diff --git a/src/pages/Item/ItemTypeList.vue b/src/pages/Item/ItemTypeList.vue
index 9981a0d68..d9f7bba85 100644
--- a/src/pages/Item/ItemTypeList.vue
+++ b/src/pages/Item/ItemTypeList.vue
@@ -84,11 +84,11 @@ const columns = computed(() => [
         :url="`ItemTypes`"
         :create="{
             urlCreate: 'ItemTypes',
-            title: 'Create ItemTypes',
+            title: t('Create ItemTypes'),
             onDataSaved: () => tableRef.reload(),
             formInitialData: {},
         }"
-        order="code ASC"
+        order="name ASC"
         :columns="columns"
         auto-load
         :right-search="false"
diff --git a/src/router/modules/item.js b/src/router/modules/item.js
index 48e19dd54..2838c3be7 100644
--- a/src/router/modules/item.js
+++ b/src/router/modules/item.js
@@ -48,6 +48,28 @@ export default {
                     },
                     component: () => import('src/pages/Item/ItemList.vue'),
                 },
+                {
+                    path: 'request',
+                    name: 'ItemRequest',
+                    meta: {
+                        title: 'buyRequest',
+                        icon: 'vn:buyrequest',
+                    },
+                    component: () => import('src/pages/Item/ItemRequest.vue'),
+                },
+                {
+                    path: 'waste-breakdown',
+                    name: 'WasteBreakdown',
+                    meta: {
+                        title: 'wasteBreakdown',
+                        icon: 'vn:claims',
+                    },
+                    beforeEnter: (to, from, next) => {
+                        next({ name: 'ItemList' });
+                        window.location.href =
+                            'https://grafana.verdnatura.es/d/TTNXQAxVk';
+                    },
+                },
                 {
                     path: 'fixed-price',
                     name: 'ItemFixedPrice',
@@ -65,19 +87,7 @@ export default {
                     },
                     component: () => import('src/pages/Item/ItemCreate.vue'),
                 },
-                {
-                    path: 'waste-breakdown',
-                    name: 'WasteBreakdown',
-                    meta: {
-                        title: 'wasteBreakdown',
-                        icon: 'vn:claims',
-                    },
-                    beforeEnter: (to, from, next) => {
-                        next({ name: 'ItemList' });
-                        window.location.href =
-                            'https://grafana.verdnatura.es/d/TTNXQAxVk';
-                    },
-                },
+
                 {
                     path: 'item-type-list',
                     name: 'ItemTypeList',
@@ -95,15 +105,6 @@ export default {
                     },
                     component: () => import('src/pages/Item/ItemTypeCreate.vue'),
                 },
-                {
-                    path: 'request',
-                    name: 'ItemRequest',
-                    meta: {
-                        title: 'buyRequest',
-                        icon: 'vn:buyrequest',
-                    },
-                    component: () => import('src/pages/Item/ItemRequest.vue'),
-                },
             ],
         },
         {

From 6f57d9e4906f7c74b50545a3271574b942efeac4 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Thu, 24 Oct 2024 10:21:25 +0200
Subject: [PATCH 067/150] refactor: refs #7524 use VnTable

---
 src/components/VnTable/VnTable.vue       |   6 +-
 src/pages/Ticket/TicketAdvance.vue       | 605 +++++++++--------------
 src/pages/Ticket/TicketAdvanceFilter.vue |   3 +
 3 files changed, 234 insertions(+), 380 deletions(-)

diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index 9209eaf7b..0afc5e8cf 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -447,7 +447,11 @@ function handleOnDataSaved(_) {
                     />
                 </template>
                 <template #header-cell="{ col }">
-                    <QTh v-if="col.visible ?? true">
+                    <QTh
+                        v-if="col.visible ?? true"
+                        :style="col.headerStyle"
+                        :class="col.headerClass"
+                    >
                         <div
                             class="column self-start q-ml-xs ellipsis"
                             :class="`text-${col?.align ?? 'left'}`"
diff --git a/src/pages/Ticket/TicketAdvance.vue b/src/pages/Ticket/TicketAdvance.vue
index 2cce1dba8..8b109d778 100644
--- a/src/pages/Ticket/TicketAdvance.vue
+++ b/src/pages/Ticket/TicketAdvance.vue
@@ -1,24 +1,20 @@
 <script setup>
-import { onMounted, ref, computed, reactive } from 'vue';
+import { ref, computed, reactive, watch, h } from 'vue';
 import { useI18n } from 'vue-i18n';
-
 import FetchData from 'components/FetchData.vue';
-import VnInput from 'src/components/common/VnInput.vue';
-import VnSelect from 'src/components/common/VnSelect.vue';
 import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
 import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
 import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
 import VnProgress from 'src/components/common/VnProgressModal.vue';
 import RightMenu from 'src/components/common/RightMenu.vue';
 import TicketAdvanceFilter from './TicketAdvanceFilter.vue';
-
 import { dashIfEmpty, toCurrency } from 'src/filters';
 import { useVnConfirm } from 'composables/useVnConfirm';
-import { useArrayData } from 'composables/useArrayData';
 import useNotify from 'src/composables/useNotify.js';
 import { useState } from 'src/composables/useState';
 import { toDateFormat } from 'src/filters/date.js';
 import axios from 'axios';
+import VnTable from 'src/components/VnTable/VnTable.vue';
 
 const state = useState();
 const { t } = useI18n();
@@ -29,109 +25,58 @@ const user = state.getUser();
 const itemPackingTypesOptions = ref([]);
 const zonesOptions = ref([]);
 const selectedTickets = ref([]);
-
-const exprBuilder = (param, value) => {
-    switch (param) {
-        case 'id':
-        case 'futureId':
-        case 'liters':
-        case 'futureLiters':
-        case 'lines':
-        case 'futureLines':
-        case 'totalWithVat':
-        case 'futureTotalWithVat':
-        case 'futureZone':
-        case 'notMovableLines':
-        case 'futureZoneFk':
-            return { [param]: value };
-        case 'iptColFilter':
-            return { ipt: { like: `%${value}%` } };
-        case 'futureIptColFilter':
-            return { futureIpt: { like: `%${value}%` } };
-    }
-};
-
-const userParams = reactive({});
-
-const arrayData = useArrayData('AdvanceTickets', {
-    url: 'Tickets/getTicketsAdvance',
-    userParams: userParams,
-    exprBuilder: exprBuilder,
-    limit: 0,
+const vnTableRef = ref({});
+const originElRef = ref(null);
+const destinationElRef = ref(null);
+let today = Date.vnNew().toISOString();
+const tomorrow = new Date(today);
+tomorrow.setDate(tomorrow.getDate() + 1);
+const userParams = reactive({
+    dateFuture: tomorrow,
+    dateToAdvance: today,
+    warehouseFk: user.value.warehouseFk,
+    ipt: 'H',
+    futureIpt: 'H',
+    isFullMovable: true,
 });
-const { store } = arrayData;
-const tickets = computed(() =>
-    (store.data || []).map((ticket, index) => ({ ...ticket, index: index }))
-);
-
-const applyColumnFilter = async (col) => {
-    try {
-        const paramKey = col.columnFilter?.filterParamKey || col.field;
-        userParams[paramKey] = col.columnFilter.filterValue;
-        await arrayData.addFilter({ params: userParams });
-    } catch (err) {
-        console.error('Error applying column filter', err);
-    }
-};
-
-const getInputEvents = (col) => {
-    return col.columnFilter.type === 'select'
-        ? { 'update:modelValue': () => applyColumnFilter(col) }
-        : {
-              'keyup.enter': () => applyColumnFilter(col),
-          };
-};
 
 const ticketColumns = computed(() => [
     {
         label: '',
         name: 'icons',
-        align: 'left',
-        columnFilter: null,
+        hidden: true,
+        headerClass: 'horizontal-separator',
     },
     {
-        label: t('advanceTickets.ticketId'),
-        name: 'ticketId',
         align: 'center',
-        sortable: true,
-        columnFilter: {
-            component: VnInput,
-            type: 'text',
-            filterValue: null,
-            filterParamKey: 'id',
-            event: getInputEvents,
-            attrs: {
-                dense: true,
-            },
-        },
+        label: t('advanceTickets.ticketId'),
+        name: 'id',
+        headerClass: 'horizontal-separator',
     },
     {
+        align: 'left',
         label: t('advanceTickets.ipt'),
         name: 'ipt',
-        field: 'ipt',
-        align: 'left',
-        sortable: true,
         columnFilter: {
-            component: VnSelect,
-            filterParamKey: 'iptColFilter',
-            type: 'select',
-            filterValue: null,
-            event: getInputEvents,
+            component: 'select',
             attrs: {
-                options: itemPackingTypesOptions.value,
-                'option-value': 'code',
-                'option-label': 'description',
-                dense: true,
+                url: 'itemPackingTypes',
+                fields: ['code', 'description'],
+                where: { isActive: true },
+                optionValue: 'code',
+                optionLabel: 'description',
+                inWhere: false,
             },
         },
-        format: (val) => dashIfEmpty(val),
+        format: (row, dashIfEmpty) => dashIfEmpty(row.ipt),
+        headerClass: 'horizontal-separator',
     },
     {
+        align: 'left',
         label: t('advanceTickets.state'),
         name: 'state',
-        align: 'left',
-        sortable: true,
-        columnFilter: null,
+        headerClass: 'horizontal-separator',
+        hidden: true,
     },
     {
         label: t('advanceTickets.preparation'),
@@ -139,171 +84,105 @@ const ticketColumns = computed(() => [
         field: 'preparation',
         align: 'left',
         sortable: true,
-        columnFilter: null,
+        headerClass: 'horizontal-separator',
+        columnFilter: false,
     },
     {
-        label: t('advanceTickets.liters'),
-        name: 'liters',
-        field: 'liters',
         align: 'left',
-        sortable: true,
-        columnFilter: {
-            component: VnInput,
-            type: 'text',
-            filterValue: null,
-            event: getInputEvents,
-            attrs: {
-                dense: true,
-            },
-        },
+        label: t('advanceTickets.liters'),
+        headerClass: 'horizontal-separator',
+        name: 'liters',
     },
     {
+        align: 'left',
         label: t('advanceTickets.lines'),
         name: 'lines',
-        field: 'lines',
-        align: 'left',
-        sortable: true,
-        columnFilter: {
-            component: VnInput,
-            type: 'text',
-            filterValue: null,
-            event: getInputEvents,
-            attrs: {
-                dense: true,
-            },
-        },
-        format: (val) => dashIfEmpty(val),
+        headerClass: 'horizontal-separator',
+        format: (row, dashIfEmpty) => dashIfEmpty(row.lines),
     },
     {
+        align: 'left',
         label: t('advanceTickets.import'),
-        field: 'import',
-        name: 'import',
-        align: 'left',
-        sortable: true,
+        name: 'totalWithVat',
+        hidden: true,
+        headerClass: 'horizontal-separator',
+        format: (row) => toCurrency(row.totalWithVat),
     },
     {
+        align: 'left',
         label: t('advanceTickets.futureId'),
         name: 'futureId',
-        align: 'left',
-        sortable: true,
-        columnFilter: {
-            component: VnInput,
-            type: 'text',
-            filterValue: null,
-            filterParamKey: 'futureId',
-            event: getInputEvents,
-            attrs: {
-                dense: true,
-            },
-        },
+        headerClass: 'vertical-separator horizontal-separator',
+        columnClass: 'vertical-separator',
     },
     {
+        align: 'left',
         label: t('advanceTickets.futureIpt'),
         name: 'futureIpt',
-        field: 'futureIpt',
-        align: 'left',
-        sortable: true,
         columnFilter: {
-            component: VnSelect,
-            filterParamKey: 'futureIptColFilter',
-            type: 'select',
-            filterValue: null,
-            event: getInputEvents,
+            component: 'select',
             attrs: {
-                options: itemPackingTypesOptions.value,
-                'option-value': 'code',
-                'option-label': 'description',
-                dense: true,
+                url: 'itemPackingTypes',
+                fields: ['code', 'description'],
+                where: { isActive: true },
+                optionValue: 'code',
+                optionLabel: 'description',
             },
         },
-        format: (val) => dashIfEmpty(val),
+        headerClass: 'horizontal-separator',
+        format: (row, dashIfEmpty) => dashIfEmpty(row.futureIpt),
     },
     {
+        align: 'left',
         label: t('advanceTickets.futureState'),
         name: 'futureState',
-        align: 'left',
-        sortable: true,
-        columnFilter: null,
-        format: (val) => dashIfEmpty(val),
+        headerClass: 'horizontal-separator',
+        hidden: true,
     },
     {
+        align: 'left',
         label: t('advanceTickets.futureLiters'),
+        headerClass: 'horizontal-separator',
         name: 'futureLiters',
-        field: 'futureLiters',
-        align: 'left',
-        sortable: true,
-        columnFilter: {
-            component: VnInput,
-            type: 'text',
-            filterValue: null,
-            event: getInputEvents,
-            attrs: {
-                dense: true,
-            },
-        },
-        format: (val) => dashIfEmpty(val),
     },
     {
+        align: 'left',
         label: t('advanceTickets.futureZone'),
-        name: 'futureZoneName',
-        field: 'futureZoneName',
-        align: 'left',
-        sortable: true,
+        name: 'futureZoneFk',
+        columnClass: 'expand',
         columnFilter: {
-            component: VnSelect,
-            type: 'select',
-            filterValue: null,
-            filterParamKey: 'futureZoneFk',
-            event: getInputEvents,
+            component: 'select',
+            inWhere: true,
             attrs: {
-                options: zonesOptions.value,
-                'option-value': 'id',
-                'option-label': 'name',
-                dense: true,
+                url: 'Zones',
+                fields: ['id', 'name'],
             },
         },
-        format: (val) => dashIfEmpty(val),
+        columnField: {
+            component: null,
+        },
+        headerClass: 'horizontal-separator',
+        format: (row, dashIfEmpty) => dashIfEmpty(row.futureZoneName),
     },
     {
+        align: 'left',
         label: t('advanceTickets.notMovableLines'),
+        headerClass: 'horizontal-separator',
         name: 'notMovableLines',
-        field: 'notMovableLines',
-        align: 'left',
-        sortable: true,
-        columnFilter: {
-            component: VnInput,
-            type: 'text',
-            filterValue: null,
-            event: getInputEvents,
-            attrs: {
-                dense: true,
-            },
-        },
-        format: (val) => dashIfEmpty(val),
     },
     {
+        align: 'left',
         label: t('advanceTickets.futureLines'),
+        headerClass: 'horizontal-separator',
         name: 'futureLines',
-        field: 'futureLines',
-        align: 'left',
-        sortable: true,
-        columnFilter: {
-            component: VnInput,
-            type: 'text',
-            filterValue: null,
-            event: getInputEvents,
-            attrs: {
-                dense: true,
-            },
-        },
-        format: (val) => dashIfEmpty(val),
     },
     {
-        label: t('advanceTickets.futureImport'),
-        name: 'futureImport',
         align: 'left',
-        sortable: true,
-        columnFilter: null,
+        label: t('advanceTickets.futureImport'),
+        name: 'futureTotalWithVat',
+        hidden: true,
+        headerClass: 'horizontal-separator',
+        format: (row) => toCurrency(row.futureTotalWithVat),
     },
 ]);
 
@@ -329,7 +208,7 @@ const requestComponentUpdate = async (ticket, isWithoutNegatives) => {
     const query = `tickets/${ticket.futureId}/componentUpdate`;
     if (!ticket.landed) {
         const newLanded = await getLanded({
-            shipped: userParams.dateToAdvance,
+            shipped: vnTableRef.value.params.dateToAdvance,
             addressFk: ticket.futureAddressFk,
             agencyModeFk: ticket.agencyModeFk ?? ticket.futureAgencyModeFk,
             warehouseFk: ticket.futureWarehouseFk,
@@ -352,7 +231,7 @@ const requestComponentUpdate = async (ticket, isWithoutNegatives) => {
         zoneFk: ticket.zoneFk ?? ticket.futureZoneFk,
         warehouseFk: ticket.futureWarehouseFk,
         companyFk: ticket.futureCompanyFk,
-        shipped: userParams.dateToAdvance,
+        shipped: vnTableRef.value.params.dateToAdvance,
         landed: ticket.landed,
         isDeleted: false,
         isWithoutNegatives,
@@ -387,7 +266,7 @@ const moveTicketsAdvance = async () => {
 
         const params = { tickets: ticketsToMove };
         await axios.post('Tickets/merge', params);
-        arrayData.fetch({ append: false });
+        vnTableRef.value.reload();
         selectedTickets.value = [];
         if (ticketsToMove.length)
             notify(t('advanceTickets.moveTicketSuccess'), 'positive');
@@ -437,7 +316,7 @@ const splitTickets = async () => {
     } catch (error) {
         console.error('Error splitting tickets', error);
     } finally {
-        arrayData.fetch({ append: false });
+        vnTableRef.value.reload();
     }
 };
 
@@ -455,21 +334,52 @@ const handleCloseProgressDialog = () => {
 
 const handleCancelProgress = () => (cancelProgress.value = true);
 
-onMounted(async () => {
-    let today = Date.vnNew().toISOString();
-    const tomorrow = new Date(today);
-    tomorrow.setDate(tomorrow.getDate() + 1);
-    userParams.dateFuture = tomorrow;
-    userParams.dateToAdvance = today;
-    userParams.warehouseFk = user.value.warehouseFk;
-    userParams.ipt = 'H';
-    userParams.futureIpt = 'H';
-    userParams.isFullMovable = true;
-    const filter = { limit: 0 };
-    await arrayData.addFilter({ filter, userParams });
-});
-</script>
+watch(
+    () => vnTableRef.value.tableRef?.$el,
+    ($el) => {
+        if (!$el) return;
+        const head = $el.querySelector('thead');
+        const firstRow = $el.querySelector('thead > tr');
 
+        const newRow = document.createElement('tr');
+        destinationElRef.value = document.createElement('th');
+        originElRef.value = document.createElement('th');
+
+        newRow.classList.add('bg-header');
+        destinationElRef.value.classList.add('text-uppercase', 'color-vn-label');
+        originElRef.value.classList.add('text-uppercase', 'color-vn-label');
+
+        destinationElRef.value.setAttribute('colspan', '7');
+        originElRef.value.setAttribute('colspan', '9');
+
+        destinationElRef.value.textContent = `${t(
+            'advanceTickets.destination'
+        )} ${toDateFormat(vnTableRef.value.params.dateToAdvance)}`;
+        originElRef.value.textContent = `${t('advanceTickets.origin')} ${toDateFormat(
+            vnTableRef.value.params.dateFuture
+        )}`;
+
+        newRow.append(destinationElRef.value, originElRef.value);
+        head.insertBefore(newRow, firstRow);
+    },
+    { once: true, inmmediate: true }
+);
+
+watch(
+    () => vnTableRef.value.params,
+    () => {
+        if (originElRef.value && destinationElRef.value) {
+            destinationElRef.value.textContent = `${t(
+                'advanceTickets.destination'
+            )} ${toDateFormat(vnTableRef.value.params.dateToAdvance)}`;
+            originElRef.value.textContent = `${t('advanceTickets.origin')} ${toDateFormat(
+                vnTableRef.value.params.dateFuture
+            )}`;
+        }
+    },
+    { deep: true }
+);
+</script>
 <template>
     <FetchData
         url="itemPackingTypes"
@@ -490,8 +400,9 @@ onMounted(async () => {
         auto-load
         @on-fetch="(data) => (zonesOptions = data)"
     />
+    <!-- Fix searchbar #8154 -->
     <VnSearchbar
-        data-key="WeeklyTickets"
+        data-key="advanceTickets"
         :label="t('weeklyTickets.search')"
         :info="t('weeklyTickets.searchInfo')"
     />
@@ -538,173 +449,109 @@ onMounted(async () => {
     </VnSubToolbar>
     <RightMenu>
         <template #right-panel>
-            <TicketAdvanceFilter data-key="AdvanceTickets" />
+            <TicketAdvanceFilter data-key="advanceTickets" />
         </template>
     </RightMenu>
     <QPage class="column items-center q-pa-md">
-        <QTable
-            :rows="tickets"
+        <VnTable
+            data-key="advanceTickets"
+            ref="vnTableRef"
+            url="Tickets/getTicketsAdvance"
+            search-url="advanceTickets"
+            :user-params="userParams"
+            :limit="0"
             :columns="ticketColumns"
-            row-key="index"
-            selection="multiple"
+            :table="{
+                'row-key': '$index',
+                selection: 'multiple',
+            }"
             v-model:selected="selectedTickets"
             :pagination="{ rowsPerPage: 0 }"
             :no-data-label="t('globals.noResults')"
-            style="max-width: 99%"
+            :right-search="false"
+            auto-load
+            :disable-option="{ card: true }"
         >
-            <template #header="props">
-                <QTr :props="props">
-                    <QTh
-                        class="horizontal-separator text-uppercase color-vn-label"
-                        colspan="7"
-                        translate
-                    >
-                        {{ t('advanceTickets.destination') }}
-                        {{ toDateFormat(userParams.dateToAdvance) }}
-                    </QTh>
-                    <QTh
-                        class="horizontal-separator text-uppercase color-vn-label"
-                        colspan="9"
-                        translate
-                    >
-                        {{ t('advanceTickets.origin') }}
-                        {{ toDateFormat(userParams.dateFuture) }}
-                    </QTh>
-                </QTr>
-                <QTr>
-                    <QTh>
-                        <QCheckbox v-model="props.selected" />
-                    </QTh>
-                    <QTh
-                        v-for="(col, index) in ticketColumns"
-                        :key="index"
-                        :class="{ 'vertical-separator': col.name === 'futureId' }"
-                    >
-                        {{ col.label }}
-                    </QTh>
-                </QTr>
+            <template #column-icons="{ row }">
+                <QIcon
+                    v-if="row.futureAgency !== row.agency && row.agency"
+                    color="primary"
+                    name="vn:agency-term"
+                    size="xs"
+                >
+                    <QTooltip class="column">
+                        <span>
+                            {{
+                                t('advanceTickets.originAgency', {
+                                    agency: row.futureAgency,
+                                })
+                            }}
+                        </span>
+                        <span>
+                            {{
+                                t('advanceTickets.destinationAgency', {
+                                    agency: row.agency,
+                                })
+                            }}
+                        </span>
+                    </QTooltip>
+                </QIcon>
             </template>
-            <template #top-row="{ cols }">
-                <QTr>
-                    <QTd />
-                    <QTd
-                        v-for="(col, index) in cols"
-                        :key="index"
-                        style="max-width: 100px"
-                    >
-                        <component
-                            :is="col.columnFilter.component"
-                            v-if="col.columnFilter"
-                            v-model="col.columnFilter.filterValue"
-                            v-bind="col.columnFilter.attrs"
-                            v-on="col.columnFilter.event(col)"
-                            dense
-                        />
-                    </QTd>
-                </QTr>
+            <template #column-id="{ row }">
+                <QBtn flat class="link">
+                    {{ row.id }}
+                    <TicketDescriptorProxy :id="row.id" />
+                </QBtn>
             </template>
-            <template #header-cell-availableLines="{ col }">
-                <QTh class="vertical-separator">
-                    {{ col.label }}
-                </QTh>
+            <template #column-state="{ row }">
+                <QBadge
+                    v-if="row.state"
+                    text-color="black"
+                    :color="row.classColor"
+                    class="q-ma-none"
+                    dense
+                >
+                    {{ row.state }}
+                </QBadge>
+                <span v-else> {{ dashIfEmpty(row.state) }}</span>
             </template>
-            <template #body-cell-icons="{ row }">
-                <QTd class="q-gutter-x-xs">
-                    <QIcon
-                        v-if="row.futureAgency !== row.agency && row.agency"
-                        color="primary"
-                        name="vn:agency-term"
-                        size="xs"
-                    >
-                        <QTooltip class="column">
-                            <span>
-                                {{
-                                    t('advanceTickets.originAgency', {
-                                        agency: row.futureAgency,
-                                    })
-                                }}
-                            </span>
-                            <span>
-                                {{
-                                    t('advanceTickets.destinationAgency', {
-                                        agency: row.agency,
-                                    })
-                                }}
-                            </span>
-                        </QTooltip>
-                    </QIcon>
-                </QTd>
+            <template #column-import="{ row }">
+                <QBadge
+                    :text-color="isLessThan50(row.totalWithVat) ? 'black' : 'white'"
+                    :color="totalPriceColor(row.totalWithVat)"
+                    class="q-ma-none"
+                    dense
+                >
+                    {{ toCurrency(row.totalWithVat || 0) }}
+                </QBadge>
             </template>
-
-            <template #body-cell-ticketId="{ row }">
-                <QTd>
-                    <QBtn flat class="link">
-                        {{ row.id }}
-                        <TicketDescriptorProxy :id="row.id" />
-                    </QBtn>
-                </QTd>
+            <template #column-futureId="{ row }">
+                <QBtn flat class="link" dense>
+                    {{ row.futureId }}
+                    <TicketDescriptorProxy :id="row.futureId" />
+                </QBtn>
             </template>
-            <template #body-cell-state="{ row }">
-                <QTd>
-                    <QBadge
-                        v-if="row.state"
-                        text-color="black"
-                        :color="row.classColor"
-                        class="q-ma-none"
-                        dense
-                    >
-                        {{ row.state }}
-                    </QBadge>
-                    <span v-else> {{ dashIfEmpty(row.state) }}</span>
-                </QTd>
+            <template #column-futureState="{ row }">
+                <QBadge
+                    text-color="black"
+                    :color="row.futureClassColor"
+                    class="q-ma-none"
+                    dense
+                >
+                    {{ row.futureState }}
+                </QBadge>
             </template>
-            <template #body-cell-import="{ row }">
-                <QTd>
-                    <QBadge
-                        :text-color="isLessThan50(row.totalWithVat) ? 'black' : 'white'"
-                        :color="totalPriceColor(row.totalWithVat)"
-                        class="q-ma-none"
-                        dense
-                    >
-                        {{ toCurrency(row.totalWithVat || 0) }}
-                    </QBadge>
-                </QTd>
+            <template #column-futureImport="{ row }">
+                <QBadge
+                    :text-color="isLessThan50(row.futureTotalWithVat) ? 'black' : 'white'"
+                    :color="totalPriceColor(row.futureTotalWithVat)"
+                    class="q-ma-none"
+                    dense
+                >
+                    {{ toCurrency(row.futureTotalWithVat || 0) }}
+                </QBadge>
             </template>
-            <template #body-cell-futureId="{ row }">
-                <QTd class="vertical-separator">
-                    <QBtn flat class="link" dense>
-                        {{ row.futureId }}
-                        <TicketDescriptorProxy :id="row.futureId" />
-                    </QBtn>
-                </QTd>
-            </template>
-            <template #body-cell-futureState="{ row }">
-                <QTd>
-                    <QBadge
-                        text-color="black"
-                        :color="row.futureClassColor"
-                        class="q-ma-none"
-                        dense
-                    >
-                        {{ row.futureState }}
-                    </QBadge>
-                </QTd>
-            </template>
-            <template #body-cell-futureImport="{ row }">
-                <QTd>
-                    <QBadge
-                        :text-color="
-                            isLessThan50(row.futureTotalWithVat) ? 'black' : 'white'
-                        "
-                        :color="totalPriceColor(row.futureTotalWithVat)"
-                        class="q-ma-none"
-                        dense
-                    >
-                        {{ toCurrency(row.futureTotalWithVat || 0) }}
-                    </QBadge>
-                </QTd>
-            </template>
-        </QTable>
+        </VnTable>
         <VnProgress
             :progress="progressPercentage"
             :cancelled="cancelProgress"
@@ -723,11 +570,11 @@ onMounted(async () => {
 </template>
 
 <style scoped lang="scss">
-.vertical-separator {
+:deep(.vertical-separator) {
     border-left: 4px solid white !important;
 }
 
-.horizontal-separator {
-    border-bottom: 4px solid white !important;
+:deep(.horizontal-separator) {
+    border-top: 4px solid white !important;
 }
 </style>
diff --git a/src/pages/Ticket/TicketAdvanceFilter.vue b/src/pages/Ticket/TicketAdvanceFilter.vue
index 182f715a3..1d08d1ebf 100644
--- a/src/pages/Ticket/TicketAdvanceFilter.vue
+++ b/src/pages/Ticket/TicketAdvanceFilter.vue
@@ -57,6 +57,7 @@ onMounted(async () => await getItemPackingTypes());
         auto-load
     />
     <VnFilterPanel
+        search-url="advanceTickets"
         :data-key="props.dataKey"
         :search-button="true"
         :hidden-tags="['search']"
@@ -100,6 +101,7 @@ onMounted(async () => await getItemPackingTypes());
                         dense
                         outlined
                         rounded
+                        :use-like="false"
                     >
                     </VnSelect>
                 </QItemSection>
@@ -117,6 +119,7 @@ onMounted(async () => await getItemPackingTypes());
                         dense
                         outlined
                         rounded
+                        :use-like="false"
                     >
                     </VnSelect>
                 </QItemSection>

From d918b76010c5ec8efa76cfec1298ba4c5cf0749b Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Thu, 24 Oct 2024 10:24:28 +0200
Subject: [PATCH 068/150] chore: refs #7524 drop useless code

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

diff --git a/src/pages/Ticket/TicketAdvance.vue b/src/pages/Ticket/TicketAdvance.vue
index 8b109d778..f6fb37e21 100644
--- a/src/pages/Ticket/TicketAdvance.vue
+++ b/src/pages/Ticket/TicketAdvance.vue
@@ -1,5 +1,5 @@
 <script setup>
-import { ref, computed, reactive, watch, h } from 'vue';
+import { ref, computed, reactive, watch } from 'vue';
 import { useI18n } from 'vue-i18n';
 import FetchData from 'components/FetchData.vue';
 import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';

From 87d2e0a39b1ee2d12e8468a0ca802168d32273f8 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 24 Oct 2024 10:36:48 +0200
Subject: [PATCH 069/150] perf: refs #7283 #7283 handle composable i18n

---
 src/i18n/locale/es.yml                  | 108 -----------------------
 src/pages/Item/Card/ItemDescriptor.vue  |   2 -
 src/pages/Item/ItemList.vue             |   2 -
 src/pages/Item/composables/cloneItem.js |   8 +-
 src/pages/Item/locale/en.yml            | 112 ++++++++++++++++++++++++
 src/pages/Item/locale/es.yml            | 112 ++++++++++++++++++++++++
 6 files changed, 227 insertions(+), 117 deletions(-)

diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml
index 7c368b7b7..bd414a793 100644
--- a/src/i18n/locale/es.yml
+++ b/src/i18n/locale/es.yml
@@ -1053,114 +1053,6 @@ travel:
         warehouse: Almacén
         travelFileDescription: 'Id envío { travelId }'
         file: Fichero
-item:
-    searchbar:
-        label: Buscar artículo
-    descriptor:
-        item: Artículo
-        buyer: Comprador
-        color: Color
-        category: Categoría
-        stems: Tallos
-        visible: Visible
-        available: Disponible
-        warehouseText: 'Calculado sobre el almacén de { warehouseName }'
-        itemDiary: Registro de compra-venta
-        producer: Productor
-    list:
-        id: Identificador
-        grouping: Grouping
-        packing: Packing
-        description: Descripción
-        stems: Tallos
-        category: Reino
-        typeName: Tipo
-        intrastat: Intrastat
-        isActive: Activo
-        size: Medida
-        origin: Origen
-        weightByPiece: Peso (gramos)/tallo
-        userName: Comprador
-        stemMultiplier: Multiplicador
-        producer: Productor
-        landed: F. entrega
-    basicData:
-        type: Tipo
-        reference: Referencia
-        relevancy: Relevancia
-        stems: Tallos
-        multiplier: Multiplicador
-        generic: Genérico
-        intrastat: Intrastat
-        expense: Gasto
-        weightByPiece: Peso (gramos)/tallo
-        boxUnits: Unidades/caja
-        recycledPlastic: Plastico reciclado
-        nonRecycledPlastic: Plático no reciclado
-        isActive: Activo
-        hasKgPrice: Precio en kg
-        isFragile: Frágil
-        isFragileTooltip: Se muestra en la web, app que este artículo no puede viajar (coronas, palmas, ...)
-        isPhotoRequested: Hacer foto
-        isPhotoRequestedTooltip: Este artículo necesita una foto
-        description: Descripción
-    fixedPrice:
-        itemFk: ID Artículo
-        groupingPrice: Precio grouping
-        packingPrice: Precio packing
-        hasMinPrice: Tiene precio mínimo
-        minPrice: Precio min
-        started: Inicio
-        ended: Fin
-        warehouse: Almacén
-    create:
-        name: Nombre
-        tag: Etiqueta
-        priority: Prioridad
-        type: Tipo
-        intrastat: Intrastat
-        origin: Origen
-    summary:
-        basicData: 'Datos básicos'
-        otherData: 'Otros datos'
-        description: 'Descripción'
-        tax: 'IVA'
-        tags: 'Etiquetas'
-        botanical: 'Botánico'
-        barcode: 'Código de barras'
-        name: 'Nombre'
-        completeName: 'Nombre completo'
-        family: 'Familia'
-        size: 'Medida'
-        origin: 'Origen'
-        stems: 'Tallos'
-        multiplier: 'Multiplicador'
-        buyer: 'Comprador'
-        doPhoto: 'Hacer foto'
-        intrastatCode: 'Código intrastat'
-        intrastat: 'Intrastat'
-        ref: 'Referencia'
-        relevance: 'Relevancia'
-        weight: 'Peso (gramos)/tallo'
-        units: 'Unidades/caja'
-        expense: 'Gasto'
-        generic: 'Genérico'
-        recycledPlastic: 'Plástico reciclado'
-        nonRecycledPlastic: 'Plástico no reciclado'
-        minSalesQuantity: 'Cantidad mínima de venta'
-        genus: 'Genus'
-        specie: 'Specie'
-    buyRequest:
-        ticketId: 'ID Ticket'
-        shipped: 'F. envío'
-        requester: 'Solicitante'
-        requested: 'Solicitado'
-        price: 'Precio'
-        attender: 'Comprador'
-        item: 'Artículo'
-        achieved: 'Conseguido'
-        concept: 'Concepto'
-        state: 'Estado'
 components:
     topbar: {}
     itemsFilterPanel:
diff --git a/src/pages/Item/Card/ItemDescriptor.vue b/src/pages/Item/Card/ItemDescriptor.vue
index 6fbc5a4bd..243d4c7cb 100644
--- a/src/pages/Item/Card/ItemDescriptor.vue
+++ b/src/pages/Item/Card/ItemDescriptor.vue
@@ -195,8 +195,6 @@ const openRegularizeStockForm = () => {
 <i18n>
 es:
     Regularize stock: Regularizar stock
-    All its properties will be copied: Todas sus propiedades serán copiadas
-    Do you want to clone this item?: ¿Desea clonar este artículo?
     Inactive article: Artículo inactivo
 </i18n>
 
diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index 30b86bc11..cca5560fe 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -355,8 +355,6 @@ const columns = computed(() => [
 <i18n>
 es:
     New item: Nuevo artículo
-    All its properties will be copied: Todas sus propiedades serán copiadas
-    Do you want to clone this item?: ¿Desea clonar este artículo?
     Preview: Vista previa
     Regularize stock: Regularizar stock
 </i18n>
diff --git a/src/pages/Item/composables/cloneItem.js b/src/pages/Item/composables/cloneItem.js
index f1114c779..2421c0808 100644
--- a/src/pages/Item/composables/cloneItem.js
+++ b/src/pages/Item/composables/cloneItem.js
@@ -1,6 +1,5 @@
 import axios from 'axios';
-import { computed, ref, onMounted } from 'vue';
-import { useRoute, useRouter } from 'vue-router';
+import { useRouter } from 'vue-router';
 import { useI18n } from 'vue-i18n';
 import { useQuasar } from 'quasar';
 import VnConfirm from 'components/ui/VnConfirm.vue';
@@ -9,7 +8,6 @@ export function cloneItem() {
     const { t } = useI18n();
 
     const quasar = useQuasar();
-    const route = useRoute();
     const router = useRouter();
     const cloneItem = async (entityId) => {
         const { id } = entityId;
@@ -26,8 +24,8 @@ export function cloneItem() {
             .dialog({
                 component: VnConfirm,
                 componentProps: {
-                    title: t('All its properties will be copied'),
-                    message: t('Do you want to clone this item?'),
+                    title: t('item.descriptor.clone.title'),
+                    message: t('item.descriptor.clone.subTitle'),
                 },
             })
             .onOk(async () => {
diff --git a/src/pages/Item/locale/en.yml b/src/pages/Item/locale/en.yml
index c32ee493c..034e39a17 100644
--- a/src/pages/Item/locale/en.yml
+++ b/src/pages/Item/locale/en.yml
@@ -88,3 +88,115 @@ itemType:
         worker: Worker
         category: Category
         temperature: Temperature
+item:
+    searchbar:
+        label: Search item
+    descriptor:
+        item: Item
+        buyer: Buyer
+        color: Color
+        category: Category
+        stems: Stems
+        visible: Visible
+        available: Available
+        warehouseText: 'Calculated on the warehouse of { warehouseName }'
+        itemDiary: Item diary
+        producer: Producer
+        clone:
+            title: All its properties will be copied
+            subTitle: Do you want to clone this item?
+
+    list:
+        id: Identifier
+        grouping: Grouping
+        packing: Packing
+        description: Description
+        stems: Stems
+        category: Category
+        typeName: Type
+        intrastat: Intrastat
+        isActive: Active
+        size: Size
+        origin: Origin
+        userName: Buyer
+        weightByPiece: Weight/Piece
+        stemMultiplier: Multiplier
+        producer: Producer
+        landed: Landed
+    basicData:
+        type: Type
+        reference: Reference
+        relevancy: Relevancy
+        stems: Stems
+        multiplier: Multiplier
+        generic: Generic
+        intrastat: Intrastat
+        expense: Expense
+        weightByPiece: Weight/Piece
+        boxUnits: Units/Box
+        recycledPlastic: Recycled Plastic
+        nonRecycledPlastic: Non recycled plastic
+        isActive: Active
+        hasKgPrice: Price in kg
+        isFragile: Fragile
+        isFragileTooltip: Is shown at website, app that this item cannot travel (wreath, palms, ...)
+        isPhotoRequested: Do photo
+        isPhotoRequestedTooltip: This item does need a photo
+        description: Description
+    fixedPrice:
+        itemFk: Item ID
+        groupingPrice: Grouping price
+        packingPrice: Packing price
+        hasMinPrice: Has min price
+        minPrice: Min price
+        started: Started
+        ended: Ended
+        warehouse: Warehouse
+    create:
+        name: Name
+        tag: Tag
+        priority: Priority
+        type: Type
+        intrastat: Intrastat
+        origin: Origin
+    buyRequest:
+        ticketId: 'Ticket ID'
+        shipped: 'Shipped'
+        requester: 'Requester'
+        requested: 'Requested'
+        price: 'Price'
+        attender: 'Attender'
+        item: 'Item'
+        achieved: 'Achieved'
+        concept: 'Concept'
+        state: 'State'
+    summary:
+        basicData: 'Basic data'
+        otherData: 'Other data'
+        description: 'Description'
+        tax: 'Tax'
+        tags: 'Tags'
+        botanical: 'Botanical'
+        barcode: 'Barcode'
+        name: 'Nombre'
+        completeName: 'Nombre completo'
+        family: 'Familia'
+        size: 'Medida'
+        origin: 'Origen'
+        stems: 'Tallos'
+        multiplier: 'Multiplicador'
+        buyer: 'Comprador'
+        doPhoto: 'Do photo'
+        intrastatCode: 'Código intrastat'
+        intrastat: 'Intrastat'
+        ref: 'Referencia'
+        relevance: 'Relevancia'
+        weight: 'Peso (gramos)/tallo'
+        units: 'Unidades/caja'
+        expense: 'Gasto'
+        generic: 'Genérico'
+        recycledPlastic: 'Plástico reciclado'
+        nonRecycledPlastic: 'Plástico no reciclado'
+        minSalesQuantity: 'Cantidad mínima de venta'
+        genus: 'Genus'
+        specie: 'Specie'
diff --git a/src/pages/Item/locale/es.yml b/src/pages/Item/locale/es.yml
index d32cb7885..917bd7e5f 100644
--- a/src/pages/Item/locale/es.yml
+++ b/src/pages/Item/locale/es.yml
@@ -88,3 +88,115 @@ itemType:
         worker: Trabajador
         category: Reino
         temperature: Temperatura
+item:
+    searchbar:
+        label: Buscar artículo
+    descriptor:
+        item: Artículo
+        buyer: Comprador
+        color: Color
+        category: Categoría
+        stems: Tallos
+        visible: Visible
+        available: Disponible
+        warehouseText: 'Calculado sobre el almacén de { warehouseName }'
+        itemDiary: Registro de compra-venta
+        producer: Productor
+        clone:
+            title: Todas sus propiedades serán copiadas
+            subTitle: ¿Desea clonar este artículo?
+
+    list:
+        id: Identificador
+        grouping: Grouping
+        packing: Packing
+        description: Descripción
+        stems: Tallos
+        category: Reino
+        typeName: Tipo
+        intrastat: Intrastat
+        isActive: Activo
+        size: Medida
+        origin: Origen
+        weightByPiece: Peso (gramos)/tallo
+        userName: Comprador
+        stemMultiplier: Multiplicador
+        producer: Productor
+        landed: F. entrega
+    basicData:
+        type: Tipo
+        reference: Referencia
+        relevancy: Relevancia
+        stems: Tallos
+        multiplier: Multiplicador
+        generic: Genérico
+        intrastat: Intrastat
+        expense: Gasto
+        weightByPiece: Peso (gramos)/tallo
+        boxUnits: Unidades/caja
+        recycledPlastic: Plastico reciclado
+        nonRecycledPlastic: Plático no reciclado
+        isActive: Activo
+        hasKgPrice: Precio en kg
+        isFragile: Frágil
+        isFragileTooltip: Se muestra en la web, app que este artículo no puede viajar (coronas, palmas, ...)
+        isPhotoRequested: Hacer foto
+        isPhotoRequestedTooltip: Este artículo necesita una foto
+        description: Descripción
+    fixedPrice:
+        itemFk: ID Artículo
+        groupingPrice: Precio grouping
+        packingPrice: Precio packing
+        hasMinPrice: Tiene precio mínimo
+        minPrice: Precio min
+        started: Inicio
+        ended: Fin
+        warehouse: Almacén
+    create:
+        name: Nombre
+        tag: Etiqueta
+        priority: Prioridad
+        type: Tipo
+        intrastat: Intrastat
+        origin: Origen
+    summary:
+        basicData: 'Datos básicos'
+        otherData: 'Otros datos'
+        description: 'Descripción'
+        tax: 'IVA'
+        tags: 'Etiquetas'
+        botanical: 'Botánico'
+        barcode: 'Código de barras'
+        name: 'Nombre'
+        completeName: 'Nombre completo'
+        family: 'Familia'
+        size: 'Medida'
+        origin: 'Origen'
+        stems: 'Tallos'
+        multiplier: 'Multiplicador'
+        buyer: 'Comprador'
+        doPhoto: 'Hacer foto'
+        intrastatCode: 'Código intrastat'
+        intrastat: 'Intrastat'
+        ref: 'Referencia'
+        relevance: 'Relevancia'
+        weight: 'Peso (gramos)/tallo'
+        units: 'Unidades/caja'
+        expense: 'Gasto'
+        generic: 'Genérico'
+        recycledPlastic: 'Plástico reciclado'
+        nonRecycledPlastic: 'Plástico no reciclado'
+        minSalesQuantity: 'Cantidad mínima de venta'
+        genus: 'Genus'
+        specie: 'Specie'
+    buyRequest:
+        ticketId: 'ID Ticket'
+        shipped: 'F. envío'
+        requester: 'Solicitante'
+        requested: 'Solicitado'
+        price: 'Precio'
+        attender: 'Comprador'
+        item: 'Artículo'
+        achieved: 'Conseguido'
+        concept: 'Concepto'
+        state: 'Estado'

From 2d8789312b41049ae833bc9f05320af1256e3af8 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 24 Oct 2024 11:34:55 +0200
Subject: [PATCH 070/150] test: refs #6943 #6943 remove skip tests

---
 .../integration/client/clientList.spec.js      | 17 -----------------
 test/cypress/support/commands.js               | 18 ------------------
 2 files changed, 35 deletions(-)

diff --git a/test/cypress/integration/client/clientList.spec.js b/test/cypress/integration/client/clientList.spec.js
index 1b41e55cd..f150fc190 100644
--- a/test/cypress/integration/client/clientList.spec.js
+++ b/test/cypress/integration/client/clientList.spec.js
@@ -43,21 +43,4 @@ describe('Client list', () => {
             cy.url().should('include', `/customer/${id}/summary`);
         });
     });
-
-    it.skip('Client founded create ticket', () => {
-        const search = 'Jessica Jones';
-        cy.searchByLabel('Name', search);
-        cy.clickButtonsDescriptor(2);
-        cy.waitForElement('#formModel');
-        cy.waitForElement('.q-form');
-        cy.checkValueSelectForm(1, search);
-    });
-    it.skip('Client founded create order', () => {
-        const search = 'Jessica Jones';
-        cy.searchByLabel('Name', search);
-        cy.clickButtonsDescriptor(3);
-        cy.waitForElement('#formModel');
-        cy.waitForElement('.q-form');
-        cy.checkValueForm(2, search);
-    });
 });
diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js
index 9106a64cd..33dfa85df 100755
--- a/test/cypress/support/commands.js
+++ b/test/cypress/support/commands.js
@@ -262,12 +262,6 @@ Cypress.Commands.add('openUserPanel', () => {
     ).click();
 });
 
-Cypress.Commands.add('clickButtonsDescriptor', (id) => {
-    cy.get(`.actions > .q-card__actions> .q-btn:nth-child(${id})`)
-        .invoke('removeAttr', 'target')
-        .click();
-});
-
 Cypress.Commands.add('openActions', (row) => {
     cy.get('tbody > tr').eq(row).find('.actions > .q-btn').click();
 });
@@ -281,18 +275,6 @@ Cypress.Commands.add('checkNotification', (type) => {
     cy.get('.q-notification__message').should('have.text', values[type]);
 });
 
-Cypress.Commands.add('checkValueForm', (id, search) => {
-    cy.get(
-        `.grid-create > :nth-child(${id}) > .q-field__inner>.q-field__control> .q-field__control-container>.q-field__native >.q-field__input`
-    ).should('have.value', search);
-});
-
-Cypress.Commands.add('checkValueSelectForm', (id, search) => {
-    cy.get(
-        `.grid-create > :nth-child(${id}) > .q-field > .q-field__inner > .q-field__control > .q-field__control-container>.q-field__native>.q-field__input`
-    ).should('have.value', search);
-});
-
 Cypress.Commands.add('searchByLabel', (label, value) => {
     cy.get(`[label="${label}"] > .q-field > .q-field__inner`).type(`${value}{enter}`);
 });

From f1350dece519b9a832656c6434eb910619fd618e Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Thu, 24 Oct 2024 11:43:29 +0200
Subject: [PATCH 071/150] fix: refs #7524 changes

---
 src/pages/Ticket/TicketAdvance.vue | 57 ++++++++++++------------------
 1 file changed, 22 insertions(+), 35 deletions(-)

diff --git a/src/pages/Ticket/TicketAdvance.vue b/src/pages/Ticket/TicketAdvance.vue
index f6fb37e21..7db2b54b3 100644
--- a/src/pages/Ticket/TicketAdvance.vue
+++ b/src/pages/Ticket/TicketAdvance.vue
@@ -243,36 +243,31 @@ const requestComponentUpdate = async (ticket, isWithoutNegatives) => {
 };
 
 const moveTicketsAdvance = async () => {
-    try {
-        let ticketsToMove = [];
-        for (const ticket of selectedTickets.value) {
-            if (!ticket.id) {
-                try {
-                    const { query, params } = await requestComponentUpdate(ticket, false);
-                    axios.post(query, params);
-                } catch (e) {
-                    console.error('Error moving ticket', e);
-                }
-                continue;
+    let ticketsToMove = [];
+    for (const ticket of selectedTickets.value) {
+        if (!ticket.id) {
+            try {
+                const { query, params } = await requestComponentUpdate(ticket, false);
+                axios.post(query, params);
+            } catch (e) {
+                console.error('Error moving ticket', e);
             }
-            ticketsToMove.push({
-                originId: ticket.futureId,
-                destinationId: ticket.id,
-                originShipped: ticket.futureShipped,
-                destinationShipped: ticket.shipped,
-                workerFk: ticket.workerFk,
-            });
+            continue;
         }
-
-        const params = { tickets: ticketsToMove };
-        await axios.post('Tickets/merge', params);
-        vnTableRef.value.reload();
-        selectedTickets.value = [];
-        if (ticketsToMove.length)
-            notify(t('advanceTickets.moveTicketSuccess'), 'positive');
-    } catch (error) {
-        console.error('Error moving tickets', error);
+        ticketsToMove.push({
+            originId: ticket.futureId,
+            destinationId: ticket.id,
+            originShipped: ticket.futureShipped,
+            destinationShipped: ticket.shipped,
+            workerFk: ticket.workerFk,
+        });
     }
+
+    const params = { tickets: ticketsToMove };
+    await axios.post('Tickets/merge', params);
+    vnTableRef.value.reload();
+    selectedTickets.value = [];
+    if (ticketsToMove.length) notify(t('advanceTickets.moveTicketSuccess'), 'positive');
 };
 
 const progressLength = ref(0);
@@ -313,8 +308,6 @@ const splitTickets = async () => {
                 progressAdd(ticket.futureId);
             }
         }
-    } catch (error) {
-        console.error('Error splitting tickets', error);
     } finally {
         vnTableRef.value.reload();
     }
@@ -400,12 +393,6 @@ watch(
         auto-load
         @on-fetch="(data) => (zonesOptions = data)"
     />
-    <!-- Fix searchbar #8154 -->
-    <VnSearchbar
-        data-key="advanceTickets"
-        :label="t('weeklyTickets.search')"
-        :info="t('weeklyTickets.searchInfo')"
-    />
     <VnSubToolbar>
         <template #st-data>
             <QBtn

From b02199eb07e63ac60ece266ab3139fdc74fba6ed Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Thu, 24 Oct 2024 12:03:49 +0200
Subject: [PATCH 072/150] fix: reload window when price is recalculated

---
 src/pages/Ticket/Card/TicketSale.vue            | 1 +
 src/pages/Ticket/Card/TicketSaleMoreActions.vue | 4 +++-
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/src/pages/Ticket/Card/TicketSale.vue b/src/pages/Ticket/Card/TicketSale.vue
index f179257f7..3e89e96bf 100644
--- a/src/pages/Ticket/Card/TicketSale.vue
+++ b/src/pages/Ticket/Card/TicketSale.vue
@@ -476,6 +476,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
                     :ticket-config="ticketConfig"
                     @get-mana="getMana()"
                     @update-discounts="updateDiscount"
+                    @re-calculated-price="resetChanges"
                 />
                 <QBtn
                     color="primary"
diff --git a/src/pages/Ticket/Card/TicketSaleMoreActions.vue b/src/pages/Ticket/Card/TicketSaleMoreActions.vue
index 94db67be2..a35e21f3f 100644
--- a/src/pages/Ticket/Card/TicketSaleMoreActions.vue
+++ b/src/pages/Ticket/Card/TicketSaleMoreActions.vue
@@ -14,7 +14,7 @@ import { toDateFormat } from 'src/filters/date';
 import { useRole } from 'src/composables/useRole';
 import { useVnConfirm } from 'composables/useVnConfirm';
 
-const emit = defineEmits(['updateDiscounts', 'getMana']);
+const emit = defineEmits(['updateDiscounts', 'getMana', 'reCalculatedPrice']);
 
 const props = defineProps({
     disable: {
@@ -105,8 +105,10 @@ const showSmsDialog = (template) => {
 const calculateSalePrice = async () => {
     if (!props.sales) return;
 
+    console.log('CALLED');
     await axios.post(`Sales/recalculatePrice`, props.sales);
     notify(t('globals.dataSaved'), 'positive');
+    emit('reCalculatedPrice', props.sales);
 };
 
 const changeMultipleDiscount = () => {

From c9e82f2caf728a475440d6faf4a0d447edc51129 Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Thu, 24 Oct 2024 12:04:54 +0200
Subject: [PATCH 073/150] refactor: deleted log

---
 src/pages/Ticket/Card/TicketSaleMoreActions.vue | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/pages/Ticket/Card/TicketSaleMoreActions.vue b/src/pages/Ticket/Card/TicketSaleMoreActions.vue
index a35e21f3f..43ba39b91 100644
--- a/src/pages/Ticket/Card/TicketSaleMoreActions.vue
+++ b/src/pages/Ticket/Card/TicketSaleMoreActions.vue
@@ -105,7 +105,6 @@ const showSmsDialog = (template) => {
 const calculateSalePrice = async () => {
     if (!props.sales) return;
 
-    console.log('CALLED');
     await axios.post(`Sales/recalculatePrice`, props.sales);
     notify(t('globals.dataSaved'), 'positive');
     emit('reCalculatedPrice', props.sales);

From c9cfb2b1cf38854ca7c7d36217c25776540a8cd5 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Thu, 24 Oct 2024 12:29:46 +0200
Subject: [PATCH 074/150] fix: refs #7524 changes

---
 src/pages/Ticket/TicketAdvance.vue | 57 ++++++++++++------------------
 1 file changed, 22 insertions(+), 35 deletions(-)

diff --git a/src/pages/Ticket/TicketAdvance.vue b/src/pages/Ticket/TicketAdvance.vue
index f6fb37e21..7db2b54b3 100644
--- a/src/pages/Ticket/TicketAdvance.vue
+++ b/src/pages/Ticket/TicketAdvance.vue
@@ -243,36 +243,31 @@ const requestComponentUpdate = async (ticket, isWithoutNegatives) => {
 };
 
 const moveTicketsAdvance = async () => {
-    try {
-        let ticketsToMove = [];
-        for (const ticket of selectedTickets.value) {
-            if (!ticket.id) {
-                try {
-                    const { query, params } = await requestComponentUpdate(ticket, false);
-                    axios.post(query, params);
-                } catch (e) {
-                    console.error('Error moving ticket', e);
-                }
-                continue;
+    let ticketsToMove = [];
+    for (const ticket of selectedTickets.value) {
+        if (!ticket.id) {
+            try {
+                const { query, params } = await requestComponentUpdate(ticket, false);
+                axios.post(query, params);
+            } catch (e) {
+                console.error('Error moving ticket', e);
             }
-            ticketsToMove.push({
-                originId: ticket.futureId,
-                destinationId: ticket.id,
-                originShipped: ticket.futureShipped,
-                destinationShipped: ticket.shipped,
-                workerFk: ticket.workerFk,
-            });
+            continue;
         }
-
-        const params = { tickets: ticketsToMove };
-        await axios.post('Tickets/merge', params);
-        vnTableRef.value.reload();
-        selectedTickets.value = [];
-        if (ticketsToMove.length)
-            notify(t('advanceTickets.moveTicketSuccess'), 'positive');
-    } catch (error) {
-        console.error('Error moving tickets', error);
+        ticketsToMove.push({
+            originId: ticket.futureId,
+            destinationId: ticket.id,
+            originShipped: ticket.futureShipped,
+            destinationShipped: ticket.shipped,
+            workerFk: ticket.workerFk,
+        });
     }
+
+    const params = { tickets: ticketsToMove };
+    await axios.post('Tickets/merge', params);
+    vnTableRef.value.reload();
+    selectedTickets.value = [];
+    if (ticketsToMove.length) notify(t('advanceTickets.moveTicketSuccess'), 'positive');
 };
 
 const progressLength = ref(0);
@@ -313,8 +308,6 @@ const splitTickets = async () => {
                 progressAdd(ticket.futureId);
             }
         }
-    } catch (error) {
-        console.error('Error splitting tickets', error);
     } finally {
         vnTableRef.value.reload();
     }
@@ -400,12 +393,6 @@ watch(
         auto-load
         @on-fetch="(data) => (zonesOptions = data)"
     />
-    <!-- Fix searchbar #8154 -->
-    <VnSearchbar
-        data-key="advanceTickets"
-        :label="t('weeklyTickets.search')"
-        :info="t('weeklyTickets.searchInfo')"
-    />
     <VnSubToolbar>
         <template #st-data>
             <QBtn

From 3f640c650b12152f3a086464d3b0140a5bc6198d Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 24 Oct 2024 12:32:15 +0200
Subject: [PATCH 075/150] perf: refs #7283 #7283 handle i18n

---
 src/i18n/locale/en.yml       | 108 -----------------------------------
 src/pages/Item/locale/en.yml |   1 -
 src/pages/Item/locale/es.yml |   1 -
 3 files changed, 110 deletions(-)

diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index b19363c12..c1748c8ff 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -1055,114 +1055,6 @@ travel:
         warehouse: Warehouse
         travelFileDescription: 'Travel id { travelId }'
         file: File
-item:
-    searchbar:
-        label: Search item
-    descriptor:
-        item: Item
-        buyer: Buyer
-        color: Color
-        category: Category
-        stems: Stems
-        visible: Visible
-        available: Available
-        warehouseText: 'Calculated on the warehouse of { warehouseName }'
-        itemDiary: Item diary
-        producer: Producer
-    list:
-        id: Identifier
-        grouping: Grouping
-        packing: Packing
-        description: Description
-        stems: Stems
-        category: Category
-        typeName: Type
-        intrastat: Intrastat
-        isActive: Active
-        size: Size
-        origin: Origin
-        userName: Buyer
-        weightByPiece: Weight/Piece
-        stemMultiplier: Multiplier
-        producer: Producer
-        landed: Landed
-    basicData:
-        type: Type
-        reference: Reference
-        relevancy: Relevancy
-        stems: Stems
-        multiplier: Multiplier
-        generic: Generic
-        intrastat: Intrastat
-        expense: Expense
-        weightByPiece: Weight/Piece
-        boxUnits: Units/Box
-        recycledPlastic: Recycled Plastic
-        nonRecycledPlastic: Non recycled plastic
-        isActive: Active
-        hasKgPrice: Price in kg
-        isFragile: Fragile
-        isFragileTooltip: Is shown at website, app that this item cannot travel (wreath, palms, ...)
-        isPhotoRequested: Do photo
-        isPhotoRequestedTooltip: This item does need a photo
-        description: Description
-    fixedPrice:
-        itemFk: Item ID
-        groupingPrice: Grouping price
-        packingPrice: Packing price
-        hasMinPrice: Has min price
-        minPrice: Min price
-        started: Started
-        ended: Ended
-        warehouse: Warehouse
-    create:
-        name: Name
-        tag: Tag
-        priority: Priority
-        type: Type
-        intrastat: Intrastat
-        origin: Origin
-    buyRequest:
-        ticketId: 'Ticket ID'
-        shipped: 'Shipped'
-        requester: 'Requester'
-        requested: 'Requested'
-        price: 'Price'
-        attender: 'Attender'
-        item: 'Item'
-        achieved: 'Achieved'
-        concept: 'Concept'
-        state: 'State'
-    summary:
-        basicData: 'Basic data'
-        otherData: 'Other data'
-        description: 'Description'
-        tax: 'Tax'
-        tags: 'Tags'
-        botanical: 'Botanical'
-        barcode: 'Barcode'
-        name: 'Nombre'
-        completeName: 'Nombre completo'
-        family: 'Familia'
-        size: 'Medida'
-        origin: 'Origen'
-        stems: 'Tallos'
-        multiplier: 'Multiplicador'
-        buyer: 'Comprador'
-        doPhoto: 'Do photo'
-        intrastatCode: 'Código intrastat'
-        intrastat: 'Intrastat'
-        ref: 'Referencia'
-        relevance: 'Relevancia'
-        weight: 'Peso (gramos)/tallo'
-        units: 'Unidades/caja'
-        expense: 'Gasto'
-        generic: 'Genérico'
-        recycledPlastic: 'Plástico reciclado'
-        nonRecycledPlastic: 'Plástico no reciclado'
-        minSalesQuantity: 'Cantidad mínima de venta'
-        genus: 'Genus'
-        specie: 'Specie'
 components:
     topbar: {}
     itemsFilterPanel:
diff --git a/src/pages/Item/locale/en.yml b/src/pages/Item/locale/en.yml
index 034e39a17..53310cbd0 100644
--- a/src/pages/Item/locale/en.yml
+++ b/src/pages/Item/locale/en.yml
@@ -105,7 +105,6 @@ item:
         clone:
             title: All its properties will be copied
             subTitle: Do you want to clone this item?
-
     list:
         id: Identifier
         grouping: Grouping
diff --git a/src/pages/Item/locale/es.yml b/src/pages/Item/locale/es.yml
index 917bd7e5f..1ade9b955 100644
--- a/src/pages/Item/locale/es.yml
+++ b/src/pages/Item/locale/es.yml
@@ -105,7 +105,6 @@ item:
         clone:
             title: Todas sus propiedades serán copiadas
             subTitle: ¿Desea clonar este artículo?
-
     list:
         id: Identificador
         grouping: Grouping

From 64f36c7d4b7a8210df9af7e087660094c0a28c2c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Carlos=20Andr=C3=A9s?= <carlosap@verdnatura.es>
Date: Thu, 24 Oct 2024 12:35:11 +0200
Subject: [PATCH 076/150] feat: refs #8087 Traspasar redadas a travels

---
 src/i18n/locale/en.yml                    | 5 +----
 src/i18n/locale/es.yml                    | 5 +----
 src/pages/Entry/Card/EntryBasicData.vue   | 6 ------
 src/pages/Entry/Card/EntryDescriptor.vue  | 4 ++--
 src/pages/Entry/Card/EntrySummary.vue     | 5 -----
 src/pages/Entry/EntryList.vue             | 4 ++--
 src/pages/Travel/Card/TravelBasicData.vue | 4 ++++
 src/pages/Travel/Card/TravelSummary.vue   | 4 ++++
 8 files changed, 14 insertions(+), 23 deletions(-)

diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index aa8df17e2..d1ea5ceb9 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -106,6 +106,7 @@ globals:
     weight: Weight
     error: Ups! Something went wrong
     recalc: Recalculate
+    daysInForward: Forward days
     pageTitles:
         logIn: Login
         addressEdit: Update address
@@ -369,7 +370,6 @@ entry:
             companyFk: Company
             travelFk: Travel
             isExcludedFromAvailable: Inventory
-            isRaid: Raid
             invoiceAmount: Import
     summary:
         commission: Commission
@@ -380,7 +380,6 @@ entry:
         ordered: Ordered
         confirmed: Confirmed
         booked: Booked
-        raid: Raid
         excludedFromAvailable: Inventory
         travelReference: Reference
         travelAgency: Agency
@@ -413,7 +412,6 @@ entry:
         ordered: Ordered
         confirmed: Confirmed
         booked: Booked
-        raid: Raid
         excludedFromAvailable: Inventory
         agency: Agency
         warehouseOut: Warehouse Out
@@ -476,7 +474,6 @@ entry:
             packingOut: Package out
             landing: Landing
             isExcludedFromAvailable: Es inventory
-            isRaid: Raid
 ticket:
     pageTitles:
         tickets: Tickets
diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml
index 575e2c6c7..18e580893 100644
--- a/src/i18n/locale/es.yml
+++ b/src/i18n/locale/es.yml
@@ -108,6 +108,7 @@ globals:
     weight: Peso
     error: ¡Ups! Algo salió mal
     recalc: Recalcular
+    daysInForward: Días en el futuro
     pageTitles:
         logIn: Inicio de sesión
         addressEdit: Modificar consignatario
@@ -371,7 +372,6 @@ entry:
             companyFk: Empresa
             travelFk: Envio
             isExcludedFromAvailable: Inventario
-            isRaid: Redada
             invoiceAmount: Importe
     summary:
         commission: Comisión
@@ -382,7 +382,6 @@ entry:
         ordered: Pedida
         confirmed: Confirmada
         booked: Contabilizada
-        raid: Redada
         excludedFromAvailable: Inventario
         travelReference: Referencia
         travelAgency: Agencia
@@ -415,7 +414,6 @@ entry:
         ordered: Pedida
         confirmed: Confirmado
         booked: Asentado
-        raid: Redada
         excludedFromAvailable: Inventario
         agency: Agencia
         warehouseOut: Alm. salida
@@ -478,7 +476,6 @@ entry:
             packingOut: Embalaje envíos
             landing: Llegada
             isExcludedFromAvailable: Es inventario
-            isRaid: Redada
 ticket:
     pageTitles:
         tickets: Tickets
diff --git a/src/pages/Entry/Card/EntryBasicData.vue b/src/pages/Entry/Card/EntryBasicData.vue
index b81b1db22..d60ed8645 100644
--- a/src/pages/Entry/Card/EntryBasicData.vue
+++ b/src/pages/Entry/Card/EntryBasicData.vue
@@ -168,12 +168,6 @@ const onFilterTravelSelected = (formData, id) => {
                     v-model="data.isExcludedFromAvailable"
                     :label="t('entry.basicData.excludedFromAvailable')"
                 />
-                <QCheckbox v-model="data.isRaid" :label="t('entry.basicData.raid')" />
-                <QCheckbox
-                    v-if="isAdministrative()"
-                    v-model="data.isBooked"
-                    :label="t('entry.basicData.booked')"
-                />
             </VnRow>
         </template>
     </FormModel>
diff --git a/src/pages/Entry/Card/EntryDescriptor.vue b/src/pages/Entry/Card/EntryDescriptor.vue
index b22d6ba53..d85e5e1a0 100644
--- a/src/pages/Entry/Card/EntryDescriptor.vue
+++ b/src/pages/Entry/Card/EntryDescriptor.vue
@@ -32,7 +32,6 @@ const entryFilter = {
         {
             relation: 'travel',
             scope: {
-                fields: ['id', 'landed', 'shipped', 'agencyModeFk', 'warehouseOutFk'],
                 include: [
                     {
                         relation: 'agency',
@@ -143,8 +142,9 @@ watch;
                 >
                     <QTooltip>{{ t('Inventory entry') }}</QTooltip>
                 </QIcon>
+                {{ console.log('currentEntry', currentEntry) }}
                 <QIcon
-                    v-if="currentEntry?.isRaid"
+                    v-if="currentEntry?.travel?.daysInForward"
                     name="vn:net"
                     color="primary"
                     size="xs"
diff --git a/src/pages/Entry/Card/EntrySummary.vue b/src/pages/Entry/Card/EntrySummary.vue
index c2f1e6b57..62e13551a 100644
--- a/src/pages/Entry/Card/EntrySummary.vue
+++ b/src/pages/Entry/Card/EntrySummary.vue
@@ -259,11 +259,6 @@ const fetchEntryBuys = async () => {
                     v-model="entry.isBooked"
                     :disable="true"
                 />
-                <QCheckbox
-                    :label="t('entry.summary.raid')"
-                    v-model="entry.isRaid"
-                    :disable="true"
-                />
                 <QCheckbox
                     :label="t('entry.summary.excludedFromAvailable')"
                     v-model="entry.isExcludedFromAvailable"
diff --git a/src/pages/Entry/EntryList.vue b/src/pages/Entry/EntryList.vue
index 6f7ff1935..00b6c8626 100644
--- a/src/pages/Entry/EntryList.vue
+++ b/src/pages/Entry/EntryList.vue
@@ -168,8 +168,8 @@ const columns = computed(() => [
     },
     {
         align: 'left',
-        label: t('entry.list.tableVisibleColumns.isRaid'),
-        name: 'isRaid',
+        label: t('entry.list.tableVisibleColumns.daysInForward'),
+        name: 'daysInForward',
         chip: {
             color: null,
             condition: (value) => value,
diff --git a/src/pages/Travel/Card/TravelBasicData.vue b/src/pages/Travel/Card/TravelBasicData.vue
index a3620a6ba..aaf8abb91 100644
--- a/src/pages/Travel/Card/TravelBasicData.vue
+++ b/src/pages/Travel/Card/TravelBasicData.vue
@@ -72,6 +72,10 @@ const agenciesOptions = ref([]);
             </VnRow>
             <VnRow>
                 <VnInput v-model="data.m3" label="m3" />
+                <VnInput
+                    :label="t('globals.daysInForward')"
+                    v-model="data.daysInForward"
+                />
             </VnRow>
             <VnRow>
                 <QCheckbox
diff --git a/src/pages/Travel/Card/TravelSummary.vue b/src/pages/Travel/Card/TravelSummary.vue
index 4be198493..59059b0db 100644
--- a/src/pages/Travel/Card/TravelSummary.vue
+++ b/src/pages/Travel/Card/TravelSummary.vue
@@ -303,6 +303,10 @@ const getLink = (param) => `#/travel/${entityId.value}/${param}`;
                 <VnLv :label="t('globals.reference')" :value="travel.ref" />
                 <VnLv label="m³" :value="travel.m3" />
                 <VnLv :label="t('globals.totalEntries')" :value="travel.totalEntries" />
+                <VnLv
+                    :label="t('globals.daysInForward')"
+                    :value="travel?.daysInForward"
+                />
             </QCard>
             <QCard class="full-width">
                 <VnTitle :text="t('travel.summary.entries')" />

From f52b4c9a598dc400c3379f800b2f1157f339159f Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Thu, 24 Oct 2024 13:24:45 +0200
Subject: [PATCH 077/150] fix: refs #7524 vnProgressModal

---
 src/components/common/VnProgressModal.vue | 22 +++++-----------------
 1 file changed, 5 insertions(+), 17 deletions(-)

diff --git a/src/components/common/VnProgressModal.vue b/src/components/common/VnProgressModal.vue
index cfd948d5f..99bf15a7e 100644
--- a/src/components/common/VnProgressModal.vue
+++ b/src/components/common/VnProgressModal.vue
@@ -9,10 +9,6 @@ const $props = defineProps({
         type: Number, //Progress value (1.0 > x > 0.0)
         required: true,
     },
-    showDialog: {
-        type: Boolean,
-        required: true,
-    },
     cancelled: {
         type: Boolean,
         required: false,
@@ -24,25 +20,17 @@ const emit = defineEmits(['cancel', 'close']);
 
 const dialogRef = ref(null);
 
-const _showDialog = computed({
-    get: () => $props.showDialog,
-    set: (value) => {
-        if (value) dialogRef.value.show();
-    },
+const showDialog = defineModel('showDialog', {
+    type: Boolean,
+    default: false,
 });
 
 const _progress = computed(() => $props.progress);
-
 const progressLabel = computed(() => `${Math.round($props.progress * 100)}%`);
-
-const cancel = () => {
-    dialogRef.value.hide();
-    emit('cancel');
-};
 </script>
 
 <template>
-    <QDialog ref="dialogRef" v-model="_showDialog" @hide="onDialogHide">
+    <QDialog ref="dialogRef" v-model="showDialog" @hide="emit('close')">
         <QCard class="full-width dialog">
             <QCardSection class="row">
                 <span class="text-h6">{{ t('Progress') }}</span>
@@ -80,7 +68,7 @@ const cancel = () => {
                     type="button"
                     flat
                     class="text-primary"
-                    @click="cancel()"
+                    v-close-popup
                 >
                     {{ t('globals.cancel') }}
                 </QBtn>

From 823c354ef1a376b988e34a7f2f50f6f210ee34b7 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Thu, 24 Oct 2024 13:27:08 +0200
Subject: [PATCH 078/150] fix: refs #7524 vnProgressModal

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

diff --git a/src/components/common/VnProgressModal.vue b/src/components/common/VnProgressModal.vue
index 99bf15a7e..23fb8ae70 100644
--- a/src/components/common/VnProgressModal.vue
+++ b/src/components/common/VnProgressModal.vue
@@ -35,7 +35,7 @@ const progressLabel = computed(() => `${Math.round($props.progress * 100)}%`);
             <QCardSection class="row">
                 <span class="text-h6">{{ t('Progress') }}</span>
                 <QSpace />
-                <QBtn icon="close" flat round dense @click="emit('close')" />
+                <QBtn icon="close" flat round dense v-close-popup />
             </QCardSection>
             <QCardSection>
                 <div class="column">

From 671d9fd6fb0f6686ade7b54bfe9e36b6d8a11a8c Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Thu, 24 Oct 2024 13:38:13 +0200
Subject: [PATCH 079/150] fix: refs #7283 itemtype fix

---
 src/pages/Item/ItemTypeList.vue | 27 ++++++++++++++++++++-------
 1 file changed, 20 insertions(+), 7 deletions(-)

diff --git a/src/pages/Item/ItemTypeList.vue b/src/pages/Item/ItemTypeList.vue
index d9f7bba85..2c1153016 100644
--- a/src/pages/Item/ItemTypeList.vue
+++ b/src/pages/Item/ItemTypeList.vue
@@ -1,14 +1,14 @@
 <script setup>
 import { useI18n } from 'vue-i18n';
-import { useRoute } from 'vue-router';
 import { ref, computed } from 'vue';
 import ItemTypeSearchbar from '../ItemType/ItemTypeSearchbar.vue';
 import VnTable from 'components/VnTable/VnTable.vue';
+import FetchData from 'components/FetchData.vue';
 
-const route = useRoute();
 const { t } = useI18n();
 const tableRef = ref();
-const entityId = computed(() => route.params.id);
+const workerOptions = ref([]);
+const ItemCategoriesOptions = ref([]);
 
 const columns = computed(() => [
     {
@@ -40,7 +40,7 @@ const columns = computed(() => [
         create: true,
         component: 'select',
         attrs: {
-            url: 'Workers',
+            options: workerOptions.value,
             optionLabel: 'firstName',
             optionValue: 'id',
         },
@@ -54,8 +54,9 @@ const columns = computed(() => [
         create: true,
         component: 'select',
         attrs: {
-            url: 'ItemCategories',
+            options: ItemCategoriesOptions.value,
             fields: ['id', 'name'],
+            order: 'name ASC',
         },
         cardVisible: false,
         visible: false,
@@ -77,6 +78,18 @@ const columns = computed(() => [
 </script>
 
 <template>
+    <FetchData
+        url="Workers"
+        :filter="{ fields: ['id', 'firstName'], order: ['firstName ASC'] }"
+        @on-fetch="(data) => (workerOptions = data)"
+        auto-load
+    />
+    <FetchData
+        url="ItemCategories"
+        :filter="{ fields: ['id', 'name'], order: ['name ASC'] }"
+        @on-fetch="(data) => (ItemCategoriesOptions = data)"
+        auto-load
+    />
     <ItemTypeSearchbar />
     <VnTable
         ref="tableRef"
@@ -102,8 +115,8 @@ const columns = computed(() => [
         id: Id
         code: Código
         name: Nombre
-        worker: Encargado
-        ItemCategory: Categoría
+        worker: Trabajador
+        ItemCategory: Reino
         Temperature: Temperatura
         Create ItemTypes: Crear familia
     en:

From 70dcadc57d58831ae7149be462536f22e00469cd Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 24 Oct 2024 11:52:13 +0000
Subject: [PATCH 080/150] Actualizar src/components/CreateNewPostcodeForm.vue

---
 src/components/CreateNewPostcodeForm.vue | 17 +++++++----------
 1 file changed, 7 insertions(+), 10 deletions(-)

diff --git a/src/components/CreateNewPostcodeForm.vue b/src/components/CreateNewPostcodeForm.vue
index 030ca1388..afd3fc025 100644
--- a/src/components/CreateNewPostcodeForm.vue
+++ b/src/components/CreateNewPostcodeForm.vue
@@ -108,11 +108,11 @@ watch(
 
 watch(
     () => postcodeFormData.provinceFk,
-    async (newProvinceFk) => {
+    async (newProvinceFk, oldValueFk) => {
         if (Array.isArray(newProvinceFk)) {
             newProvinceFk = newProvinceFk[0];
         }
-        if (newProvinceFk !== postcodeFormData.provinceFk) {
+        if (newProvinceFk !== oldValueFk) {
             await townsFetchDataRef.value.fetch({
                 where: { provinceFk: newProvinceFk },
             });
@@ -147,13 +147,7 @@ async function handleCountries(data) {
         auto-load
         url="Towns/location"
     />
-    <FetchData
-        @on-fetch="handleCountries"
-        :sort-by="['name ASC']"
-        :limit="30"
-        auto-load
-        url="Countries"
-    />
+
     <FormModelPopup
         url-create="postcodes"
         model="postcode"
@@ -219,8 +213,11 @@ async function handleCountries(data) {
                     @on-province-created="onProvinceCreated"
                 />
                 <VnSelect
+                    url="Countries"
+                    :limit="30"
+                    :sort-by="['name ASC']"
                     :label="t('Country')"
-                    :options="countriesOptions"
+                    @update:options="handleCountries"
                     hide-selected
                     option-label="name"
                     option-value="id"

From c2043902f8f77a6f4957b82d7f7576f540bf8f1e Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Thu, 24 Oct 2024 14:08:11 +0200
Subject: [PATCH 081/150] fix: refs #7283 fix required

---
 src/pages/Item/ItemRequestDenyForm.vue | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/pages/Item/ItemRequestDenyForm.vue b/src/pages/Item/ItemRequestDenyForm.vue
index be70fb799..c9a4cbe9c 100644
--- a/src/pages/Item/ItemRequestDenyForm.vue
+++ b/src/pages/Item/ItemRequestDenyForm.vue
@@ -10,6 +10,7 @@ defineProps({
     requestId: {
         type: Number,
         default: null,
+        required: true,
     },
 });
 
@@ -43,6 +44,7 @@ onMounted(async () => {
                         type="textarea"
                         v-model="data.observation"
                         fill-input
+                        :required="true"
                         autogrow
                     />
                 </div>

From 4828e32c9eec24cb349bb5c39cae075653d08538 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Thu, 24 Oct 2024 14:26:16 +0200
Subject: [PATCH 082/150] fix: refs #7652 datakey

---
 src/pages/Worker/Card/WorkerDescriptor.vue      | 7 ++++++-
 src/pages/Worker/Card/WorkerDescriptorProxy.vue | 7 ++++++-
 2 files changed, 12 insertions(+), 2 deletions(-)

diff --git a/src/pages/Worker/Card/WorkerDescriptor.vue b/src/pages/Worker/Card/WorkerDescriptor.vue
index 3675d40f8..b9ccedbfc 100644
--- a/src/pages/Worker/Card/WorkerDescriptor.vue
+++ b/src/pages/Worker/Card/WorkerDescriptor.vue
@@ -17,6 +17,11 @@ const $props = defineProps({
         required: false,
         default: null,
     },
+    dataKey: {
+        type: String,
+        required: false,
+        default: 'workerData',
+    },
 });
 const image = ref(null);
 
@@ -70,7 +75,7 @@ const refetch = async () => await cardDescriptorRef.value.getData();
     <CardDescriptor
         ref="cardDescriptorRef"
         module="Worker"
-        data-key="workerData"
+        :data-key="dataKey"
         url="Workers/descriptor"
         :filter="{ where: { id: entityId } }"
         title="user.nickname"
diff --git a/src/pages/Worker/Card/WorkerDescriptorProxy.vue b/src/pages/Worker/Card/WorkerDescriptorProxy.vue
index a142570f9..43deb7821 100644
--- a/src/pages/Worker/Card/WorkerDescriptorProxy.vue
+++ b/src/pages/Worker/Card/WorkerDescriptorProxy.vue
@@ -12,6 +12,11 @@ const $props = defineProps({
 
 <template>
     <QPopupProxy>
-        <WorkerDescriptor v-if="$props.id" :id="$props.id" :summary="WorkerSummary" />
+        <WorkerDescriptor
+            v-if="$props.id"
+            :id="$props.id"
+            :summary="WorkerSummary"
+            data-key="workerDescriptorProxy"
+        />
     </QPopupProxy>
 </template>

From 26de0cf72f4febeffa487d2b37202b6e1bc1df52 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 24 Oct 2024 12:39:49 +0000
Subject: [PATCH 083/150] perf: remove limit

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

diff --git a/src/components/CreateNewPostcodeForm.vue b/src/components/CreateNewPostcodeForm.vue
index afd3fc025..03cba8ac7 100644
--- a/src/components/CreateNewPostcodeForm.vue
+++ b/src/components/CreateNewPostcodeForm.vue
@@ -214,7 +214,6 @@ async function handleCountries(data) {
                 />
                 <VnSelect
                     url="Countries"
-                    :limit="30"
                     :sort-by="['name ASC']"
                     :label="t('Country')"
                     @update:options="handleCountries"

From 855979d22d3397182210a661717880e7bb04ab2d Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 24 Oct 2024 14:59:14 +0200
Subject: [PATCH 084/150] perf: refs #7283 #7283 i18n params

---
 src/pages/Item/ItemRequest.vue |  1 +
 src/pages/Item/locale/en.yml   | 13 +++++++++++++
 src/pages/Item/locale/es.yml   | 15 +++++++++++++++
 3 files changed, 29 insertions(+)

diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue
index ea265e706..450031a0e 100644
--- a/src/pages/Item/ItemRequest.vue
+++ b/src/pages/Item/ItemRequest.vue
@@ -225,6 +225,7 @@ onMounted(async () => {
         :is-editable="true"
         auto-load
         :disable-option="{ card: true }"
+        chip-locale="item.params"
     >
         <template #column-ticketFk="{ row }">
             <span class="link">
diff --git a/src/pages/Item/locale/en.yml b/src/pages/Item/locale/en.yml
index 53310cbd0..e99853760 100644
--- a/src/pages/Item/locale/en.yml
+++ b/src/pages/Item/locale/en.yml
@@ -89,6 +89,19 @@ itemType:
         category: Category
         temperature: Temperature
 item:
+    params:
+        daysOnward: Days onward
+        search: General search
+        ticketFk: Ticket id
+        attenderFk: Atender
+        clientFk: Client id
+        warehouseFk: Warehouse
+        requesterFk: Salesperson
+        from: From
+        to: To
+        mine: For me
+        state: State
+        myTeam: My team
     searchbar:
         label: Search item
     descriptor:
diff --git a/src/pages/Item/locale/es.yml b/src/pages/Item/locale/es.yml
index 1ade9b955..56c6ec317 100644
--- a/src/pages/Item/locale/es.yml
+++ b/src/pages/Item/locale/es.yml
@@ -88,7 +88,22 @@ itemType:
         worker: Trabajador
         category: Reino
         temperature: Temperatura
+params:
+    state: asfsdf
 item:
+    params:
+        daysOnward: Días adelante
+        search: Búsqueda general
+        ticketFk: Id ticket
+        attenderFk: Comprador
+        clientFk: Id cliente
+        warehouseFk: Almacén
+        requesterFk: Comercial
+        from: Desde
+        to: Hasta
+        mine: Para mi
+        state: Estado
+        myTeam: Mi equipo
     searchbar:
         label: Buscar artículo
     descriptor:

From 92e147355fea8b083948f544daf608fc8db8734c Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Thu, 24 Oct 2024 15:13:05 +0200
Subject: [PATCH 085/150] fix: better performance

---
 src/components/VnTable/VnTable.vue  |  7 ++++---
 src/pages/Account/AccountAcls.vue   | 28 +++++++++++++++++++++++++---
 src/pages/Account/AccountFilter.vue |  1 +
 3 files changed, 30 insertions(+), 6 deletions(-)

diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index 908157610..c1680bf13 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -151,8 +151,8 @@ const tableModes = [
     },
 ];
 onBeforeMount(() => {
-    setUserParams(route.query[$props.searchUrl]);
-    hasParams.value = params.value && Object.keys(params.value).length !== 0;
+    const urlParams = route.query[$props.searchUrl];
+    hasParams.value = urlParams && Object.keys(urlParams).length !== 0;
 });
 
 onMounted(() => {
@@ -185,7 +185,8 @@ watch(
 
 watch(
     () => route.query[$props.searchUrl],
-    (val) => setUserParams(val)
+    (val) => setUserParams(val),
+    { immediate: true, deep: true }
 );
 
 const isTableMode = computed(() => mode.value == TABLE_MODE);
diff --git a/src/pages/Account/AccountAcls.vue b/src/pages/Account/AccountAcls.vue
index dd93a0cb5..63cdac9c7 100644
--- a/src/pages/Account/AccountAcls.vue
+++ b/src/pages/Account/AccountAcls.vue
@@ -9,6 +9,8 @@ import { useQuasar } from 'quasar';
 import VnTable from 'components/VnTable/VnTable.vue';
 import VnSearchbar from 'components/ui/VnSearchbar.vue';
 import VnConfirm from 'components/ui/VnConfirm.vue';
+import FetchData from 'src/components/FetchData.vue';
+import { useValidator } from 'src/composables/useValidator';
 
 defineProps({
     id: {
@@ -23,11 +25,18 @@ const stateStore = useStateStore();
 const quasar = useQuasar();
 
 const tableRef = ref();
-
+const roles = ref();
+const validationsStore = useValidator();
+const { models } = validationsStore;
 const exprBuilder = (param, value) => {
     switch (param) {
         case 'search':
-            return { model: { like: `%${value}%` } };
+            return {
+                or: [
+                    { model: { like: `%${value}%` } },
+                    { property: { like: `%${value}%` } },
+                ],
+            };
         default:
             return { [param]: value };
     }
@@ -47,6 +56,13 @@ const columns = computed(() => [
         label: t('model'),
         cardVisible: true,
         create: true,
+        columnCreate: {
+            label: t('model'),
+            component: 'select',
+            attrs: {
+                options: Object.keys(models),
+            },
+        },
     },
     {
         align: 'left',
@@ -55,9 +71,10 @@ const columns = computed(() => [
         cardVisible: true,
         component: 'select',
         attrs: {
-            url: 'VnRoles',
+            options: roles,
             optionLabel: 'name',
             optionValue: 'name',
+            inputDebounce: 0,
         },
         create: true,
     },
@@ -130,6 +147,11 @@ const deleteAcl = async ({ id }) => {
     />
     <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
     </QDrawer>
+    <FetchData
+        url="VnRoles?fields=['name']"
+        auto-load
+        @on-fetch="(data) => (roles = data)"
+    />
     <VnTable
         ref="tableRef"
         data-key="AccountAcls"
diff --git a/src/pages/Account/AccountFilter.vue b/src/pages/Account/AccountFilter.vue
index 1775aa06b..3c8378d93 100644
--- a/src/pages/Account/AccountFilter.vue
+++ b/src/pages/Account/AccountFilter.vue
@@ -33,6 +33,7 @@ const rolesOptions = ref([]);
         :search-button="true"
         :hidden-tags="['search']"
         :redirect="false"
+        search-url="table"
     >
         <template #tags="{ tag, formatFn }">
             <div class="q-gutter-x-xs">

From 12c236276d1d321ae657a2a005630df210917af6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Carlos=20Andr=C3=A9s?= <carlosap@verdnatura.es>
Date: Thu, 24 Oct 2024 19:32:20 +0200
Subject: [PATCH 086/150] feat: refs #8087 Traspasar redadas a travels

---
 src/i18n/locale/en.yml                     |  3 +-
 src/i18n/locale/es.yml                     |  3 +-
 src/pages/Entry/Card/EntryDescriptor.vue   | 19 +++++-----
 src/pages/Entry/EntryList.vue              | 43 ++++++++++++----------
 src/pages/Entry/locale/en.yml              |  1 -
 src/pages/Entry/locale/es.yml              |  1 -
 src/pages/Travel/Card/TravelBasicData.vue  | 17 ++++++++-
 src/pages/Travel/Card/TravelDescriptor.vue | 17 +++++++++
 src/pages/Travel/Card/TravelSummary.vue    |  2 +-
 src/pages/Travel/TravelList.vue            | 15 ++++++++
 10 files changed, 86 insertions(+), 35 deletions(-)

diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index d1ea5ceb9..928553293 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -106,7 +106,6 @@ globals:
     weight: Weight
     error: Ups! Something went wrong
     recalc: Recalculate
-    daysInForward: Forward days
     pageTitles:
         logIn: Login
         addressEdit: Update address
@@ -313,6 +312,7 @@ globals:
     changePass: Change password
     deleteConfirmTitle: Delete selected elements
     changeState: Change state
+    raid: 'Raid {daysInForward} days'
 errors:
     statusUnauthorized: Access denied
     statusInternalServerError: An internal server error has ocurred
@@ -1039,6 +1039,7 @@ travel:
         warehouseIn: Warehouse In
         delivered: Delivered
         received: Received
+        daysInForward: Days in forward
     thermographs:
         code: Code
         temperature: Temperature
diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml
index 18e580893..52bc821d1 100644
--- a/src/i18n/locale/es.yml
+++ b/src/i18n/locale/es.yml
@@ -108,7 +108,6 @@ globals:
     weight: Peso
     error: ¡Ups! Algo salió mal
     recalc: Recalcular
-    daysInForward: Días en el futuro
     pageTitles:
         logIn: Inicio de sesión
         addressEdit: Modificar consignatario
@@ -317,6 +316,7 @@ globals:
     changePass: Cambiar contraseña
     deleteConfirmTitle: Eliminar los elementos seleccionados
     changeState: Cambiar estado
+    raid: 'Redada {daysInForward} días'
 errors:
     statusUnauthorized: Acceso denegado
     statusInternalServerError: Ha ocurrido un error interno del servidor
@@ -1037,6 +1037,7 @@ travel:
         warehouseIn: Alm. entrada
         delivered: Enviada
         received: Recibida
+        daysInForward: Días redada
     thermographs:
         code: Código
         temperature: Temperatura
diff --git a/src/pages/Entry/Card/EntryDescriptor.vue b/src/pages/Entry/Card/EntryDescriptor.vue
index d85e5e1a0..bbfc73138 100644
--- a/src/pages/Entry/Card/EntryDescriptor.vue
+++ b/src/pages/Entry/Card/EntryDescriptor.vue
@@ -7,7 +7,6 @@ import CardDescriptor from 'components/ui/CardDescriptor.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
 import useCardDescription from 'src/composables/useCardDescription';
 
-import { useState } from 'src/composables/useState';
 import { toDate } from 'src/filters';
 import { usePrintService } from 'composables/usePrintService';
 import { getUrl } from 'src/composables/getUrl';
@@ -23,7 +22,6 @@ const $props = defineProps({
 const route = useRoute();
 const { t } = useI18n();
 const { openReport } = usePrintService();
-const state = useState();
 const entryDescriptorRef = ref(null);
 const url = ref();
 
@@ -74,8 +72,6 @@ const data = ref(useCardDescription());
 const setData = (entity) =>
     (data.value = useCardDescription(entity.supplier?.nickname, entity.id));
 
-const currentEntry = computed(() => state.get('entry'));
-
 const getEntryRedirectionFilter = (entry) => {
     let entryTravel = entry && entry.travel;
 
@@ -132,24 +128,29 @@ watch;
                 :value="entity.travel?.warehouseOut?.name"
             />
         </template>
-        <template #icons>
+        <template #icons="{ entity }">
             <QCardActions class="q-gutter-x-md">
                 <QIcon
-                    v-if="currentEntry?.isExcludedFromAvailable"
+                    v-if="entity?.isExcludedFromAvailable"
                     name="vn:inventory"
                     color="primary"
                     size="xs"
                 >
                     <QTooltip>{{ t('Inventory entry') }}</QTooltip>
                 </QIcon>
-                {{ console.log('currentEntry', currentEntry) }}
                 <QIcon
-                    v-if="currentEntry?.travel?.daysInForward"
+                    v-if="entity?.travel?.daysInForward"
                     name="vn:net"
                     color="primary"
                     size="xs"
                 >
-                    <QTooltip>{{ t('Virtual entry') }}</QTooltip>
+                    <QTooltip>
+                        {{
+                            t('globals.raid', {
+                                daysInForward: entity?.travel?.daysInForward,
+                            })
+                        }}</QTooltip
+                    >
                 </QIcon>
             </QCardActions>
         </template>
diff --git a/src/pages/Entry/EntryList.vue b/src/pages/Entry/EntryList.vue
index 00b6c8626..518d72d82 100644
--- a/src/pages/Entry/EntryList.vue
+++ b/src/pages/Entry/EntryList.vue
@@ -41,6 +41,10 @@ const entryFilter = {
 };
 
 const columns = computed(() => [
+    {
+        name: 'status',
+        hidden: true,
+    },
     {
         align: 'left',
         label: t('entry.list.tableVisibleColumns.id'),
@@ -154,27 +158,8 @@ const columns = computed(() => [
         cardVisible: true,
     },
     {
-        align: 'left',
         label: t('entry.list.tableVisibleColumns.isExcludedFromAvailable'),
         name: 'isExcludedFromAvailable',
-        chip: {
-            color: null,
-            condition: (value) => value,
-            icon: 'vn:inventory',
-        },
-        columnFilter: {
-            inWhere: true,
-        },
-    },
-    {
-        align: 'left',
-        label: t('entry.list.tableVisibleColumns.daysInForward'),
-        name: 'daysInForward',
-        chip: {
-            color: null,
-            condition: (value) => value,
-            icon: 'vn:net',
-        },
         columnFilter: {
             inWhere: true,
         },
@@ -225,6 +210,26 @@ onMounted(async () => {
         auto-load
         :right-search="false"
     >
+        <template #column-status="{ row }">
+            <div class="row q-gutter-xs">
+                <QIcon
+                    v-if="!!row.isExcludedFromAvailable"
+                    name="vn:inventory"
+                    color="primary"
+                >
+                    <QTooltip>{{
+                        t('entry.list.tableVisibleColumns.isExcludedFromAvailable')
+                    }}</QTooltip>
+                </QIcon>
+                <QIcon v-if="!!row.daysInForward" name="vn:net" color="primary">
+                    <QTooltip>
+                        {{
+                            t('globals.raid', { daysInForward: row.daysInForward })
+                        }}</QTooltip
+                    >
+                </QIcon>
+            </div>
+        </template>
         <template #column-supplierFk="{ row }">
             <span class="link" @click.stop>
                 {{ row.supplierName }}
diff --git a/src/pages/Entry/locale/en.yml b/src/pages/Entry/locale/en.yml
index f9dbd0589..cd5113d84 100644
--- a/src/pages/Entry/locale/en.yml
+++ b/src/pages/Entry/locale/en.yml
@@ -1,7 +1,6 @@
 entryList:
     list:
         inventoryEntry: Inventory entry
-        virtualEntry: Virtual entry
 entryFilter:
     filter:
         search: General search
diff --git a/src/pages/Entry/locale/es.yml b/src/pages/Entry/locale/es.yml
index feeea1fc9..3007c5d44 100644
--- a/src/pages/Entry/locale/es.yml
+++ b/src/pages/Entry/locale/es.yml
@@ -4,7 +4,6 @@ You can search by entry reference: Puedes buscar por referencia de la entrada
 entryList:
     list:
         inventoryEntry: Es inventario
-        virtualEntry: Es una redada
 entryFilter:
     filter:
         search: Búsqueda general
diff --git a/src/pages/Travel/Card/TravelBasicData.vue b/src/pages/Travel/Card/TravelBasicData.vue
index aaf8abb91..d6245e655 100644
--- a/src/pages/Travel/Card/TravelBasicData.vue
+++ b/src/pages/Travel/Card/TravelBasicData.vue
@@ -73,9 +73,15 @@ const agenciesOptions = ref([]);
             <VnRow>
                 <VnInput v-model="data.m3" label="m3" />
                 <VnInput
-                    :label="t('globals.daysInForward')"
+                    :label="t('travel.basicData.daysInForward')"
                     v-model="data.daysInForward"
-                />
+                >
+                    <template #append>
+                        <QIcon name="info" class="cursor-info">
+                            <QTooltip>{{ t('raidDays') }}</QTooltip>
+                        </QIcon>
+                    </template>
+                </VnInput>
             </VnRow>
             <VnRow>
                 <QCheckbox
@@ -90,3 +96,10 @@ const agenciesOptions = ref([]);
         </template>
     </FormModel>
 </template>
+
+<i18n>
+es:
+    raidDays: Al rellenarlo, generamos una redada. Indica los días que un travel se moverá automáticamente en el tiempo
+en:
+    raidDays: When filling, a raid is generated. Enter the number of days the travel will automatically forward in time
+</i18n>
diff --git a/src/pages/Travel/Card/TravelDescriptor.vue b/src/pages/Travel/Card/TravelDescriptor.vue
index bda29903b..6025ad045 100644
--- a/src/pages/Travel/Card/TravelDescriptor.vue
+++ b/src/pages/Travel/Card/TravelDescriptor.vue
@@ -32,6 +32,7 @@ const filter = {
         'warehouseOutFk',
         'cargoSupplierFk',
         'agencyModeFk',
+        'daysInForward',
     ],
     include: [
         {
@@ -77,6 +78,22 @@ const setData = (entity) => (data.value = useCardDescription(entity.ref, entity.
             <VnLv :label="t('globals.landed')" :value="toDate(entity.landed)" />
             <VnLv :label="t('globals.totalEntries')" :value="entity.totalEntries" />
         </template>
+        <template #icons="{ entity }">
+            <QCardActions class="q-gutter-x-md">
+                <QIcon
+                    v-if="entity.daysInForward"
+                    name="vn:net"
+                    color="primary"
+                    size="xs"
+                >
+                    <QTooltip>
+                        {{
+                            t('globals.raid', { daysInForward: entity.daysInForward })
+                        }}</QTooltip
+                    >
+                </QIcon>
+            </QCardActions>
+        </template>
         <template #actions="{ entity }">
             <QCardActions>
                 <QBtn
diff --git a/src/pages/Travel/Card/TravelSummary.vue b/src/pages/Travel/Card/TravelSummary.vue
index 59059b0db..7dc267671 100644
--- a/src/pages/Travel/Card/TravelSummary.vue
+++ b/src/pages/Travel/Card/TravelSummary.vue
@@ -304,7 +304,7 @@ const getLink = (param) => `#/travel/${entityId.value}/${param}`;
                 <VnLv label="m³" :value="travel.m3" />
                 <VnLv :label="t('globals.totalEntries')" :value="travel.totalEntries" />
                 <VnLv
-                    :label="t('globals.daysInForward')"
+                    :label="t('travel.basicData.daysInForward')"
                     :value="travel?.daysInForward"
                 />
             </QCard>
diff --git a/src/pages/Travel/TravelList.vue b/src/pages/Travel/TravelList.vue
index a8c0e69cb..199746bb7 100644
--- a/src/pages/Travel/TravelList.vue
+++ b/src/pages/Travel/TravelList.vue
@@ -45,6 +45,10 @@ const redirectCreateEntryView = (travelData) => {
 };
 
 const columns = computed(() => [
+    {
+        name: 'status',
+        hidden: true,
+    },
     {
         align: 'left',
         name: 'id',
@@ -221,6 +225,17 @@ const columns = computed(() => [
         :is-editable="false"
         :use-model="true"
     >
+        <template #column-status="{ row }">
+            <div class="row">
+                <QIcon v-if="!!row.daysInForward" name="vn:net" color="primary">
+                    <QTooltip>
+                        {{
+                            t('globals.raid', { daysInForward: row.daysInForward })
+                        }}</QTooltip
+                    >
+                </QIcon>
+            </div>
+        </template>
         <template #column-shipped="{ row }">
             <QBadge
                 text-color="black"

From 994237260ed24fef644c6fdc5c4091a52607f89d Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Fri, 25 Oct 2024 06:48:09 +0200
Subject: [PATCH 087/150] refactor: changed name of emitted event

---
 src/pages/Ticket/Card/TicketSale.vue            | 2 +-
 src/pages/Ticket/Card/TicketSaleMoreActions.vue | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/pages/Ticket/Card/TicketSale.vue b/src/pages/Ticket/Card/TicketSale.vue
index 3e89e96bf..798d629c7 100644
--- a/src/pages/Ticket/Card/TicketSale.vue
+++ b/src/pages/Ticket/Card/TicketSale.vue
@@ -476,7 +476,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
                     :ticket-config="ticketConfig"
                     @get-mana="getMana()"
                     @update-discounts="updateDiscount"
-                    @re-calculated-price="resetChanges"
+                    @refresh-table="resetChanges"
                 />
                 <QBtn
                     color="primary"
diff --git a/src/pages/Ticket/Card/TicketSaleMoreActions.vue b/src/pages/Ticket/Card/TicketSaleMoreActions.vue
index 43ba39b91..221cf6fad 100644
--- a/src/pages/Ticket/Card/TicketSaleMoreActions.vue
+++ b/src/pages/Ticket/Card/TicketSaleMoreActions.vue
@@ -14,7 +14,7 @@ import { toDateFormat } from 'src/filters/date';
 import { useRole } from 'src/composables/useRole';
 import { useVnConfirm } from 'composables/useVnConfirm';
 
-const emit = defineEmits(['updateDiscounts', 'getMana', 'reCalculatedPrice']);
+const emit = defineEmits(['updateDiscounts', 'getMana', 'refreshTable']);
 
 const props = defineProps({
     disable: {
@@ -107,7 +107,7 @@ const calculateSalePrice = async () => {
 
     await axios.post(`Sales/recalculatePrice`, props.sales);
     notify(t('globals.dataSaved'), 'positive');
-    emit('reCalculatedPrice', props.sales);
+    emit('refreshTable', props.sales);
 };
 
 const changeMultipleDiscount = () => {

From e8d2a40dafbc8ccec24957715bf6cfea82e4bc25 Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Fri, 25 Oct 2024 08:11:31 +0200
Subject: [PATCH 088/150] fix: entryFilters

---
 src/pages/Entry/EntryLatestBuys.vue | 65 ++++++++++++++++++++++++++++-
 1 file changed, 64 insertions(+), 1 deletion(-)

diff --git a/src/pages/Entry/EntryLatestBuys.vue b/src/pages/Entry/EntryLatestBuys.vue
index 61c430b23..119808176 100644
--- a/src/pages/Entry/EntryLatestBuys.vue
+++ b/src/pages/Entry/EntryLatestBuys.vue
@@ -23,7 +23,6 @@ const columns = [
                 return {
                     id: row.id,
                     size: '50x50',
-                    width: '50px',
                 };
             },
         },
@@ -34,21 +33,37 @@ const columns = [
         label: t('entry.latestBuys.tableVisibleColumns.itemFk'),
         name: 'itemFk',
         isTitle: true,
+        columnFilter: {
+            component: 'number',
+            inWhere: true,
+        },
     },
     {
         align: 'left',
         label: t('entry.latestBuys.tableVisibleColumns.packing'),
         name: 'packing',
+        columnFilter: {
+            component: 'number',
+            inWhere: true,
+        },
     },
     {
         align: 'left',
         label: t('entry.latestBuys.tableVisibleColumns.grouping'),
         name: 'grouping',
+        columnFilter: {
+            component: 'number',
+            inWhere: true,
+        },
     },
     {
         align: 'left',
         label: t('entry.latestBuys.tableVisibleColumns.quantity'),
         name: 'quantity',
+        columnFilter: {
+            component: 'number',
+            inWhere: true,
+        },
     },
     {
         align: 'left',
@@ -59,6 +74,10 @@ const columns = [
         align: 'left',
         label: t('entry.latestBuys.tableVisibleColumns.size'),
         name: 'size',
+        columnFilter: {
+            component: 'number',
+            inWhere: true,
+        },
     },
     {
         align: 'left',
@@ -84,6 +103,10 @@ const columns = [
         align: 'left',
         label: t('entry.latestBuys.tableVisibleColumns.weightByPiece'),
         name: 'weightByPiece',
+        columnFilter: {
+            component: 'number',
+            inWhere: true,
+        },
     },
     {
         align: 'left',
@@ -99,26 +122,46 @@ const columns = [
         align: 'left',
         label: t('entry.latestBuys.tableVisibleColumns.entryFk'),
         name: 'entryFk',
+        columnFilter: {
+            component: 'number',
+            inWhere: true,
+        },
     },
     {
         align: 'left',
         label: t('entry.latestBuys.tableVisibleColumns.buyingValue'),
         name: 'buyingValue',
+        columnFilter: {
+            component: 'number',
+            inWhere: true,
+        },
     },
     {
         align: 'left',
         label: t('entry.latestBuys.tableVisibleColumns.freightValue'),
         name: 'freightValue',
+        columnFilter: {
+            component: 'number',
+            inWhere: true,
+        },
     },
     {
         align: 'left',
         label: t('entry.latestBuys.tableVisibleColumns.comissionValue'),
         name: 'comissionValue',
+        columnFilter: {
+            component: 'number',
+            inWhere: true,
+        },
     },
     {
         align: 'left',
         label: t('entry.latestBuys.tableVisibleColumns.packageValue'),
         name: 'packageValue',
+        columnFilter: {
+            component: 'number',
+            inWhere: true,
+        },
     },
     {
         align: 'left',
@@ -129,16 +172,28 @@ const columns = [
         align: 'left',
         label: t('entry.latestBuys.tableVisibleColumns.price2'),
         name: 'price2',
+        columnFilter: {
+            component: 'number',
+            inWhere: true,
+        },
     },
     {
         align: 'left',
         label: t('entry.latestBuys.tableVisibleColumns.price3'),
         name: 'price3',
+        columnFilter: {
+            component: 'number',
+            inWhere: true,
+        },
     },
     {
         align: 'left',
         label: t('entry.latestBuys.tableVisibleColumns.minPrice'),
         name: 'minPrice',
+        columnFilter: {
+            component: 'number',
+            inWhere: true,
+        },
     },
     {
         align: 'left',
@@ -149,11 +204,19 @@ const columns = [
         align: 'left',
         label: t('entry.latestBuys.tableVisibleColumns.weight'),
         name: 'weight',
+        columnFilter: {
+            component: 'number',
+            inWhere: true,
+        },
     },
     {
         align: 'left',
         label: t('entry.latestBuys.tableVisibleColumns.packagingFk'),
         name: 'packagingFk',
+        columnFilter: {
+            component: 'number',
+            inWhere: true,
+        },
     },
     {
         align: 'left',

From 1daa8be63597f770ae0d0a9a7588628622556c00 Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Fri, 25 Oct 2024 09:41:28 +0200
Subject: [PATCH 089/150] perf: optimize get packing type function

---
 .../components/CustomerSummaryTable.vue       | 21 +++++++++++--------
 1 file changed, 12 insertions(+), 9 deletions(-)

diff --git a/src/pages/Customer/components/CustomerSummaryTable.vue b/src/pages/Customer/components/CustomerSummaryTable.vue
index ff1474fb9..e9bb36be7 100644
--- a/src/pages/Customer/components/CustomerSummaryTable.vue
+++ b/src/pages/Customer/components/CustomerSummaryTable.vue
@@ -152,17 +152,20 @@ const setShippedColor = (date) => {
 };
 
 const getItemPackagingType = (ticketSales) => {
-    if (!ticketSales || ticketSales.length === 0) return '-';
+    if (!ticketSales?.length) return '-';
 
-    const packagingTypes = ticketSales
-        .map((sale) => sale.item?.itemPackingTypeFk)
-        .filter((type) => type !== undefined && type !== null);
+    const packagingTypes = ticketSales.reduce((types, sale) => {
+        const { itemPackingTypeFk } = sale.item;
+        if (
+            !types.includes(itemPackingTypeFk) &&
+            (itemPackingTypeFk === 'H' || itemPackingTypeFk === 'V')
+        ) {
+            types.push(itemPackingTypeFk);
+        }
+        return types;
+    }, []);
 
-    const uniquePackagingTypes = [...new Set(packagingTypes)];
-
-    return dashIfEmpty(
-        uniquePackagingTypes.length > 0 ? uniquePackagingTypes.join(', ') : '-'
-    );
+    return dashIfEmpty(packagingTypes.join(', ') || '-');
 };
 </script>
 

From 4c1e4aedd2099bd08526c2a51d7d9308627a01dc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Carlos=20Andr=C3=A9s?= <carlosap@verdnatura.es>
Date: Fri, 25 Oct 2024 10:03:09 +0200
Subject: [PATCH 090/150] feat: refs #8087 Traspasar redadas a travels

---
 src/pages/Entry/Card/EntryBasicData.vue  | 6 ++++++
 src/pages/Entry/Card/EntryDescriptor.vue | 8 ++++++++
 src/pages/Entry/EntryList.vue            | 2 +-
 src/pages/Travel/TravelList.vue          | 2 +-
 4 files changed, 16 insertions(+), 2 deletions(-)

diff --git a/src/pages/Entry/Card/EntryBasicData.vue b/src/pages/Entry/Card/EntryBasicData.vue
index d60ed8645..b81b1db22 100644
--- a/src/pages/Entry/Card/EntryBasicData.vue
+++ b/src/pages/Entry/Card/EntryBasicData.vue
@@ -168,6 +168,12 @@ const onFilterTravelSelected = (formData, id) => {
                     v-model="data.isExcludedFromAvailable"
                     :label="t('entry.basicData.excludedFromAvailable')"
                 />
+                <QCheckbox v-model="data.isRaid" :label="t('entry.basicData.raid')" />
+                <QCheckbox
+                    v-if="isAdministrative()"
+                    v-model="data.isBooked"
+                    :label="t('entry.basicData.booked')"
+                />
             </VnRow>
         </template>
     </FormModel>
diff --git a/src/pages/Entry/Card/EntryDescriptor.vue b/src/pages/Entry/Card/EntryDescriptor.vue
index bbfc73138..d66185aa9 100644
--- a/src/pages/Entry/Card/EntryDescriptor.vue
+++ b/src/pages/Entry/Card/EntryDescriptor.vue
@@ -30,6 +30,14 @@ const entryFilter = {
         {
             relation: 'travel',
             scope: {
+                fields: [
+                    'id',
+                    'landed',
+                    'shipped',
+                    'agencyModeFk',
+                    'warehouseOutFk',
+                    'daysInForward',
+                ],
                 include: [
                     {
                         relation: 'agency',
diff --git a/src/pages/Entry/EntryList.vue b/src/pages/Entry/EntryList.vue
index 518d72d82..e9179c239 100644
--- a/src/pages/Entry/EntryList.vue
+++ b/src/pages/Entry/EntryList.vue
@@ -43,7 +43,7 @@ const entryFilter = {
 const columns = computed(() => [
     {
         name: 'status',
-        hidden: true,
+        columnFilter: false,
     },
     {
         align: 'left',
diff --git a/src/pages/Travel/TravelList.vue b/src/pages/Travel/TravelList.vue
index 199746bb7..334640bff 100644
--- a/src/pages/Travel/TravelList.vue
+++ b/src/pages/Travel/TravelList.vue
@@ -47,7 +47,7 @@ const redirectCreateEntryView = (travelData) => {
 const columns = computed(() => [
     {
         name: 'status',
-        hidden: true,
+        columnFilter: false,
     },
     {
         align: 'left',

From 9e57c5e4527b8ffade7be3d0a02889d0c2550346 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Carlos=20Andr=C3=A9s?= <carlosap@verdnatura.es>
Date: Fri, 25 Oct 2024 10:13:09 +0200
Subject: [PATCH 091/150] feat: refs #8087 Traspasar redadas a travels

---
 src/pages/Entry/Card/EntryBasicData.vue | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/pages/Entry/Card/EntryBasicData.vue b/src/pages/Entry/Card/EntryBasicData.vue
index b81b1db22..3288616fb 100644
--- a/src/pages/Entry/Card/EntryBasicData.vue
+++ b/src/pages/Entry/Card/EntryBasicData.vue
@@ -168,7 +168,6 @@ const onFilterTravelSelected = (formData, id) => {
                     v-model="data.isExcludedFromAvailable"
                     :label="t('entry.basicData.excludedFromAvailable')"
                 />
-                <QCheckbox v-model="data.isRaid" :label="t('entry.basicData.raid')" />
                 <QCheckbox
                     v-if="isAdministrative()"
                     v-model="data.isBooked"

From ff559384aad8a7d59a9ebfcb7263ffd93cc40c60 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Fri, 25 Oct 2024 10:33:59 +0200
Subject: [PATCH 092/150] perf: refs #6943 #6943 merge command

---
 .../route/agency/agencyWorkCenter.spec.js     |  6 +++---
 .../worker/workerNotificationsManager.spec.js |  4 +++-
 test/cypress/support/commands.js              | 20 ++++++++-----------
 3 files changed, 14 insertions(+), 16 deletions(-)

diff --git a/test/cypress/integration/route/agency/agencyWorkCenter.spec.js b/test/cypress/integration/route/agency/agencyWorkCenter.spec.js
index 6a3cab664..311c0130c 100644
--- a/test/cypress/integration/route/agency/agencyWorkCenter.spec.js
+++ b/test/cypress/integration/route/agency/agencyWorkCenter.spec.js
@@ -11,16 +11,16 @@ describe('AgencyWorkCenter', () => {
         // create
         cy.get(createButton).click();
         cy.get(workCenterCombobox).type('workCenterOne{enter}');
-        cy.hasNotify('Data created');
+        cy.checkNotification('created');
 
         // expect error when duplicate
         cy.get(createButton).click();
         cy.get('[data-cy="FormModelPopup_save"]').click();
-        cy.hasNotify('This workCenter is already assigned to this agency');
+        cy.checkNotification('This workCenter is already assigned to this agency');
         cy.get('[data-cy="FormModelPopup_cancel"]').click();
 
         // delete
         cy.get('.q-item__section--side > .q-btn > .q-btn__content > .q-icon').click();
-        cy.hasNotify('WorkCenter removed successfully');
+        cy.checkNotification('WorkCenter removed successfully');
     });
 });
diff --git a/test/cypress/integration/worker/workerNotificationsManager.spec.js b/test/cypress/integration/worker/workerNotificationsManager.spec.js
index 367287a5a..f121b3894 100644
--- a/test/cypress/integration/worker/workerNotificationsManager.spec.js
+++ b/test/cypress/integration/worker/workerNotificationsManager.spec.js
@@ -17,7 +17,9 @@ describe('WorkerNotificationsManager', () => {
         cy.login('developer');
         cy.visit(`/#/worker/${salesPersonId}/notifications`);
         cy.get(firstAvailableNotification).click();
-        cy.hasNotify('The notification subscription of this worker cant be modified');
+        cy.checkNotification(
+            'The notification subscription of this worker cant be modified'
+        );
     });
 
     it('should active a notification that is yours', () => {
diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js
index dcfb54a3e..7d5a44f78 100755
--- a/test/cypress/support/commands.js
+++ b/test/cypress/support/commands.js
@@ -274,21 +274,13 @@ Cypress.Commands.add('openActions', (row) => {
     cy.get('tbody > tr').eq(row).find('.actions > .q-btn').click();
 });
 
-Cypress.Commands.add('checkNotification', (type) => {
-    const values = {
+Cypress.Commands.add('checkNotification', (tag) => {
+    const defaultTags = {
         created: 'Data created',
         updated: 'Data saved',
         deleted: 'Data deleted',
-    };
-    cy.get('.q-notification__message').should('have.text', values[type]);
-});
-
-Cypress.Commands.add('searchByLabel', (label, value) => {
-    cy.get(`[label="${label}"] > .q-field > .q-field__inner`).type(`${value}{enter}`);
-});
-
-Cypress.Commands.add('hasNotify', (text) => {
-    //last
+    }; //last
+    const text = defaultTags[tag] ?? tag;
     cy.get('.q-notification')
         .should('be.visible')
         .last()
@@ -297,3 +289,7 @@ Cypress.Commands.add('hasNotify', (text) => {
                 throw new Error(`Notification not found: "${text}"`);
         });
 });
+
+Cypress.Commands.add('searchByLabel', (label, value) => {
+    cy.get(`[label="${label}"] > .q-field > .q-field__inner`).type(`${value}{enter}`);
+});

From 02f682ac0b5d153c43339812b41fab0605c861c3 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Fri, 25 Oct 2024 10:53:19 +0200
Subject: [PATCH 093/150] perf: refs #6943 #6943 merge command

---
 test/cypress/integration/client/clientList.spec.js        | 2 +-
 .../integration/route/agency/agencyWorkCenter.spec.js     | 2 +-
 test/cypress/support/commands.js                          | 8 +-------
 3 files changed, 3 insertions(+), 9 deletions(-)

diff --git a/test/cypress/integration/client/clientList.spec.js b/test/cypress/integration/client/clientList.spec.js
index f150fc190..22bca15ac 100644
--- a/test/cypress/integration/client/clientList.spec.js
+++ b/test/cypress/integration/client/clientList.spec.js
@@ -28,7 +28,7 @@ describe('Client list', () => {
 
         cy.get('.q-mt-lg > .q-btn--standard').click();
 
-        cy.checkNotification('created');
+        cy.checkNotification('Data created');
         cy.url().should('include', '/summary');
     });
     it('Client list search client', () => {
diff --git a/test/cypress/integration/route/agency/agencyWorkCenter.spec.js b/test/cypress/integration/route/agency/agencyWorkCenter.spec.js
index 311c0130c..fdfcd4286 100644
--- a/test/cypress/integration/route/agency/agencyWorkCenter.spec.js
+++ b/test/cypress/integration/route/agency/agencyWorkCenter.spec.js
@@ -11,7 +11,7 @@ describe('AgencyWorkCenter', () => {
         // create
         cy.get(createButton).click();
         cy.get(workCenterCombobox).type('workCenterOne{enter}');
-        cy.checkNotification('created');
+        cy.checkNotification('Data created');
 
         // expect error when duplicate
         cy.get(createButton).click();
diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js
index 7d5a44f78..c9b1a748e 100755
--- a/test/cypress/support/commands.js
+++ b/test/cypress/support/commands.js
@@ -274,13 +274,7 @@ Cypress.Commands.add('openActions', (row) => {
     cy.get('tbody > tr').eq(row).find('.actions > .q-btn').click();
 });
 
-Cypress.Commands.add('checkNotification', (tag) => {
-    const defaultTags = {
-        created: 'Data created',
-        updated: 'Data saved',
-        deleted: 'Data deleted',
-    }; //last
-    const text = defaultTags[tag] ?? tag;
+Cypress.Commands.add('checkNotification', (text) => {
     cy.get('.q-notification')
         .should('be.visible')
         .last()

From bb51a9e6870af70d95a0b70c6e63e49c1633b749 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Fri, 25 Oct 2024 11:38:00 +0200
Subject: [PATCH 094/150] fix: refs #7943 use summary

---
 src/pages/Worker/Card/WorkerCard.vue    | 2 +-
 src/pages/Worker/Card/WorkerSummary.vue | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/pages/Worker/Card/WorkerCard.vue b/src/pages/Worker/Card/WorkerCard.vue
index 5f9fa0f8e..428731a5c 100644
--- a/src/pages/Worker/Card/WorkerCard.vue
+++ b/src/pages/Worker/Card/WorkerCard.vue
@@ -6,7 +6,7 @@ import WorkerFilter from '../WorkerFilter.vue';
 <template>
     <VnCard
         data-key="Worker"
-        base-url="Workers"
+        custom-url="Workers/summary"
         :descriptor="WorkerDescriptor"
         :filter-panel="WorkerFilter"
         search-data-key="WorkerList"
diff --git a/src/pages/Worker/Card/WorkerSummary.vue b/src/pages/Worker/Card/WorkerSummary.vue
index ed34e771d..4a78afb52 100644
--- a/src/pages/Worker/Card/WorkerSummary.vue
+++ b/src/pages/Worker/Card/WorkerSummary.vue
@@ -45,7 +45,7 @@ onBeforeMount(async () => {
         ref="summary"
         :url="`Workers/summary`"
         :filter="{ where: { id: entityId } }"
-        data-key="WorkerSummary"
+        data-key="Worker"
     >
         <template #header="{ entity }">
             <div>{{ entity.id }} - {{ entity.firstName }} {{ entity.lastName }}</div>

From cdb73f7a17588b70d628af92d349ece882aa336b Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Fri, 25 Oct 2024 11:50:20 +0200
Subject: [PATCH 095/150] fix: refs #7943 use correct data-key

---
 src/pages/Worker/WorkerList.vue | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/pages/Worker/WorkerList.vue b/src/pages/Worker/WorkerList.vue
index 7a3f760bc..022cecdc6 100644
--- a/src/pages/Worker/WorkerList.vue
+++ b/src/pages/Worker/WorkerList.vue
@@ -169,7 +169,7 @@ async function autofillBic(worker) {
 </script>
 <template>
     <VnSearchbar
-        data-key="Worker"
+        data-key="WorkerList"
         :label="t('Search worker')"
         :info="t('You can search by worker id or name')"
     />
@@ -191,13 +191,13 @@ async function autofillBic(worker) {
     />
     <RightMenu>
         <template #right-panel>
-            <WorkerFilter data-key="Worker" />
+            <WorkerFilter data-key="WorkerList" />
         </template>
     </RightMenu>
     <VnTable
         v-if="defaultPayMethod"
         ref="tableRef"
-        data-key="Worker"
+        data-key="WorkerList"
         url="Workers/filter"
         :create="{
             urlCreate: 'Workers/new',

From 5b24aa457074fbf0f153dadfe49c4b15c805e0cb Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Fri, 25 Oct 2024 12:58:23 +0200
Subject: [PATCH 096/150] fix: performance addEventListeners

---
 src/boot/mainShortcutMixin.js       | 38 -----------------------------
 src/boot/quasar.js                  |  2 --
 src/components/VnTable/VnColumn.vue |  2 +-
 src/components/VnTable/VnFilter.vue |  2 +-
 src/layouts/MainLayout.vue          | 37 ++++++++++++++++++++++++++++
 5 files changed, 39 insertions(+), 42 deletions(-)
 delete mode 100644 src/boot/mainShortcutMixin.js

diff --git a/src/boot/mainShortcutMixin.js b/src/boot/mainShortcutMixin.js
deleted file mode 100644
index 8e5f147db..000000000
--- a/src/boot/mainShortcutMixin.js
+++ /dev/null
@@ -1,38 +0,0 @@
-import routes from 'src/router/modules';
-import { useRouter } from 'vue-router';
-
-let isNotified = false;
-
-export default {
-    created: function () {
-        const router = useRouter();
-        const keyBindingMap = routes
-            .filter((route) => route.meta.keyBinding)
-            .reduce((map, route) => {
-                map['Key' + route.meta.keyBinding.toUpperCase()] = route.path;
-                return map;
-            }, {});
-
-        const handleKeyDown = (event) => {
-            const { ctrlKey, altKey, code } = event;
-
-            if (ctrlKey && altKey && keyBindingMap[code] && !isNotified) {
-                event.preventDefault();
-                router.push(keyBindingMap[code]);
-                isNotified = true;
-            }
-        };
-
-        const handleKeyUp = (event) => {
-            const { ctrlKey, altKey } = event;
-
-            // Resetea la bandera cuando se sueltan las teclas ctrl o alt
-            if (!ctrlKey || !altKey) {
-                isNotified = false;
-            }
-        };
-
-        window.addEventListener('keydown', handleKeyDown);
-        window.addEventListener('keyup', handleKeyUp);
-    },
-};
diff --git a/src/boot/quasar.js b/src/boot/quasar.js
index 5db6edd24..f39d1f0be 100644
--- a/src/boot/quasar.js
+++ b/src/boot/quasar.js
@@ -1,13 +1,11 @@
 import { boot } from 'quasar/wrappers';
 import qFormMixin from './qformMixin';
-import mainShortcutMixin from './mainShortcutMixin';
 import keyShortcut from './keyShortcut';
 import useNotify from 'src/composables/useNotify.js';
 const { notify } = useNotify();
 
 export default boot(({ app }) => {
     app.mixin(qFormMixin);
-    app.mixin(mainShortcutMixin);
     app.directive('shortcut', keyShortcut);
     app.config.errorHandler = function (err) {
         console.error(err);
diff --git a/src/components/VnTable/VnColumn.vue b/src/components/VnTable/VnColumn.vue
index ed34e9eee..9e9bfad69 100644
--- a/src/components/VnTable/VnColumn.vue
+++ b/src/components/VnTable/VnColumn.vue
@@ -1,5 +1,5 @@
 <script setup>
-import { markRaw, computed, defineModel } from 'vue';
+import { markRaw, computed } from 'vue';
 import { QIcon, QCheckbox } from 'quasar';
 import { dashIfEmpty } from 'src/filters';
 
diff --git a/src/components/VnTable/VnFilter.vue b/src/components/VnTable/VnFilter.vue
index b17fd4407..9da0b26a4 100644
--- a/src/components/VnTable/VnFilter.vue
+++ b/src/components/VnTable/VnFilter.vue
@@ -1,5 +1,5 @@
 <script setup>
-import { markRaw, computed, defineModel } from 'vue';
+import { markRaw, computed } from 'vue';
 import { QCheckbox } from 'quasar';
 import { useArrayData } from 'composables/useArrayData';
 
diff --git a/src/layouts/MainLayout.vue b/src/layouts/MainLayout.vue
index 51cd20071..754b084fc 100644
--- a/src/layouts/MainLayout.vue
+++ b/src/layouts/MainLayout.vue
@@ -1,7 +1,44 @@
 <script setup>
 import { useQuasar } from 'quasar';
 import Navbar from 'src/components/NavBar.vue';
+import { useRouter } from 'vue-router';
+import routes from 'src/router/modules';
+import { onMounted } from 'vue';
+
 const quasar = useQuasar();
+
+onMounted(() => {
+    let isNotified = false;
+
+    const router = useRouter();
+    const keyBindingMap = routes
+        .filter((route) => route.meta.keyBinding)
+        .reduce((map, route) => {
+            map['Key' + route.meta.keyBinding.toUpperCase()] = route.path;
+            return map;
+        }, {});
+
+    const handleKeyDown = (event) => {
+        const { ctrlKey, altKey, code } = event;
+
+        if (ctrlKey && altKey && keyBindingMap[code] && !isNotified) {
+            event.preventDefault();
+            router.push(keyBindingMap[code]);
+            isNotified = true;
+        }
+    };
+
+    const handleKeyUp = (event) => {
+        const { ctrlKey, altKey } = event;
+
+        if (!ctrlKey || !altKey) {
+            isNotified = false;
+        }
+    };
+
+    window.addEventListener('keydown', handleKeyDown);
+    window.addEventListener('keyup', handleKeyUp);
+});
 </script>
 
 <template>

From 8edba36a7401bad871b994d2e6f2a870c8ed92d0 Mon Sep 17 00:00:00 2001
From: guillermo <guillermo@verdnatura.es>
Date: Fri, 25 Oct 2024 13:46:36 +0200
Subject: [PATCH 097/150] feat: refs #7006 itemType basic data new inputs

---
 src/pages/ItemType/Card/ItemTypeBasicData.vue | 36 ++++++++++++++++++-
 src/pages/ItemType/locale/en.yml              |  3 ++
 src/pages/ItemType/locale/es.yml              |  3 ++
 3 files changed, 41 insertions(+), 1 deletion(-)

diff --git a/src/pages/ItemType/Card/ItemTypeBasicData.vue b/src/pages/ItemType/Card/ItemTypeBasicData.vue
index f4013e126..e447f565b 100644
--- a/src/pages/ItemType/Card/ItemTypeBasicData.vue
+++ b/src/pages/ItemType/Card/ItemTypeBasicData.vue
@@ -8,12 +8,14 @@ import FormModel from 'components/FormModel.vue';
 import VnRow from 'components/ui/VnRow.vue';
 import VnInput from 'src/components/common/VnInput.vue';
 import VnSelect from 'src/components/common/VnSelect.vue';
+import VnAvatar from 'src/components/ui/VnAvatar.vue';
 
 const route = useRoute();
 const { t } = useI18n();
 
 const categoriesOptions = ref([]);
 const temperaturesOptions = ref([]);
+const itemPackingTypesOptions = ref([]);
 </script>
 <template>
     <FetchData
@@ -28,6 +30,16 @@ const temperaturesOptions = ref([]);
         :filter="{ order: 'name ASC', fields: ['code', 'name'] }"
         auto-load
     />
+    <FetchData
+        url="ItemPackingTypes"
+        @on-fetch="(data) => (itemPackingTypesOptions = data)"
+        :filter="{
+            where: { isActive: true },
+            order: 'description ASC',
+            fields: ['code', 'description'],
+        }"
+        auto-load
+    />
     <FormModel
         :url="`ItemTypes/${route.params.id}`"
         :url-update="`ItemTypes/${route.params.id}`"
@@ -50,7 +62,15 @@ const temperaturesOptions = ref([]);
                     option-label="nickname"
                     option-value="id"
                     hide-selected
-                    ><template #option="scope">
+                >
+                    <template #prepend>
+                        <VnAvatar
+                            :worker-id="data.workerFk"
+                            color="primary"
+                            :title="title"
+                        />
+                    </template>
+                    <template #option="scope">
                         <QItem v-bind="scope.itemProps">
                             <QItemSection>
                                 <QItemLabel>{{ scope.opt?.name }}</QItemLabel>
@@ -82,6 +102,20 @@ const temperaturesOptions = ref([]);
                 />
                 <VnInput v-model="data.life" :label="t('shared.life')" />
             </VnRow>
+            <VnRow>
+                <VnSelect
+                    v-model="data.itemPackingTypeFk"
+                    :label="t('shared.itemPackingType')"
+                    :options="itemPackingTypesOptions"
+                    option-value="code"
+                    option-label="description"
+                    hide-selected
+                />
+                <VnInput v-model="data.maxRefs" :label="t('shared.maxRefs')" />
+            </VnRow>
+            <VnRow>
+                <QCheckbox v-model="data.isFragile" :label="t('shared.fragile')" />
+            </VnRow>
         </template>
     </FormModel>
 </template>
diff --git a/src/pages/ItemType/locale/en.yml b/src/pages/ItemType/locale/en.yml
index 4b203bd68..575d5e402 100644
--- a/src/pages/ItemType/locale/en.yml
+++ b/src/pages/ItemType/locale/en.yml
@@ -5,6 +5,9 @@ shared:
     category: Category
     temperature: Temperature
     life: Life
+    itemPackingType: Item packing type
+    maxRefs: Maximum references
+    fragile: Fragile
 summary:
     id: id
     life: Life
diff --git a/src/pages/ItemType/locale/es.yml b/src/pages/ItemType/locale/es.yml
index 43699c332..09ee5a1f7 100644
--- a/src/pages/ItemType/locale/es.yml
+++ b/src/pages/ItemType/locale/es.yml
@@ -5,6 +5,9 @@ shared:
     category: Reino
     temperature: Temperatura
     life: Vida
+    itemPackingType: Tipo de embalaje
+    maxRefs: Referencias máximas
+    fragile: Frágil
 summary:
     id: id
     code: Código

From 494fc66c06377838c2d23d6f6cf945c91c753f67 Mon Sep 17 00:00:00 2001
From: guillermo <guillermo@verdnatura.es>
Date: Fri, 25 Oct 2024 14:14:51 +0200
Subject: [PATCH 098/150] feat: refs #7006 itemType basic data new inputs

---
 src/pages/Item/ItemTypeList.vue               | 15 ++++-----------
 src/pages/ItemType/Card/ItemTypeBasicData.vue |  1 -
 2 files changed, 4 insertions(+), 12 deletions(-)

diff --git a/src/pages/Item/ItemTypeList.vue b/src/pages/Item/ItemTypeList.vue
index 2c1153016..13dd56a42 100644
--- a/src/pages/Item/ItemTypeList.vue
+++ b/src/pages/Item/ItemTypeList.vue
@@ -7,7 +7,6 @@ import FetchData from 'components/FetchData.vue';
 
 const { t } = useI18n();
 const tableRef = ref();
-const workerOptions = ref([]);
 const ItemCategoriesOptions = ref([]);
 
 const columns = computed(() => [
@@ -40,12 +39,12 @@ const columns = computed(() => [
         create: true,
         component: 'select',
         attrs: {
-            options: workerOptions.value,
-            optionLabel: 'firstName',
+            url: 'Workers/search',
+            optionLabel: 'nickname',
             optionValue: 'id',
         },
-        cardVisible: false,
-        visible: false,
+        cardVisible: true,
+        visible: true,
     },
     {
         align: 'left',
@@ -78,12 +77,6 @@ const columns = computed(() => [
 </script>
 
 <template>
-    <FetchData
-        url="Workers"
-        :filter="{ fields: ['id', 'firstName'], order: ['firstName ASC'] }"
-        @on-fetch="(data) => (workerOptions = data)"
-        auto-load
-    />
     <FetchData
         url="ItemCategories"
         :filter="{ fields: ['id', 'name'], order: ['name ASC'] }"
diff --git a/src/pages/ItemType/Card/ItemTypeBasicData.vue b/src/pages/ItemType/Card/ItemTypeBasicData.vue
index e447f565b..1a4a7c9f3 100644
--- a/src/pages/ItemType/Card/ItemTypeBasicData.vue
+++ b/src/pages/ItemType/Card/ItemTypeBasicData.vue
@@ -58,7 +58,6 @@ const itemPackingTypesOptions = ref([]);
                     :label="t('shared.worker')"
                     sort-by="nickname ASC"
                     :fields="['id', 'nickname']"
-                    :params="{ departmentCodes: ['shopping'] }"
                     option-label="nickname"
                     option-value="id"
                     hide-selected

From 1da86900e653a072bbe898f2d75997a5851c59be Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Sun, 27 Oct 2024 13:43:38 +0100
Subject: [PATCH 099/150] fix: refs #7524 e2e & worker module

---
 src/components/ui/VnRow.vue                   |  3 --
 src/composables/useAdvancedSummary.js         | 11 ++++++
 src/pages/Ticket/Card/TicketExpedition.vue    |  1 +
 src/pages/Ticket/TicketAdvance.vue            |  1 -
 src/pages/Worker/Card/WorkerBasicData.vue     | 36 +++++++++----------
 src/pages/Worker/Card/WorkerLocker.vue        |  3 +-
 src/pages/Worker/Card/WorkerSummary.vue       | 32 ++++++++---------
 src/router/modules/worker.js                  |  7 ++++
 .../ticket/ticketExpedition.spec.js           | 12 +++----
 .../integration/worker/workerList.spec.js     |  2 +-
 .../integration/worker/workerLocker.spec.js   | 11 +++---
 test/cypress/support/commands.js              |  2 +-
 12 files changed, 63 insertions(+), 58 deletions(-)
 create mode 100644 src/composables/useAdvancedSummary.js

diff --git a/src/components/ui/VnRow.vue b/src/components/ui/VnRow.vue
index 16bcfab7d..40dabf610 100644
--- a/src/components/ui/VnRow.vue
+++ b/src/components/ui/VnRow.vue
@@ -1,6 +1,3 @@
-<script setup>
-defineProps({ wrap: { type: Boolean, default: false } });
-</script>
 <template>
     <div class="vn-row q-gutter-md q-mb-md">
         <slot />
diff --git a/src/composables/useAdvancedSummary.js b/src/composables/useAdvancedSummary.js
new file mode 100644
index 000000000..98b998d2a
--- /dev/null
+++ b/src/composables/useAdvancedSummary.js
@@ -0,0 +1,11 @@
+import axios from 'axios';
+import { useRole } from './useRole';
+
+export async function useAdvancedSummary(model, id, roles = ['hr']) {
+    if (useRole().hasAny(roles)) {
+        const { data } = await axios.get(`${model}/advancedSummary`, {
+            params: { filter: { where: { id } } },
+        });
+        return Array.isArray(data) ? data[0] : data;
+    }
+}
diff --git a/src/pages/Ticket/Card/TicketExpedition.vue b/src/pages/Ticket/Card/TicketExpedition.vue
index 8041ad069..d682d0408 100644
--- a/src/pages/Ticket/Card/TicketExpedition.vue
+++ b/src/pages/Ticket/Card/TicketExpedition.vue
@@ -211,6 +211,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
         <template #st-actions>
             <QBtnGroup push class="q-gutter-x-sm" flat>
                 <VnBtnSelect
+                    data-cy="change-state"
                     :disable="!hasSelectedRows"
                     color="primary"
                     :label="t('globals.changeState')"
diff --git a/src/pages/Ticket/TicketAdvance.vue b/src/pages/Ticket/TicketAdvance.vue
index 7db2b54b3..bdd980c07 100644
--- a/src/pages/Ticket/TicketAdvance.vue
+++ b/src/pages/Ticket/TicketAdvance.vue
@@ -4,7 +4,6 @@ import { useI18n } from 'vue-i18n';
 import FetchData from 'components/FetchData.vue';
 import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
 import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
-import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
 import VnProgress from 'src/components/common/VnProgressModal.vue';
 import RightMenu from 'src/components/common/RightMenu.vue';
 import TicketAdvanceFilter from './TicketAdvanceFilter.vue';
diff --git a/src/pages/Worker/Card/WorkerBasicData.vue b/src/pages/Worker/Card/WorkerBasicData.vue
index d131fea3e..6a13e3f39 100644
--- a/src/pages/Worker/Card/WorkerBasicData.vue
+++ b/src/pages/Worker/Card/WorkerBasicData.vue
@@ -1,5 +1,5 @@
 <script setup>
-import { ref } from 'vue';
+import { ref, onBeforeMount } from 'vue';
 import { useRoute } from 'vue-router';
 import { useI18n } from 'vue-i18n';
 import VnInputDate from 'src/components/common/VnInputDate.vue';
@@ -8,32 +8,22 @@ import FormModel from 'src/components/FormModel.vue';
 import VnRow from 'components/ui/VnRow.vue';
 import VnInput from 'src/components/common/VnInput.vue';
 import VnSelect from 'src/components/common/VnSelect.vue';
+import { useAdvancedSummary } from 'src/composables/useAdvancedSummary';
 
-const route = useRoute();
 const { t } = useI18n();
-
 const educationLevels = ref([]);
 const countries = ref([]);
 const maritalStatus = [
     { code: 'M', name: t('Married') },
     { code: 'S', name: t('Single') },
 ];
+const advancedSummary = ref({});
 
-const workerFilter = {
-    include: [
-        {
-            relation: 'user',
-            scope: {
-                fields: ['name', 'emailVerified'],
-                include: { relation: 'emailUser', scope: { fields: ['email'] } },
-            },
-        },
-        { relation: 'sip', scope: { fields: ['extension', 'secret'] } },
-        { relation: 'department', scope: { include: { relation: 'department' } } },
-    ],
-};
+onBeforeMount(async () => {
+    advancedSummary.value =
+        (await useAdvancedSummary('Workers', +useRoute().params.id)) ?? {};
+});
 </script>
-
 <template>
     <FetchData
         :filter="{ fields: ['id', 'name'], order: 'name ASC' }"
@@ -48,10 +38,16 @@ const workerFilter = {
         auto-load
     />
     <FormModel
-        :filter="workerFilter"
-        :url="`Workers/${route.params.id}`"
+        :filter="{ where: { id: +$route.params.id } }"
+        url="Workers/summary"
+        :url-update="`Workers/${$route.params.id}`"
         auto-load
         model="Worker"
+        @on-fetch="
+            async (data) => {
+                Object.assign(data, advancedSummary);
+            }
+        "
     >
         <template #form="{ data }">
             <VnRow>
@@ -134,7 +130,7 @@ const workerFilter = {
                 <VnInput v-model="data.fi" :label="t('fi')" />
                 <VnInputDate :label="t('birth')" v-model="data.birth" />
             </VnRow>
-            <VnRow>
+            <VnRow wrap>
                 <QCheckbox
                     size="sm"
                     :label="t('isFreelance')"
diff --git a/src/pages/Worker/Card/WorkerLocker.vue b/src/pages/Worker/Card/WorkerLocker.vue
index 4a19e472c..015bced35 100644
--- a/src/pages/Worker/Card/WorkerLocker.vue
+++ b/src/pages/Worker/Card/WorkerLocker.vue
@@ -18,7 +18,7 @@ const { store } = useArrayData('Worker');
 const entityId = computed(() => useRoute().params.id);
 const filter = computed(() => ({
     where: {
-        gender: store.data?.sex,
+        gender: store.data?.[0]?.sex,
         or: [{ workerFk: null }, { workerFk: entityId.value }],
     },
 }));
@@ -51,6 +51,7 @@ const init = async (data) => {
     >
         <template #form="{ data }">
             <VnSelect
+                data-cy="locker"
                 :label="t('Locker')"
                 v-model="data.id"
                 :options="lockers"
diff --git a/src/pages/Worker/Card/WorkerSummary.vue b/src/pages/Worker/Card/WorkerSummary.vue
index 4a78afb52..0a0694fdf 100644
--- a/src/pages/Worker/Card/WorkerSummary.vue
+++ b/src/pages/Worker/Card/WorkerSummary.vue
@@ -2,7 +2,6 @@
 import { ref, onBeforeMount, computed } from 'vue';
 import { useRoute } from 'vue-router';
 import { useI18n } from 'vue-i18n';
-import axios from 'axios';
 import { dashIfEmpty, toDate } from 'src/filters';
 import VnLv from 'src/components/ui/VnLv.vue';
 import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue';
@@ -11,7 +10,7 @@ import VnUserLink from 'src/components/ui/VnUserLink.vue';
 import VnTitle from 'src/components/common/VnTitle.vue';
 import RoleDescriptorProxy from 'src/pages/Account/Role/Card/RoleDescriptorProxy.vue';
 import DepartmentDescriptorProxy from 'src/pages/Department/Card/DepartmentDescriptorProxy.vue';
-import { useRole } from 'src/composables/useRole';
+import { useAdvancedSummary } from 'src/composables/useAdvancedSummary';
 
 const route = useRoute();
 const { t } = useI18n();
@@ -25,18 +24,11 @@ const $props = defineProps({
 
 const entityId = computed(() => $props.id || route.params.id);
 const basicDataUrl = ref(null);
-const isHr = computed(() => useRole().hasAny(['hr']));
 const advancedSummary = ref();
 
 onBeforeMount(async () => {
-    if (isHr.value) {
-        advancedSummary.value = (
-            await axios.get('Workers/advancedSummary', {
-                params: { filter: { where: { id: entityId.value } } },
-            })
-        ).data[0];
-        basicDataUrl.value = `#/worker/${entityId.value}/basic-data`;
-    }
+    advancedSummary.value = await useAdvancedSummary('Workers', entityId.value);
+    basicDataUrl.value = `#/worker/${entityId.value}/basic-data`;
 });
 </script>
 
@@ -101,21 +93,27 @@ onBeforeMount(async () => {
                     :label="t('worker.summary.seniority')"
                     :value="toDate(worker.seniority)"
                 />
-                <VnLv :label="t('worker.summary.fi')" :value="worker.fi" />
-                <VnLv :label="t('worker.summary.birth')" :value="toDate(worker.birth)" />
+                <VnLv :label="t('worker.summary.fi')" :value="advancedSummary.fi" />
+                <VnLv
+                    :label="t('worker.summary.birth')"
+                    :value="toDate(advancedSummary.birth)"
+                />
                 <VnLv
                     :label="t('worker.summary.isFreelance')"
-                    :value="worker.isFreelance"
+                    :value="advancedSummary.isFreelance"
                 />
                 <VnLv
                     :label="t('worker.summary.isSsDiscounted')"
-                    :value="worker.isSsDiscounted"
+                    :value="advancedSummary.isSsDiscounted"
                 />
                 <VnLv
                     :label="t('worker.summary.hasMachineryAuthorized')"
-                    :value="worker.hasMachineryAuthorized"
+                    :value="advancedSummary.hasMachineryAuthorized"
+                />
+                <VnLv
+                    :label="t('worker.summary.isDisable')"
+                    :value="advancedSummary.isDisable"
                 />
-                <VnLv :label="t('worker.summary.isDisable')" :value="worker.isDisable" />
             </QCard>
             <QCard class="vn-one">
                 <VnTitle :text="t('worker.summary.userData')" />
diff --git a/src/router/modules/worker.js b/src/router/modules/worker.js
index c2a9e668f..d1feff23d 100644
--- a/src/router/modules/worker.js
+++ b/src/router/modules/worker.js
@@ -87,6 +87,13 @@ export default {
                     meta: {
                         title: 'basicData',
                         icon: 'vn:settings',
+                        acls: [
+                            {
+                                model: 'Worker',
+                                props: 'updateAttributes',
+                                accessType: 'WRITE',
+                            },
+                        ],
                     },
                     component: () => import('src/pages/Worker/Card/WorkerBasicData.vue'),
                 },
diff --git a/test/cypress/integration/ticket/ticketExpedition.spec.js b/test/cypress/integration/ticket/ticketExpedition.spec.js
index 5eb2c1a2a..d4afd401f 100644
--- a/test/cypress/integration/ticket/ticketExpedition.spec.js
+++ b/test/cypress/integration/ticket/ticketExpedition.spec.js
@@ -11,15 +11,13 @@ describe('Ticket expedtion', () => {
 
     it('should change the state', () => {
         cy.visit('#/ticket/1/expedition');
-        cy.intercept('GET', /\/api\/Expeditions\/filter/).as('expeditions');
-        cy.intercept('POST', /\/api\/Expeditions\/crud/).as('crud');
-
-        cy.wait('@expeditions');
+        cy.intercept('GET', /\/api\/Expeditions\/filter/).as('show');
+        cy.intercept('POST', /\/api\/ExpeditionStates\/addExpeditionState/).as('add');
 
+        cy.wait('@show');
         cy.selectRows([1, 2]);
-        cy.get('#subToolbar [aria-controls]:nth-child(1)').click();
-        cy.get('.q-menu .q-item').contains('Perdida').click();
-        cy.wait('@crud');
+        cy.selectOption('[data-cy="change-state"]', 'Perdida');
+        cy.wait('@add');
 
         cy.get(`${tableContent} tr:nth-child(-n+2) ${stateTd}`).each(($el) => {
             cy.wrap($el).contains('Perdida');
diff --git a/test/cypress/integration/worker/workerList.spec.js b/test/cypress/integration/worker/workerList.spec.js
index 8a8bea443..c1c37fd32 100644
--- a/test/cypress/integration/worker/workerList.spec.js
+++ b/test/cypress/integration/worker/workerList.spec.js
@@ -11,7 +11,7 @@ describe('WorkerList', () => {
     it('should open the worker summary', () => {
         cy.get(inputName).type('jessica{enter}');
         cy.get(searchBtn).click();
-        cy.intercept('GET', /\/api\/Workers\/\d+/).as('worker');
+        cy.intercept('GET', /\/api\/Workers\/summary+/).as('worker');
         cy.wait('@worker').then(() =>
             cy.get(descriptorTitle).should('include.text', 'Jessica')
         );
diff --git a/test/cypress/integration/worker/workerLocker.spec.js b/test/cypress/integration/worker/workerLocker.spec.js
index 8a169dfb2..c222414fd 100644
--- a/test/cypress/integration/worker/workerLocker.spec.js
+++ b/test/cypress/integration/worker/workerLocker.spec.js
@@ -1,8 +1,7 @@
 describe('WorkerLocker', () => {
     const productionId = 49;
-    const lockerCode = '2F';
-    const input = '.q-card input';
-    const thirdOpt = '[role="listbox"] .q-item:nth-child(1)';
+    const lockerCode = '4F';
+    const lockerSelect = '[data-cy="locker"]';
     beforeEach(() => {
         cy.viewport(1280, 720);
         cy.login('productionBoss');
@@ -10,10 +9,8 @@ describe('WorkerLocker', () => {
     });
 
     it('should allocates a locker', () => {
-        cy.get(input).click();
-        cy.waitForElement('[role="listbox"]');
-        cy.get(thirdOpt).click();
+        cy.selectOption(lockerSelect, lockerCode);
         cy.saveCard();
-        cy.get(input).invoke('val').should('eq', lockerCode);
+        cy.get(lockerSelect).invoke('val').should('eq', lockerCode);
     });
 });
diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js
index c9b1a748e..76bdefd27 100755
--- a/test/cypress/support/commands.js
+++ b/test/cypress/support/commands.js
@@ -82,7 +82,7 @@ Cypress.Commands.add('getValue', (selector) => {
 // Fill Inputs
 Cypress.Commands.add('selectOption', (selector, option) => {
     cy.waitForElement(selector);
-    cy.get(selector).find('.q-select__dropdown-icon').click();
+    cy.get(selector).click();
     cy.get('.q-menu .q-item').contains(option).click();
 });
 

From 0cc0e82aaaba838cbcd6c7ced6d52347af7438d0 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Sun, 27 Oct 2024 14:48:44 +0100
Subject: [PATCH 100/150] feat: refs #8078 add shortcut multi selection

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

diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index c1680bf13..74a96e0e3 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -331,6 +331,16 @@ function handleScroll() {
     const isAtBottom = Math.abs(scrollHeight - scrollTop - clientHeight) <= 40;
     if (isAtBottom) CrudModelRef.value.vnPaginateRef.paginate();
 }
+
+function handleSelection({ evt, added, rows: selectedRows }, rows) {
+    if (evt?.shiftKey && added) {
+        const rowIndex = selectedRows[0].$index;
+        for (const row of rows) {
+            if (row.$index > rowIndex) break;
+            selected.value.push(row);
+        }
+    }
+}
 </script>
 <template>
     <QDrawer
@@ -431,6 +441,7 @@ function handleScroll() {
                 @virtual-scroll="handleScroll"
                 @row-click="(_, row) => rowClickFunction && rowClickFunction(row)"
                 @update:selected="emit('update:selected', $event)"
+                @selection="(details) => handleSelection(details, rows)"
             >
                 <template #top-left v-if="!$props.withoutHeader">
                     <slot name="top-left"></slot>

From ca6547e174b12865105cd819ccb3f1ccedf47803 Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Mon, 28 Oct 2024 07:48:11 +0100
Subject: [PATCH 101/150] feat: refs #7970 notify changes

---
 src/components/ui/VnConfirm.vue      | 23 ++++++---
 src/pages/Ticket/Card/TicketSale.vue | 76 ++++++++++++++++++++++++++--
 2 files changed, 89 insertions(+), 10 deletions(-)

diff --git a/src/components/ui/VnConfirm.vue b/src/components/ui/VnConfirm.vue
index 4fa374b62..0fbc55cfb 100644
--- a/src/components/ui/VnConfirm.vue
+++ b/src/components/ui/VnConfirm.vue
@@ -30,10 +30,10 @@ const props = defineProps({
     },
 });
 
-defineEmits(['confirm', ...useDialogPluginComponent.emits]);
-defineExpose({ show: () => dialogRef.value.show(), hide: () => dialogRef.value.hide() });
+defineEmits([...useDialogPluginComponent.emits]);
 
-const { dialogRef, onDialogOK } = useDialogPluginComponent();
+const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
+    useDialogPluginComponent();
 
 const title = props.title || t('Confirm');
 const message =
@@ -53,9 +53,13 @@ async function confirm() {
     }
     onDialogOK(props.data);
 }
+
+function cancel() {
+    onDialogCancel();
+}
 </script>
 <template>
-    <QDialog ref="dialogRef">
+    <QDialog ref="dialogRef" @hide="onDialogHide">
         <QCard class="q-pa-sm">
             <QCardSection class="row items-center q-pb-none">
                 <QAvatar
@@ -67,7 +71,14 @@ async function confirm() {
                 />
                 <span class="text-h6">{{ title }}</span>
                 <QSpace />
-                <QBtn icon="close" :disable="isLoading" flat round dense v-close-popup />
+                <QBtn
+                    icon="close"
+                    :disable="isLoading"
+                    flat
+                    round
+                    dense
+                    @click="cancel()"
+                />
             </QCardSection>
             <QCardSection class="q-pb-none">
                 <span v-if="message !== false" v-html="message" />
@@ -81,7 +92,7 @@ async function confirm() {
                     color="primary"
                     :disable="isLoading"
                     flat
-                    v-close-popup
+                    @click="cancel()"
                 />
                 <QBtn
                     :label="t('globals.confirm')"
diff --git a/src/pages/Ticket/Card/TicketSale.vue b/src/pages/Ticket/Card/TicketSale.vue
index c786c67e3..3426db880 100644
--- a/src/pages/Ticket/Card/TicketSale.vue
+++ b/src/pages/Ticket/Card/TicketSale.vue
@@ -2,6 +2,7 @@
 import { onMounted, ref, computed, onUnmounted, watch } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useRouter, useRoute } from 'vue-router';
+import { useQuasar } from 'quasar';
 
 import FetchData from 'components/FetchData.vue';
 import FetchedTags from 'components/ui/FetchedTags.vue';
@@ -23,6 +24,7 @@ import useNotify from 'src/composables/useNotify.js';
 import axios from 'axios';
 import VnTable from 'src/components/VnTable/VnTable.vue';
 import VnUsesMana from 'src/components/ui/VnUsesMana.vue';
+import VnConfirm from 'src/components/ui/VnConfirm.vue';
 
 const route = useRoute();
 const router = useRouter();
@@ -32,7 +34,7 @@ const { notify } = useNotify();
 const { openConfirmationModal } = useVnConfirm();
 const editPriceProxyRef = ref(null);
 const stateBtnDropdownRef = ref(null);
-
+const quasar = useQuasar();
 const arrayData = useArrayData('ticketData');
 const { store } = arrayData;
 const selectedRows = ref([]);
@@ -51,6 +53,7 @@ const transfer = ref({
     sales: [],
 });
 const tableRef = ref([]);
+const canProceed = ref();
 
 watch(
     () => route.params.id,
@@ -214,7 +217,9 @@ const addSale = async (sale) => {
     }
 };
 
-const changeQuantity = (sale) => {
+const changeQuantity = async (sale) => {
+    canProceed.value = await isSalePrepared(sale);
+    if (!canProceed.value) return;
     if (
         !sale.itemFk ||
         sale.quantity == null ||
@@ -226,6 +231,8 @@ const changeQuantity = (sale) => {
 };
 
 const updateConcept = async (sale) => {
+    canProceed.value = await isSalePrepared(sale);
+    if (!canProceed.value) return;
     try {
         const data = { newConcept: sale.concept };
         await axios.post(`Sales/${sale.id}/updateConcept`, data);
@@ -286,6 +293,8 @@ const onOpenEditDiscountPopover = async (sale) => {
 };
 
 const updatePrice = async (sale) => {
+    canProceed.value = await isSalePrepared(sale);
+    if (!canProceed.value) return;
     try {
         const newPrice = edit.value.price;
         if (newPrice != null && newPrice != sale.price) {
@@ -300,12 +309,18 @@ const updatePrice = async (sale) => {
     }
 };
 
-const changeDiscount = (sale) => {
+const changeDiscount = async (sale) => {
+    canProceed.value = await isSalePrepared(sale);
+    if (!canProceed.value) return;
     const newDiscount = edit.value.discount;
     if (newDiscount != null && newDiscount != sale.discount) updateDiscount([sale]);
 };
 
 const updateDiscount = async (sales, newDiscount = null) => {
+    for (const sale of sales) {
+        const canProceed = await isSalePrepared(sale);
+        if (!canProceed) return;
+    }
     const saleIds = sales.map((sale) => sale.id);
     const _newDiscount = newDiscount || edit.value.discount;
     const params = {
@@ -433,7 +448,9 @@ onUnmounted(() => (stateStore.rightDrawer = false));
 const items = ref([]);
 const newRow = ref({});
 
-const updateItem = (row) => {
+const updateItem = async (row) => {
+    canProceed.value = await isSalePrepared(row);
+    if (!canProceed.value) return;
     const selectedItem = items.value.find((item) => item.id === row.itemFk);
     if (selectedItem) {
         row.item = selectedItem;
@@ -476,6 +493,55 @@ const endNewRow = (row) => {
     }
 };
 
+async function isSalePrepared(item) {
+    const filter = {
+        params: {
+            where: { ticketFk: route.params.id },
+            order: ['concept ASC', 'quantity DESC'],
+        },
+    };
+    const { data } = await axios.get(`SaleTrackings/${route.params.id}/filter`, {
+        params: {
+            filter: JSON.stringify(filter),
+        },
+    });
+
+    const matchingSale = data.find((sale) => sale.itemFk === item.itemFk);
+    if (!matchingSale) {
+        return true;
+    }
+
+    if (
+        matchingSale.hasSaleGroupDetail ||
+        matchingSale.isControled ||
+        matchingSale.isPrepared ||
+        matchingSale.isPrevious ||
+        matchingSale.isPreviousSelected
+    ) {
+        try {
+            await new Promise((resolve, reject) => {
+                quasar
+                    .dialog({
+                        component: VnConfirm,
+                        componentProps: {
+                            title: t('Item prepared'),
+                            message: t(
+                                'This item is already prepared. Do you want to continue?'
+                            ),
+                            data: item,
+                        },
+                    })
+                    .onOk(() => resolve(true))
+                    .onCancel(() => reject(new Error('cancelled')));
+            });
+        } catch (error) {
+            tableRef.value.reload();
+            return false;
+        }
+    }
+    return true;
+}
+
 watch(
     () => newRow.value.itemFk,
     (newItemFk) => {
@@ -820,4 +886,6 @@ es:
     You are going to delete lines of the ticket: Vas a eliminar lineas del ticket
     Add item: Añadir artículo
     Transfer lines: Transferir líneas
+    Item prepared: Artículo preparado
+    This item is already prepared. Do you want to continue?: Este artículo ya esta preparado. Desea continuar?
 </i18n>

From cca255507a593dce4f57fb949f2170dfb2187dd2 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Mon, 28 Oct 2024 09:57:25 +0100
Subject: [PATCH 102/150] fix: itemType redirection and fix filters

---
 src/components/common/VnComponent.vue         |  2 +-
 src/components/ui/VnFilterPanel.vue           |  2 +-
 .../ItemType/Card/ItemTypeBasicData.vue       |  0
 .../{ => Item}/ItemType/Card/ItemTypeCard.vue |  4 +-
 .../ItemType/Card/ItemTypeDescriptor.vue      |  0
 .../{ => Item}/ItemType/Card/ItemTypeLog.vue  |  0
 .../ItemType/Card/ItemTypeSummary.vue         |  0
 .../{ => Item}/ItemType/ItemTypeFilter.vue    | 35 +++++++
 .../{ => Item}/ItemType/ItemTypeSearchbar.vue |  1 +
 src/pages/{ => Item}/ItemType/locale/en.yml   |  0
 src/pages/{ => Item}/ItemType/locale/es.yml   |  0
 src/pages/Item/ItemTypeCreate.vue             | 98 -------------------
 src/pages/Item/ItemTypeList.vue               | 58 +++++++++--
 .../Ticket/Card/TicketDescriptorMenu.vue      |  1 -
 src/router/modules/item.js                    |  8 --
 src/router/modules/itemType.js                |  9 +-
 16 files changed, 93 insertions(+), 125 deletions(-)
 rename src/pages/{ => Item}/ItemType/Card/ItemTypeBasicData.vue (100%)
 rename src/pages/{ => Item}/ItemType/Card/ItemTypeCard.vue (75%)
 rename src/pages/{ => Item}/ItemType/Card/ItemTypeDescriptor.vue (100%)
 rename src/pages/{ => Item}/ItemType/Card/ItemTypeLog.vue (100%)
 rename src/pages/{ => Item}/ItemType/Card/ItemTypeSummary.vue (100%)
 rename src/pages/{ => Item}/ItemType/ItemTypeFilter.vue (57%)
 rename src/pages/{ => Item}/ItemType/ItemTypeSearchbar.vue (94%)
 rename src/pages/{ => Item}/ItemType/locale/en.yml (100%)
 rename src/pages/{ => Item}/ItemType/locale/es.yml (100%)
 delete mode 100644 src/pages/Item/ItemTypeCreate.vue

diff --git a/src/components/common/VnComponent.vue b/src/components/common/VnComponent.vue
index bd25c787c..580bcf348 100644
--- a/src/components/common/VnComponent.vue
+++ b/src/components/common/VnComponent.vue
@@ -1,5 +1,5 @@
 <script setup>
-import { computed, defineModel } from 'vue';
+import { computed } from 'vue';
 
 const model = defineModel(undefined, { required: true });
 const $props = defineProps({
diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue
index 43d634ad9..e902214b2 100644
--- a/src/components/ui/VnFilterPanel.vue
+++ b/src/components/ui/VnFilterPanel.vue
@@ -37,7 +37,7 @@ const $props = defineProps({
     },
     hiddenTags: {
         type: Array,
-        default: () => ['filter', 'search', 'or', 'and'],
+        default: () => ['filter', 'or', 'and'],
     },
     customTags: {
         type: Array,
diff --git a/src/pages/ItemType/Card/ItemTypeBasicData.vue b/src/pages/Item/ItemType/Card/ItemTypeBasicData.vue
similarity index 100%
rename from src/pages/ItemType/Card/ItemTypeBasicData.vue
rename to src/pages/Item/ItemType/Card/ItemTypeBasicData.vue
diff --git a/src/pages/ItemType/Card/ItemTypeCard.vue b/src/pages/Item/ItemType/Card/ItemTypeCard.vue
similarity index 75%
rename from src/pages/ItemType/Card/ItemTypeCard.vue
rename to src/pages/Item/ItemType/Card/ItemTypeCard.vue
index 9daec7921..0a2706a07 100644
--- a/src/pages/ItemType/Card/ItemTypeCard.vue
+++ b/src/pages/Item/ItemType/Card/ItemTypeCard.vue
@@ -1,7 +1,7 @@
 <script setup>
 import VnCard from 'components/common/VnCard.vue';
-import ItemTypeDescriptor from 'src/pages/ItemType/Card/ItemTypeDescriptor.vue';
-import ItemTypeFilter from 'src/pages/ItemType/ItemTypeFilter.vue';
+import ItemTypeDescriptor from 'src/pages/Item/ItemType/Card/ItemTypeDescriptor.vue';
+import ItemTypeFilter from 'src/pages/Item/ItemType/ItemTypeFilter.vue';
 import ItemTypeSearchbar from '../ItemTypeSearchbar.vue';
 </script>
 <template>
diff --git a/src/pages/ItemType/Card/ItemTypeDescriptor.vue b/src/pages/Item/ItemType/Card/ItemTypeDescriptor.vue
similarity index 100%
rename from src/pages/ItemType/Card/ItemTypeDescriptor.vue
rename to src/pages/Item/ItemType/Card/ItemTypeDescriptor.vue
diff --git a/src/pages/ItemType/Card/ItemTypeLog.vue b/src/pages/Item/ItemType/Card/ItemTypeLog.vue
similarity index 100%
rename from src/pages/ItemType/Card/ItemTypeLog.vue
rename to src/pages/Item/ItemType/Card/ItemTypeLog.vue
diff --git a/src/pages/ItemType/Card/ItemTypeSummary.vue b/src/pages/Item/ItemType/Card/ItemTypeSummary.vue
similarity index 100%
rename from src/pages/ItemType/Card/ItemTypeSummary.vue
rename to src/pages/Item/ItemType/Card/ItemTypeSummary.vue
diff --git a/src/pages/ItemType/ItemTypeFilter.vue b/src/pages/Item/ItemType/ItemTypeFilter.vue
similarity index 57%
rename from src/pages/ItemType/ItemTypeFilter.vue
rename to src/pages/Item/ItemType/ItemTypeFilter.vue
index 2a86795c2..f6fb73098 100644
--- a/src/pages/ItemType/ItemTypeFilter.vue
+++ b/src/pages/Item/ItemType/ItemTypeFilter.vue
@@ -12,6 +12,39 @@ const props = defineProps({
 });
 
 const emit = defineEmits(['search']);
+const exprBuilder = (param, value) => {
+    switch (param) {
+        case 'name':
+            return {
+                name: { like: `%${value}%` },
+            };
+        case 'code':
+            return {
+                code: { like: `%${value}%` },
+            };
+        case 'search':
+            if (value) {
+                if (!isNaN(value)) {
+                    return { id: value };
+                } else {
+                    return {
+                        or: [
+                            {
+                                name: {
+                                    like: `%${value}%`,
+                                },
+                            },
+                            {
+                                code: {
+                                    like: `%${value}%`,
+                                },
+                            },
+                        ],
+                    };
+                }
+            }
+    }
+};
 </script>
 
 <template>
@@ -19,6 +52,8 @@ const emit = defineEmits(['search']);
         :data-key="props.dataKey"
         :search-button="true"
         @search="emit('search')"
+        search-url="table"
+        :expr-builder="exprBuilder"
     >
         <template #tags="{ tag, formatFn }">
             <div class="q-gutter-x-xs">
diff --git a/src/pages/ItemType/ItemTypeSearchbar.vue b/src/pages/Item/ItemType/ItemTypeSearchbar.vue
similarity index 94%
rename from src/pages/ItemType/ItemTypeSearchbar.vue
rename to src/pages/Item/ItemType/ItemTypeSearchbar.vue
index 749033d43..87903a517 100644
--- a/src/pages/ItemType/ItemTypeSearchbar.vue
+++ b/src/pages/Item/ItemType/ItemTypeSearchbar.vue
@@ -10,6 +10,7 @@ const { t } = useI18n();
         url="ItemTypes"
         :label="t('Search item type')"
         :info="t('Search itemType by id, name or code')"
+        search-url="table"
     />
 </template>
 <i18n>
diff --git a/src/pages/ItemType/locale/en.yml b/src/pages/Item/ItemType/locale/en.yml
similarity index 100%
rename from src/pages/ItemType/locale/en.yml
rename to src/pages/Item/ItemType/locale/en.yml
diff --git a/src/pages/ItemType/locale/es.yml b/src/pages/Item/ItemType/locale/es.yml
similarity index 100%
rename from src/pages/ItemType/locale/es.yml
rename to src/pages/Item/ItemType/locale/es.yml
diff --git a/src/pages/Item/ItemTypeCreate.vue b/src/pages/Item/ItemTypeCreate.vue
deleted file mode 100644
index 60c037510..000000000
--- a/src/pages/Item/ItemTypeCreate.vue
+++ /dev/null
@@ -1,98 +0,0 @@
-<script setup>
-import { reactive, ref } from 'vue';
-import { useI18n } from 'vue-i18n';
-import { useRouter } from 'vue-router';
-
-import FetchData from 'components/FetchData.vue';
-import VnSelect from 'src/components/common/VnSelect.vue';
-import FormModel from 'components/FormModel.vue';
-import VnRow from 'components/ui/VnRow.vue';
-import VnInput from 'src/components/common/VnInput.vue';
-import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
-
-const { t } = useI18n();
-const router = useRouter();
-
-const newItemTypeForm = reactive({});
-
-const categoriesOptions = ref([]);
-const temperaturesOptions = ref([]);
-
-const redirectToItemTypeBasicData = (_, { id }) => {
-    router.push({ name: 'ItemTypeBasicData', params: { id } });
-};
-</script>
-
-<template>
-    <FetchData
-        url="ItemCategories"
-        @on-fetch="(data) => (categoriesOptions = data)"
-        :filter="{ order: 'name ASC', fields: ['id', 'name'] }"
-        auto-load
-    />
-    <FetchData
-        url="Temperatures"
-        @on-fetch="(data) => (temperaturesOptions = data)"
-        :filter="{ order: 'name ASC', fields: ['code', 'name'] }"
-        auto-load
-    />
-    <QPage>
-        <VnSubToolbar />
-        <FormModel
-            url-create="ItemTypes"
-            model="itemTypeCreate"
-            :form-initial-data="newItemTypeForm"
-            observe-form-changes
-            @on-data-saved="redirectToItemTypeBasicData"
-        >
-            <template #form="{ data }">
-                <VnRow>
-                    <VnInput v-model="data.code" :label="t('itemType.shared.code')" />
-                    <VnInput v-model="data.name" :label="t('itemType.shared.name')" />
-                </VnRow>
-                <VnRow>
-                    <VnSelect
-                        url="Workers/search"
-                        v-model="data.workerFk"
-                        :label="t('shared.worker')"
-                        sort-by="nickname ASC"
-                        :fields="['id', 'nickname']"
-                        :params="{ departmentCodes: ['shopping'] }"
-                        option-label="nickname"
-                        option-value="id"
-                        hide-selected
-                        ><template #option="scope">
-                            <QItem v-bind="scope.itemProps">
-                                <QItemSection>
-                                    <QItemLabel>{{ scope.opt?.name }}</QItemLabel>
-                                    <QItemLabel caption
-                                        >{{ scope.opt?.nickname }},
-                                        {{ scope.opt?.code }}</QItemLabel
-                                    >
-                                </QItemSection>
-                            </QItem>
-                        </template>
-                    </VnSelect>
-                    <VnSelect
-                        v-model="data.categoryFk"
-                        :label="t('itemType.shared.category')"
-                        :options="categoriesOptions"
-                        option-value="id"
-                        option-label="name"
-                        hide-selected
-                    />
-                </VnRow>
-                <VnRow>
-                    <VnSelect
-                        v-model="data.temperatureFk"
-                        :label="t('itemType.shared.temperature')"
-                        :options="temperaturesOptions"
-                        option-value="code"
-                        option-label="name"
-                        hide-selected
-                    />
-                </VnRow>
-            </template>
-        </FormModel>
-    </QPage>
-</template>
diff --git a/src/pages/Item/ItemTypeList.vue b/src/pages/Item/ItemTypeList.vue
index 13dd56a42..149de482d 100644
--- a/src/pages/Item/ItemTypeList.vue
+++ b/src/pages/Item/ItemTypeList.vue
@@ -1,13 +1,16 @@
 <script setup>
 import { useI18n } from 'vue-i18n';
 import { ref, computed } from 'vue';
-import ItemTypeSearchbar from '../ItemType/ItemTypeSearchbar.vue';
+import ItemTypeSearchbar from 'src/pages/Item/ItemType/ItemTypeSearchbar.vue';
 import VnTable from 'components/VnTable/VnTable.vue';
 import FetchData from 'components/FetchData.vue';
+import RightMenu from 'src/components/common/RightMenu.vue';
+import ItemTypeFilter from './ItemType/ItemTypeFilter.vue';
 
 const { t } = useI18n();
 const tableRef = ref();
-const ItemCategoriesOptions = ref([]);
+const itemCategoriesOptions = ref([]);
+const temperatureOptions = ref([]);
 
 const columns = computed(() => [
     {
@@ -34,7 +37,6 @@ const columns = computed(() => [
     },
     {
         align: 'left',
-        name: 'workerFk',
         label: t('worker'),
         create: true,
         component: 'select',
@@ -45,6 +47,19 @@ const columns = computed(() => [
         },
         cardVisible: true,
         visible: true,
+        columnField: {
+            component: 'userLink',
+            attrs: ({ row }) => {
+                return {
+                    workerId: row?.worker?.id,
+                    name: row.worker?.user?.name,
+                    defaultName: true,
+                };
+            },
+        },
+        columnFilter: {
+            name: 'workerFk',
+        },
     },
     {
         align: 'left',
@@ -53,9 +68,7 @@ const columns = computed(() => [
         create: true,
         component: 'select',
         attrs: {
-            options: ItemCategoriesOptions.value,
-            fields: ['id', 'name'],
-            order: 'name ASC',
+            options: itemCategoriesOptions.value,
         },
         cardVisible: false,
         visible: false,
@@ -67,8 +80,7 @@ const columns = computed(() => [
         create: true,
         component: 'select',
         attrs: {
-            url: 'Temperatures',
-            fields: ['id', 'name'],
+            options: temperatureOptions.value,
         },
         cardVisible: false,
         visible: false,
@@ -80,26 +92,52 @@ const columns = computed(() => [
     <FetchData
         url="ItemCategories"
         :filter="{ fields: ['id', 'name'], order: ['name ASC'] }"
-        @on-fetch="(data) => (ItemCategoriesOptions = data)"
+        @on-fetch="(data) => (itemCategoriesOptions = data)"
         auto-load
     />
+    <FetchData
+        url="Temperatures"
+        :filter="{ fields: ['id', 'name'], order: ['name ASC'] }"
+        @on-fetch="(data) => (temperatureOptions = data)"
+        auto-load
+    />
+    <RightMenu>
+        <template #right-panel>
+            <ItemTypeFilter data-key="ItemTypeList" />
+        </template>
+    </RightMenu>
     <ItemTypeSearchbar />
     <VnTable
         ref="tableRef"
         data-key="ItemTypeList"
-        :url="`ItemTypes`"
+        url="ItemTypes"
         :create="{
             urlCreate: 'ItemTypes',
             title: t('Create ItemTypes'),
             onDataSaved: () => tableRef.reload(),
             formInitialData: {},
         }"
+        :user-filter="{
+            include: {
+                relation: 'worker',
+                scope: {
+                    fields: ['id'],
+                    include: {
+                        relation: 'user',
+                        scope: {
+                            fields: ['id', 'name'],
+                        },
+                    },
+                },
+            },
+        }"
         order="name ASC"
         :columns="columns"
         auto-load
         :right-search="false"
         :is-editable="false"
         :use-model="true"
+        redirect="item/item-type"
     />
 </template>
 
diff --git a/src/pages/Ticket/Card/TicketDescriptorMenu.vue b/src/pages/Ticket/Card/TicketDescriptorMenu.vue
index 834fced87..bf4a1efb4 100644
--- a/src/pages/Ticket/Card/TicketDescriptorMenu.vue
+++ b/src/pages/Ticket/Card/TicketDescriptorMenu.vue
@@ -348,7 +348,6 @@ async function hasDocuware() {
 }
 
 async function uploadDocuware(force) {
-    console.log('force: ', force);
     if (!force)
         return quasar
             .dialog({
diff --git a/src/router/modules/item.js b/src/router/modules/item.js
index 2838c3be7..0f810434c 100644
--- a/src/router/modules/item.js
+++ b/src/router/modules/item.js
@@ -97,14 +97,6 @@ export default {
                     },
                     component: () => import('src/pages/Item/ItemTypeList.vue'),
                 },
-                {
-                    path: 'item-type-list/create',
-                    name: 'ItemTypeCreate',
-                    meta: {
-                        title: 'itemTypeCreate',
-                    },
-                    component: () => import('src/pages/Item/ItemTypeCreate.vue'),
-                },
             ],
         },
         {
diff --git a/src/router/modules/itemType.js b/src/router/modules/itemType.js
index 0fd3797e6..1ceecd4cc 100644
--- a/src/router/modules/itemType.js
+++ b/src/router/modules/itemType.js
@@ -18,7 +18,7 @@ export default {
         {
             name: 'ItemTypeCard',
             path: ':id',
-            component: () => import('src/pages/ItemType/Card/ItemTypeCard.vue'),
+            component: () => import('src/pages/Item/ItemType/Card/ItemTypeCard.vue'),
             redirect: { name: 'ItemTypeSummary' },
             children: [
                 {
@@ -28,7 +28,7 @@ export default {
                         title: 'summary',
                     },
                     component: () =>
-                        import('src/pages/ItemType/Card/ItemTypeSummary.vue'),
+                        import('src/pages/Item/ItemType/Card/ItemTypeSummary.vue'),
                 },
                 {
                     name: 'ItemTypeBasicData',
@@ -38,7 +38,7 @@ export default {
                         icon: 'vn:settings',
                     },
                     component: () =>
-                        import('src/pages/ItemType/Card/ItemTypeBasicData.vue'),
+                        import('src/pages/Item/ItemType/Card/ItemTypeBasicData.vue'),
                 },
                 {
                     path: 'log',
@@ -47,7 +47,8 @@ export default {
                         title: 'log',
                         icon: 'vn:History',
                     },
-                    component: () => import('src/pages/ItemType/Card/ItemTypeLog.vue'),
+                    component: () =>
+                        import('src/pages/Item/ItemType/Card/ItemTypeLog.vue'),
                 },
             ],
         },

From bc8a87b267921f339843e6639de268a55a482938 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Mon, 28 Oct 2024 09:58:07 +0100
Subject: [PATCH 103/150] chore: typo

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

diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue
index e902214b2..43d634ad9 100644
--- a/src/components/ui/VnFilterPanel.vue
+++ b/src/components/ui/VnFilterPanel.vue
@@ -37,7 +37,7 @@ const $props = defineProps({
     },
     hiddenTags: {
         type: Array,
-        default: () => ['filter', 'or', 'and'],
+        default: () => ['filter', 'search', 'or', 'and'],
     },
     customTags: {
         type: Array,

From df1c123444e577336fed9c5b1c17d093ecf37d68 Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Mon, 28 Oct 2024 10:39:45 +0100
Subject: [PATCH 104/150] feat: refs #7193 modified parking to use the scope
 and corrected small errors

---
 src/i18n/locale/en.yml                       | 3 +--
 src/i18n/locale/es.yml                       | 3 +--
 src/pages/Parking/Card/ParkingCard.vue       | 6 ------
 src/pages/Parking/Card/ParkingDescriptor.vue | 1 +
 src/pages/Parking/ParkingList.vue            | 4 +---
 5 files changed, 4 insertions(+), 13 deletions(-)

diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml
index c1748c8ff..24fda7aff 100644
--- a/src/i18n/locale/en.yml
+++ b/src/i18n/locale/en.yml
@@ -278,6 +278,7 @@ globals:
         RouteExtendedList: Router
         wasteRecalc: Waste recaclulate
         operator: Operator
+        parking: Parking
     supplier: Supplier
     created: Created
     worker: Worker
@@ -663,8 +664,6 @@ parking:
     sector: Sector
     row: Row
     column: Column
-    pageTitles:
-        parking: Parking
     searchBar:
         info: You can search by parking code
         label: Search parking...
diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml
index bd414a793..1571f7b92 100644
--- a/src/i18n/locale/es.yml
+++ b/src/i18n/locale/es.yml
@@ -282,6 +282,7 @@ globals:
         medical: Mutua
         wasteRecalc: Recalcular mermas
         operator: Operario
+        parking: Parking
     supplier: Proveedor
     created: Fecha creación
     worker: Trabajador
@@ -710,8 +711,6 @@ parking:
     pickingOrder: Orden de recogida
     row: Fila
     column: Columna
-    pageTitles:
-        parking: Parking
     searchBar:
         info: Puedes buscar por código de parking
         label: Buscar parking...
diff --git a/src/pages/Parking/Card/ParkingCard.vue b/src/pages/Parking/Card/ParkingCard.vue
index ad37eb630..337106986 100644
--- a/src/pages/Parking/Card/ParkingCard.vue
+++ b/src/pages/Parking/Card/ParkingCard.vue
@@ -2,17 +2,11 @@
 import VnCard from 'components/common/VnCard.vue';
 import ParkingDescriptor from 'pages/Parking/Card/ParkingDescriptor.vue';
 import ParkingFilter from 'pages/Parking/ParkingFilter.vue';
-
-const filter = {
-    fields: ['id', 'sectorFk', 'code', 'pickingOrder', 'row', 'column'],
-    include: [{ relation: 'sector', scope: { fields: ['id', 'description'] } }],
-};
 </script>
 <template>
     <VnCard
         data-key="Parking"
         base-url="Parkings"
-        :filter="filter"
         :descriptor="ParkingDescriptor"
         :filter-panel="ParkingFilter"
         search-data-key="ParkingList"
diff --git a/src/pages/Parking/Card/ParkingDescriptor.vue b/src/pages/Parking/Card/ParkingDescriptor.vue
index b57bfb0cc..d36ea16fc 100644
--- a/src/pages/Parking/Card/ParkingDescriptor.vue
+++ b/src/pages/Parking/Card/ParkingDescriptor.vue
@@ -29,6 +29,7 @@ const filter = {
         :url="`Parkings/${entityId}`"
         title="code"
         :filter="filter"
+        :to-module="{ name: 'ParkingList' }"
     >
         <template #body="{ entity }">
             <VnLv :label="t('globals.code')" :value="entity.code" />
diff --git a/src/pages/Parking/ParkingList.vue b/src/pages/Parking/ParkingList.vue
index b6f4e8146..109613383 100644
--- a/src/pages/Parking/ParkingList.vue
+++ b/src/pages/Parking/ParkingList.vue
@@ -22,7 +22,6 @@ onUnmounted(() => (stateStore.rightDrawer = false));
 
 const filter = {
     fields: ['id', 'sectorFk', 'code', 'pickingOrder'],
-    include: [{ relation: 'sector', scope: { fields: ['id', 'description'] } }],
 };
 
 function exprBuilder(param, value) {
@@ -55,10 +54,9 @@ function exprBuilder(param, value) {
             <VnPaginate
                 data-key="ParkingList"
                 url="Parkings"
-                :filter="filter"
+                :user-filter="filter"
                 :expr-builder="exprBuilder"
                 :limit="20"
-                auto-load
                 order="code"
             >
                 <template #body="{ rows }">

From 2cde8501327333e6f259f8e7d1bea88e1d5be1a9 Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Mon, 28 Oct 2024 11:37:32 +0100
Subject: [PATCH 105/150] fix: refs #7193 fixed e2e test

---
 test/cypress/integration/parking/parkingList.spec.js | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/test/cypress/integration/parking/parkingList.spec.js b/test/cypress/integration/parking/parkingList.spec.js
index b78a660d1..f1efaa375 100644
--- a/test/cypress/integration/parking/parkingList.spec.js
+++ b/test/cypress/integration/parking/parkingList.spec.js
@@ -1,5 +1,6 @@
 /// <reference types="cypress" />
 describe('ParkingList', () => {
+    const searchbar = '#searchbar input';
     const firstCard = '.q-card:nth-child(1)';
     const firstChipId =
         ':nth-child(1) > :nth-child(1) > .justify-between > .flex > .q-chip > .q-chip__content';
@@ -14,6 +15,7 @@ describe('ParkingList', () => {
     });
 
     it('should redirect on clicking a parking', () => {
+        cy.get(searchbar).type('{enter}');
         cy.get(firstChipId)
             .invoke('text')
             .then((content) => {
@@ -24,6 +26,7 @@ describe('ParkingList', () => {
     });
 
     it('should open the details', () => {
+        cy.get(searchbar).type('{enter}');
         cy.get(firstDetailBtn).click();
         cy.get(summaryHeader).contains('Basic data');
     });

From 9347bb1ef5384aee2e84f73afa3d3b48b449b500 Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Mon, 28 Oct 2024 11:44:49 +0100
Subject: [PATCH 106/150] refactor: refs #7970 refactored VnConfirm to emit
 events

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

diff --git a/src/components/ui/VnConfirm.vue b/src/components/ui/VnConfirm.vue
index 0fbc55cfb..c5d2b43f5 100644
--- a/src/components/ui/VnConfirm.vue
+++ b/src/components/ui/VnConfirm.vue
@@ -30,7 +30,7 @@ const props = defineProps({
     },
 });
 
-defineEmits([...useDialogPluginComponent.emits]);
+defineEmits(['confirm', 'cancel', ...useDialogPluginComponent.emits]);
 
 const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
     useDialogPluginComponent();

From 17b1921af6603db83744efc8095c6dbc4eb34141 Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Mon, 28 Oct 2024 12:21:26 +0100
Subject: [PATCH 107/150] feat: added composable to confirm orders

---
 src/composables/confirmOrder.js       | 22 +++++++++++++++++++++
 src/pages/Order/Card/OrderLines.vue   | 28 +++++++++++++--------------
 src/pages/Order/Card/OrderSummary.vue | 21 +++++++++-----------
 3 files changed, 44 insertions(+), 27 deletions(-)
 create mode 100644 src/composables/confirmOrder.js

diff --git a/src/composables/confirmOrder.js b/src/composables/confirmOrder.js
new file mode 100644
index 000000000..33e05a1d7
--- /dev/null
+++ b/src/composables/confirmOrder.js
@@ -0,0 +1,22 @@
+import axios from 'axios';
+import { useQuasar } from 'quasar';
+import { useI18n } from 'vue-i18n';
+
+export function confirmOrder() {
+    const quasar = useQuasar();
+    const { t } = useI18n();
+
+    async function confirm(route) {
+        const { data } = await axios.post(`Orders/${route}/confirm`);
+        if (data) {
+            quasar.notify({
+                message: t('globals.confirm'),
+                type: 'positive',
+            });
+            return data;
+        }
+        return null;
+    }
+
+    return { confirm };
+}
diff --git a/src/pages/Order/Card/OrderLines.vue b/src/pages/Order/Card/OrderLines.vue
index ee883b73a..90586dc15 100644
--- a/src/pages/Order/Card/OrderLines.vue
+++ b/src/pages/Order/Card/OrderLines.vue
@@ -6,6 +6,7 @@ import { useQuasar } from 'quasar';
 import axios from 'axios';
 import { useStateStore } from 'stores/useStateStore';
 import { useArrayData } from 'composables/useArrayData';
+import { confirmOrder } from 'composables/confirmOrder';
 import { toCurrency, toDate } from 'src/filters';
 
 import VnConfirm from 'components/ui/VnConfirm.vue';
@@ -31,7 +32,7 @@ const orderSummary = ref({
 });
 const getTotalRef = ref();
 const getVATRef = ref();
-
+const { confirm } = confirmOrder();
 const lineFilter = ref({
     include: [
         {
@@ -204,20 +205,17 @@ async function remove(item) {
     getVATRef.value.fetch();
 }
 
-async function confirmOrder() {
-    await axios.post(`Orders/${route.params.id}/confirm`);
-    quasar.notify({
-        message: t('globals.confirm'),
-        type: 'positive',
-    });
-    router.push({
-        name: 'TicketList',
-        query: {
-            table: JSON.stringify({ clientFk: descriptorData.store.data.clientFk }),
-        },
-    });
+async function handleConfirm() {
+    const result = await confirm(route.params.id);
+    if (result) {
+        router.push({
+            name: 'TicketList',
+            query: {
+                table: JSON.stringify({ clientFk: descriptorData.store.data.clientFk }),
+            },
+        });
+    }
 }
-
 watch(
     () => router.currentRoute.value.params.id,
     () => {
@@ -314,7 +312,7 @@ watch(
         </template>
     </VnTable>
     <QPageSticky :offset="[20, 20]" v-if="!order?.isConfirmed" style="z-index: 2">
-        <QBtn fab icon="check" color="primary" @click="confirmOrder()" />
+        <QBtn fab icon="check" color="primary" @click="handleConfirm()" />
         <QTooltip>
             {{ t('confirm') }}
         </QTooltip>
diff --git a/src/pages/Order/Card/OrderSummary.vue b/src/pages/Order/Card/OrderSummary.vue
index 9bc89ad28..3669fe8d8 100644
--- a/src/pages/Order/Card/OrderSummary.vue
+++ b/src/pages/Order/Card/OrderSummary.vue
@@ -2,10 +2,9 @@
 import { computed, ref } from 'vue';
 import { useRoute } from 'vue-router';
 import { useI18n } from 'vue-i18n';
-import { useQuasar } from 'quasar';
-import axios from 'axios';
 import { dashIfEmpty, toCurrency, toDateHourMinSec } from 'src/filters';
 import { useArrayData } from 'composables/useArrayData';
+import { confirmOrder } from 'composables/confirmOrder';
 import VnLv from 'components/ui/VnLv.vue';
 import CardSummary from 'components/ui/CardSummary.vue';
 import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
@@ -25,8 +24,8 @@ const $props = defineProps({
 
 const entityId = computed(() => $props.id || route.params.id);
 const summary = ref();
-const quasar = useQuasar();
 const descriptorData = useArrayData('orderData');
+const { confirm } = confirmOrder();
 const detailsColumns = ref([
     {
         name: 'item',
@@ -56,14 +55,12 @@ const detailsColumns = ref([
     },
 ]);
 
-async function confirmOrder() {
-    await axios.post(`Orders/${route.params.id}/confirm`);
-    quasar.notify({
-        message: t('globals.confirm'),
-        type: 'positive',
-    });
-    summary.value.fetch({});
-    descriptorData.fetch({});
+async function handleConfirm() {
+    const result = await confirm(route.params.id);
+    if (result) {
+        summary.value.fetch({});
+        descriptorData.fetch({});
+    }
 }
 </script>
 
@@ -84,7 +81,7 @@ async function confirmOrder() {
                     text-color="white"
                     :disabled="isConfirmed"
                     :label="t('order.summary.confirm')"
-                    @click="confirmOrder()"
+                    @click="handleConfirm()"
                 >
                     <QTooltip>{{ t('order.summary.confirmLines') }}</QTooltip>
                 </QBtn>

From 3d4ca2bc672a2375a14df8c2f2918b137839ffef Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Mon, 28 Oct 2024 12:45:13 +0100
Subject: [PATCH 108/150] refactor: refs #7970 added emit

---
 src/components/ui/VnConfirm.vue | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/components/ui/VnConfirm.vue b/src/components/ui/VnConfirm.vue
index c5d2b43f5..ec9d0f48b 100644
--- a/src/components/ui/VnConfirm.vue
+++ b/src/components/ui/VnConfirm.vue
@@ -30,7 +30,7 @@ const props = defineProps({
     },
 });
 
-defineEmits(['confirm', 'cancel', ...useDialogPluginComponent.emits]);
+const emit = defineEmits(['confirm', 'cancel', ...useDialogPluginComponent.emits]);
 
 const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
     useDialogPluginComponent();
@@ -55,6 +55,7 @@ async function confirm() {
 }
 
 function cancel() {
+    emit('cancel');
     onDialogCancel();
 }
 </script>

From cab5a165d2acb6c4e5d92859e5ed96611029d938 Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Mon, 28 Oct 2024 12:59:25 +0100
Subject: [PATCH 109/150] refactor: changed confirmOrder directory

---
 src/{ => pages/Order}/composables/confirmOrder.js | 0
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename src/{ => pages/Order}/composables/confirmOrder.js (100%)

diff --git a/src/composables/confirmOrder.js b/src/pages/Order/composables/confirmOrder.js
similarity index 100%
rename from src/composables/confirmOrder.js
rename to src/pages/Order/composables/confirmOrder.js

From f2803a63136a9478e492d112d4f6f1b6d16b5c73 Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Mon, 28 Oct 2024 13:08:19 +0100
Subject: [PATCH 110/150] fix: refs #7283 item filters

---
 src/pages/Item/ItemList.vue    | 35 +++++++++++++++++++++-------------
 src/pages/Item/ItemRequest.vue | 24 +++++++++++++++++++++++
 2 files changed, 46 insertions(+), 13 deletions(-)

diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index cca5560fe..c9422a0fe 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -40,6 +40,12 @@ const itemFilter = {
                 fields: ['id', 'name'],
             },
         },
+        {
+            relation: 'production',
+            scope: {
+                fields: ['id', 'name'],
+            },
+        },
     ],
 };
 const columns = computed(() => [
@@ -161,19 +167,13 @@ const columns = computed(() => [
         name: 'intrastat',
         align: 'left',
         component: 'select',
-        attrs: {
-            url: 'Intrastats',
-            optionValue: 'description',
-            optionLabel: 'description',
-        },
         columnFilter: {
-            name: 'description',
+            name: 'intrastat',
             attrs: {
                 url: 'Intrastats',
                 optionValue: 'description',
                 optionLabel: 'description',
             },
-            alias: 'intr',
         },
         columnField: {
             component: null,
@@ -211,14 +211,19 @@ const columns = computed(() => [
         label: t('item.list.userName'),
         name: 'userName',
         align: 'left',
+        component: 'select',
         columnFilter: {
             name: 'workerFk',
             attrs: {
-                url: 'Users',
+                url: 'VnUsers',
                 optionValue: 'id',
-                optionLabel: 'userName',
+                optionLabel: 'nickname',
             },
         },
+
+        columnField: {
+            component: null,
+        },
     },
     {
         label: t('item.list.weightByPiece'),
@@ -255,9 +260,13 @@ const columns = computed(() => [
         name: 'producer',
         align: 'left',
         component: 'select',
-        attrs: {
-            url: 'Producers',
-            fields: ['id', 'name'],
+        columnFilter: {
+            name: 'producerFk',
+            attrs: {
+                url: 'Producers',
+                optionValue: 'id',
+                optionLabel: 'name',
+            },
         },
         columnField: {
             component: null,
@@ -331,7 +340,7 @@ const columns = computed(() => [
         <template #column-userName="{ row }">
             <span class="link" @click.stop>
                 {{ row.userName }}
-                <WorkerDescriptorProxy :id="row.buyerFk" />
+                <WorkerDescriptorProxy :id="row.workerFk" />
             </span>
         </template>
         <template #column-description="{ row }">
diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue
index 450031a0e..7a737dd71 100644
--- a/src/pages/Item/ItemRequest.vue
+++ b/src/pages/Item/ItemRequest.vue
@@ -70,6 +70,18 @@ const columns = computed(() => [
     {
         label: t('item.buyRequest.requester'),
         name: 'requesterName',
+        component: 'select',
+        columnFilter: {
+            name: 'requesterFk',
+            attrs: {
+                url: 'VnUsers',
+                optionValue: 'id',
+                optionLabel: 'nickname',
+            },
+        },
+        columnField: {
+            component: null,
+        },
         columnClass: 'shrink',
     },
     {
@@ -88,6 +100,18 @@ const columns = computed(() => [
         label: t('item.buyRequest.attender'),
         name: 'attenderName',
         align: 'left',
+        component: 'select',
+        columnFilter: {
+            name: 'attenderFk',
+            attrs: {
+                url: 'VnUsers',
+                optionValue: 'id',
+                optionLabel: 'nickname',
+            },
+        },
+        columnField: {
+            component: null,
+        },
         columnClass: 'shrink',
     },
     {

From 965c2f7b20bce7ce702fddb758654b810d94e121 Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Mon, 28 Oct 2024 13:16:51 +0100
Subject: [PATCH 111/150] fix: refs #7283 preview

---
 src/pages/Item/ItemList.vue    | 2 +-
 src/pages/Item/ItemRequest.vue | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index c9422a0fe..a6873d10c 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -215,7 +215,7 @@ const columns = computed(() => [
         columnFilter: {
             name: 'workerFk',
             attrs: {
-                url: 'VnUsers',
+                url: 'VnUsers/preview',
                 optionValue: 'id',
                 optionLabel: 'nickname',
             },
diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue
index 7a737dd71..4122b0c12 100644
--- a/src/pages/Item/ItemRequest.vue
+++ b/src/pages/Item/ItemRequest.vue
@@ -104,7 +104,7 @@ const columns = computed(() => [
         columnFilter: {
             name: 'attenderFk',
             attrs: {
-                url: 'VnUsers',
+                url: 'VnUsers/preview',
                 optionValue: 'id',
                 optionLabel: 'nickname',
             },

From d252f6d2a3deb600cc0290a5892c26b614decc76 Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Mon, 28 Oct 2024 13:22:33 +0100
Subject: [PATCH 112/150] fix: refs #7283 fix preview

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

diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue
index 4122b0c12..36da0368b 100644
--- a/src/pages/Item/ItemRequest.vue
+++ b/src/pages/Item/ItemRequest.vue
@@ -74,7 +74,7 @@ const columns = computed(() => [
         columnFilter: {
             name: 'requesterFk',
             attrs: {
-                url: 'VnUsers',
+                url: 'VnUsers/preview',
                 optionValue: 'id',
                 optionLabel: 'nickname',
             },

From a1105f2ef19b15eb3cc54891b737d4495b20b8a9 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Mon, 28 Oct 2024 13:24:46 +0100
Subject: [PATCH 113/150] feat: refs #7524 add front test

---
 .../pages/Tickets/TicketAdvance.spec.js       | 116 ++++++++++++++++++
 test/vitest/helper.js                         |   2 +
 2 files changed, 118 insertions(+)
 create mode 100644 test/vitest/__tests__/pages/Tickets/TicketAdvance.spec.js

diff --git a/test/vitest/__tests__/pages/Tickets/TicketAdvance.spec.js b/test/vitest/__tests__/pages/Tickets/TicketAdvance.spec.js
new file mode 100644
index 000000000..6fae2788e
--- /dev/null
+++ b/test/vitest/__tests__/pages/Tickets/TicketAdvance.spec.js
@@ -0,0 +1,116 @@
+import { vi, describe, expect, it, beforeAll, afterEach, beforeEach } from 'vitest';
+import { createWrapper, axios } from 'app/test/vitest/helper';
+import TicketAdvance from 'pages/Ticket/TicketAdvance.vue';
+import { Notify } from 'quasar';
+
+describe('TicketAdvance', () => {
+    let wrapper;
+    let vm;
+
+    beforeAll(() => {
+        vi.spyOn(axios, 'get').mockImplementation(() => ({ data: [] }));
+        wrapper = createWrapper(TicketAdvance);
+        vm = wrapper.vm;
+        // vm.vnTableRef.value = { reload: vi.fn(), params: {} };
+    });
+    beforeEach(() => {
+        Notify.create = vi.fn();
+    });
+    afterEach(() => {
+        vi.clearAllMocks();
+    });
+
+    describe('requestComponentUpdate()', () => {
+        const mockTicket = {
+            futureId: 1,
+            futureClientFk: 1,
+            nickname: 'test',
+            futureAddressFk: 1,
+            futureAgencyModeFk: 1,
+            futureWarehouseFk: 1,
+            futureCompanyFk: 1,
+            landed: '2023-01-01',
+            zoneFk: 1,
+        };
+        const mockParams = {
+            clientFk: 1,
+            nickname: 'test',
+            agencyModeFk: 1,
+            addressFk: 1,
+            zoneFk: 1,
+            warehouseFk: 1,
+            companyFk: 1,
+            landed: '2023-01-01',
+            isDeleted: false,
+            isWithoutNegatives: false,
+            newTicket: undefined,
+            keepPrice: true,
+        };
+        const queryResult = 'tickets/1/componentUpdate';
+
+        it('should return query and params when ticket has no landed', async () => {
+            const mockLanded = { landed: '2023-01-01', zoneFk: 1 };
+            vi.spyOn(vm, 'getLanded').mockResolvedValue(mockLanded);
+
+            const { query, params } = await vm.requestComponentUpdate(mockTicket, false);
+
+            expect(query).toBe(queryResult);
+            expect(params).toEqual(mockParams);
+        });
+
+        it('should return query and params when ticket has landed', async () => {
+            const { query, params } = await vm.requestComponentUpdate(mockTicket, false);
+
+            expect(query).toBe(queryResult);
+            expect(params).toEqual(mockParams);
+        });
+    });
+
+    describe('moveTicketsAdvance()', () => {
+        it('should move tickets and notify success', async () => {
+            const tickets = [
+                {
+                    id: 1,
+                    futureId: 2,
+                    futureShipped: '2023-01-01',
+                    shipped: '2023-01-02',
+                    workerFk: 1,
+                },
+                {
+                    id: 2,
+                    futureId: 3,
+                    futureShipped: '2023-01-01',
+                    shipped: '2023-01-02',
+                    workerFk: 1,
+                },
+            ];
+            vm.selectedTickets.value = tickets;
+            vi.spyOn(axios, 'post').mockResolvedValue({});
+            await vm.moveTicketsAdvance();
+
+            expect(axios.post).toHaveBeenCalledOnce('Tickets/merge', {
+                tickets: [
+                    {
+                        originId: 2,
+                        destinationId: 1,
+                        originShipped: '2023-01-01',
+                        destinationShipped: '2023-01-02',
+                        workerFk: 1,
+                    },
+                    {
+                        originId: 3,
+                        destinationId: 2,
+                        originShipped: '2023-01-01',
+                        destinationShipped: '2023-01-02',
+                        workerFk: 1,
+                    },
+                ],
+            });
+            expect(Notify.create).toHaveBeenCalledWith({
+                type: 'positive',
+                message: 'advanceTickets.moveTicketSuccess',
+            });
+            expect(vm.selectedTickets).toEqual([]);
+        });
+    });
+});
diff --git a/test/vitest/helper.js b/test/vitest/helper.js
index e201535ec..4bfae5dc8 100644
--- a/test/vitest/helper.js
+++ b/test/vitest/helper.js
@@ -70,8 +70,10 @@ class FormDataMock {
         vi.fn();
     }
 }
+
 global.FormData = FormDataMock;
 global.URL = class URL {};
+global.Date.vnNew = () => new Date(Date.UTC(2001, 0, 1, 11));
 
 export function createWrapper(component, options) {
     const defaultOptions = {

From 15977357d9c4c3e76d79cd5798ee78a222e367ff Mon Sep 17 00:00:00 2001
From: guillermo <guillermo@verdnatura.es>
Date: Mon, 28 Oct 2024 14:11:34 +0100
Subject: [PATCH 114/150] refactor: refs #7266 Changed method name

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

diff --git a/src/pages/Entry/EntryBuysTableDialog.vue b/src/pages/Entry/EntryBuysTableDialog.vue
index 0f9be6298..23a6a0021 100644
--- a/src/pages/Entry/EntryBuysTableDialog.vue
+++ b/src/pages/Entry/EntryBuysTableDialog.vue
@@ -121,7 +121,7 @@ const entriesTableColumns = computed(() => [
                                         :loading="isLoading"
                                         @click="
                                             openReport(
-                                                `Entries/${props.row.id}/buy-label`
+                                                `Entries/${props.row.id}/buy-label-supplier`
                                             )
                                         "
                                         unelevated

From 934b32092bb27eebcbbd298674bb5e5f7e832515 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Mon, 28 Oct 2024 14:21:03 +0100
Subject: [PATCH 115/150] chore: refs #7524 fix test

---
 .../pages/Tickets/TicketAdvance.spec.js       | 22 +++++++++++--------
 1 file changed, 13 insertions(+), 9 deletions(-)

diff --git a/test/vitest/__tests__/pages/Tickets/TicketAdvance.spec.js b/test/vitest/__tests__/pages/Tickets/TicketAdvance.spec.js
index 6fae2788e..ab1a47544 100644
--- a/test/vitest/__tests__/pages/Tickets/TicketAdvance.spec.js
+++ b/test/vitest/__tests__/pages/Tickets/TicketAdvance.spec.js
@@ -2,6 +2,7 @@ import { vi, describe, expect, it, beforeAll, afterEach, beforeEach } from 'vite
 import { createWrapper, axios } from 'app/test/vitest/helper';
 import TicketAdvance from 'pages/Ticket/TicketAdvance.vue';
 import { Notify } from 'quasar';
+import { nextTick } from 'vue';
 
 describe('TicketAdvance', () => {
     let wrapper;
@@ -11,7 +12,8 @@ describe('TicketAdvance', () => {
         vi.spyOn(axios, 'get').mockImplementation(() => ({ data: [] }));
         wrapper = createWrapper(TicketAdvance);
         vm = wrapper.vm;
-        // vm.vnTableRef.value = { reload: vi.fn(), params: {} };
+        vi.spyOn(vm.vnTableRef, 'reload').mockImplementation(() => vi.fn());
+        vm.vnTableRef.value = { params: {} };
     });
     beforeEach(() => {
         Notify.create = vi.fn();
@@ -29,7 +31,7 @@ describe('TicketAdvance', () => {
             futureAgencyModeFk: 1,
             futureWarehouseFk: 1,
             futureCompanyFk: 1,
-            landed: '2023-01-01',
+            landed: '2023-01-02',
             zoneFk: 1,
         };
         const mockParams = {
@@ -40,7 +42,8 @@ describe('TicketAdvance', () => {
             zoneFk: 1,
             warehouseFk: 1,
             companyFk: 1,
-            landed: '2023-01-01',
+            landed: '2023-01-02',
+            shipped: '2023-01-01',
             isDeleted: false,
             isWithoutNegatives: false,
             newTicket: undefined,
@@ -49,7 +52,10 @@ describe('TicketAdvance', () => {
         const queryResult = 'tickets/1/componentUpdate';
 
         it('should return query and params when ticket has no landed', async () => {
-            const mockLanded = { landed: '2023-01-01', zoneFk: 1 };
+            vm.vnTableRef.params.dateToAdvance = '2023-01-01';
+            await nextTick();
+
+            const mockLanded = { landed: '2023-01-02', zoneFk: 1 };
             vi.spyOn(vm, 'getLanded').mockResolvedValue(mockLanded);
 
             const { query, params } = await vm.requestComponentUpdate(mockTicket, false);
@@ -84,7 +90,7 @@ describe('TicketAdvance', () => {
                     workerFk: 1,
                 },
             ];
-            vm.selectedTickets.value = tickets;
+            vm.selectedTickets = tickets;
             vi.spyOn(axios, 'post').mockResolvedValue({});
             await vm.moveTicketsAdvance();
 
@@ -106,10 +112,8 @@ describe('TicketAdvance', () => {
                     },
                 ],
             });
-            expect(Notify.create).toHaveBeenCalledWith({
-                type: 'positive',
-                message: 'advanceTickets.moveTicketSuccess',
-            });
+            expect(vm.vnTableRef.reload).toHaveBeenCalled();
+            expect(Notify.create).toHaveBeenCalled();
             expect(vm.selectedTickets).toEqual([]);
         });
     });

From d5639471cf358c89c9dfb28b3f3b9eb11b2d4a23 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Mon, 28 Oct 2024 15:40:36 +0100
Subject: [PATCH 116/150] feat: refs #8078 add tests

---
 .../__tests__/components/VnTable.spec.js      | 47 +++++++++++++++++++
 1 file changed, 47 insertions(+)
 create mode 100644 test/vitest/__tests__/components/VnTable.spec.js

diff --git a/test/vitest/__tests__/components/VnTable.spec.js b/test/vitest/__tests__/components/VnTable.spec.js
new file mode 100644
index 000000000..d11c2b6c3
--- /dev/null
+++ b/test/vitest/__tests__/components/VnTable.spec.js
@@ -0,0 +1,47 @@
+import { describe, expect, it, beforeAll, beforeEach } from 'vitest';
+import { createWrapper } from 'app/test/vitest/helper';
+import VnTable from 'src/components/VnTable/VnTable.vue';
+
+describe('VnTable', () => {
+    let wrapper;
+    let vm;
+
+    beforeAll(() => {
+        wrapper = createWrapper(VnTable, {
+            propsData: {
+                columns: [],
+            },
+        });
+        vm = wrapper.vm;
+    });
+
+    beforeEach(() => (vm.selected = []));
+
+    describe('handleSelection()', () => {
+        const rows = [{ $index: 0 }, { $index: 1 }, { $index: 2 }];
+        const selectedRows = [{ $index: 1 }];
+        it('should add rows to selected when shift key is pressed and rows are added', () => {
+            vm.handleSelection(
+                { evt: { shiftKey: true }, added: true, rows: selectedRows },
+                rows
+            );
+            expect(vm.selected).toEqual([{ $index: 0 }, { $index: 1 }]);
+        });
+
+        it('should not add rows to selected when shift key is not pressed', () => {
+            vm.handleSelection(
+                { evt: { shiftKey: false }, added: true, rows: selectedRows },
+                rows
+            );
+            expect(vm.selected).toEqual([]);
+        });
+
+        it('should not add rows to selected when rows are not added', () => {
+            vm.handleSelection(
+                { evt: { shiftKey: true }, added: false, rows: selectedRows },
+                rows
+            );
+            expect(vm.selected).toEqual([]);
+        });
+    });
+});

From 7e3e194b465f0ba3abfa4a57aa762f58f63d24c1 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Mon, 28 Oct 2024 16:19:13 +0100
Subject: [PATCH 117/150] fix: refs #8078 e2e #7970

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

diff --git a/src/components/ui/VnConfirm.vue b/src/components/ui/VnConfirm.vue
index ec9d0f48b..89726c891 100644
--- a/src/components/ui/VnConfirm.vue
+++ b/src/components/ui/VnConfirm.vue
@@ -31,9 +31,9 @@ const props = defineProps({
 });
 
 const emit = defineEmits(['confirm', 'cancel', ...useDialogPluginComponent.emits]);
-
 const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
     useDialogPluginComponent();
+defineExpose({ dialogRef });
 
 const title = props.title || t('Confirm');
 const message =

From 57488503aa1c9494586f6bf3c1b6b7860e7ccf6b Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Mon, 28 Oct 2024 16:19:24 +0100
Subject: [PATCH 118/150] fix: refs #8078 e2e #7970

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

diff --git a/src/pages/Ticket/Card/TicketDescriptorMenu.vue b/src/pages/Ticket/Card/TicketDescriptorMenu.vue
index bf4a1efb4..8efd0f1e1 100644
--- a/src/pages/Ticket/Card/TicketDescriptorMenu.vue
+++ b/src/pages/Ticket/Card/TicketDescriptorMenu.vue
@@ -659,7 +659,7 @@ async function uploadDocuware(force) {
             </QList>
         </QMenu>
     </QItem>
-    <QItem @click="$refs.weightDialog.show()" v-ripple clickable>
+    <QItem @click="$refs.weightDialog.dialogRef.show()" v-ripple clickable>
         <QItemSection avatar>
             <QIcon name="weight" />
         </QItemSection>

From 1e16e3312e3e22d0c6170195acd9c3e4670e5b5a Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Mon, 28 Oct 2024 16:21:00 +0100
Subject: [PATCH 119/150] fix: refs #8078 improve cy command

---
 src/components/ui/CardDescriptor.vue                     | 1 +
 test/cypress/integration/ticket/ticketDescriptor.spec.js | 4 ++++
 test/cypress/support/commands.js                         | 2 +-
 3 files changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/components/ui/CardDescriptor.vue b/src/components/ui/CardDescriptor.vue
index 7f35f0a28..83af77442 100644
--- a/src/components/ui/CardDescriptor.vue
+++ b/src/components/ui/CardDescriptor.vue
@@ -167,6 +167,7 @@ const toModule = computed(() =>
                     icon="more_vert"
                     round
                     size="md"
+                    data-cy="descriptor-more-opts"
                 >
                     <QTooltip>
                         {{ t('components.cardDescriptor.moreOptions') }}
diff --git a/test/cypress/integration/ticket/ticketDescriptor.spec.js b/test/cypress/integration/ticket/ticketDescriptor.spec.js
index 0ba2723a2..cd9f288f5 100644
--- a/test/cypress/integration/ticket/ticketDescriptor.spec.js
+++ b/test/cypress/integration/ticket/ticketDescriptor.spec.js
@@ -14,6 +14,8 @@ describe('Ticket descriptor', () => {
 
     it('should clone the ticket without warehouse', () => {
         cy.visit('/#/ticket/1/summary');
+        cy.intercept('GET', /\/api\/Tickets\/\d/).as('ticket');
+        cy.wait('@ticket');
         cy.openActionsDescriptor();
         cy.contains(listItem, toCloneOpt).click();
         cy.clickConfirm();
@@ -28,6 +30,8 @@ describe('Ticket descriptor', () => {
 
     it('should set the weight of the ticket', () => {
         cy.visit('/#/ticket/10/summary');
+        cy.intercept('GET', /\/api\/Tickets\/\d/).as('ticket');
+        cy.wait('@ticket');
         cy.openActionsDescriptor();
         cy.contains(listItem, setWeightOpt).click();
         cy.intercept('POST', /\/api\/Tickets\/\d+\/setWeight/).as('weight');
diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js
index 76bdefd27..1e9ca93b5 100755
--- a/test/cypress/support/commands.js
+++ b/test/cypress/support/commands.js
@@ -261,7 +261,7 @@ Cypress.Commands.add('openActionDescriptor', (opt) => {
 });
 
 Cypress.Commands.add('openActionsDescriptor', () => {
-    cy.get('.header > :nth-child(3) > .q-btn__content > .q-icon').click();
+    cy.get('[data-cy="descriptor-more-opts"]').click();
 });
 
 Cypress.Commands.add('openUserPanel', () => {

From 0de4dfd4f8bcf5f55a56a48c5a89fa8f702745e9 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Tue, 29 Oct 2024 02:15:02 +0100
Subject: [PATCH 120/150] feat: apply changes for customerModule

---
 src/components/ui/VnLinkMail.vue              |  16 +++
 src/pages/Customer/Card/CustomerAddress.vue   |   2 +-
 src/pages/Customer/Card/CustomerBalance.vue   | 120 +++++++++++-------
 src/pages/Customer/Card/CustomerCredits.vue   |   4 +-
 .../Customer/Card/CustomerDescriptor.vue      |  38 +++++-
 .../Customer/Card/CustomerDescriptorMenu.vue  |  28 ----
 .../Customer/Card/CustomerFiscalData.vue      |   7 +-
 src/pages/Customer/Card/CustomerGreuges.vue   |   5 +
 .../Customer/Card/CustomerRecoveries.vue      |   1 +
 src/pages/Customer/Card/CustomerSummary.vue   |  26 +++-
 src/pages/Customer/CustomerList.vue           |   6 +-
 .../components/CustomerAddressCreate.vue      |  15 ++-
 .../components/CustomerNewPayment.vue         |  10 +-
 .../components/CustomerSummaryTable.vue       |  14 +-
 .../Customer/composables/getClientRisk.js     |  12 ++
 .../integration/client/clientList.spec.js     |  19 ++-
 test/cypress/support/commands.js              |  33 +++--
 17 files changed, 251 insertions(+), 105 deletions(-)
 create mode 100644 src/components/ui/VnLinkMail.vue
 create mode 100644 src/pages/Customer/composables/getClientRisk.js

diff --git a/src/components/ui/VnLinkMail.vue b/src/components/ui/VnLinkMail.vue
new file mode 100644
index 000000000..a54f463f5
--- /dev/null
+++ b/src/components/ui/VnLinkMail.vue
@@ -0,0 +1,16 @@
+<script setup>
+defineProps({ email: { type: [String], default: null } });
+</script>
+<template>
+    <QBtn
+        v-if="email"
+        flat
+        round
+        icon="email"
+        size="sm"
+        color="primary"
+        padding="none"
+        :href="`mailto:${email}`"
+        @click.stop
+    />
+</template>
diff --git a/src/pages/Customer/Card/CustomerAddress.vue b/src/pages/Customer/Card/CustomerAddress.vue
index 166c33e1a..294e49cc8 100644
--- a/src/pages/Customer/Card/CustomerAddress.vue
+++ b/src/pages/Customer/Card/CustomerAddress.vue
@@ -27,7 +27,7 @@ const addressFilter = {
         'isLogifloraAllowed',
         'postalCode',
     ],
-    order: ['isDefaultAddress DESC', 'isActive DESC', 'nickname ASC'],
+    order: ['isDefaultAddress DESC', 'isActive DESC', 'id DESC', 'nickname ASC'],
     include: [
         {
             relation: 'observations',
diff --git a/src/pages/Customer/Card/CustomerBalance.vue b/src/pages/Customer/Card/CustomerBalance.vue
index f148194f8..f6fc3eed2 100644
--- a/src/pages/Customer/Card/CustomerBalance.vue
+++ b/src/pages/Customer/Card/CustomerBalance.vue
@@ -5,7 +5,7 @@ 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 { getClientRisk } from '../composables/getClientRisk';
 
 import { toCurrency, toDate, toDateHourMin } from 'src/filters';
 import { useState } from 'composables/useState';
@@ -16,7 +16,7 @@ import { useVnConfirm } from 'composables/useVnConfirm';
 import VnTable from 'components/VnTable/VnTable.vue';
 import VnInput from 'components/common/VnInput.vue';
 import VnSubToolbar from 'components/ui/VnSubToolbar.vue';
-import VnSelect from 'src/components/common/VnSelect.vue';
+import VnFilter from 'components/VnTable/VnFilter.vue';
 
 import CustomerNewPayment from 'src/pages/Customer/components/CustomerNewPayment.vue';
 import InvoiceOutDescriptorProxy from 'src/pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue';
@@ -25,7 +25,7 @@ const { openConfirmationModal } = useVnConfirm();
 const { sendEmail, openReport } = usePrintService();
 const { t } = useI18n();
 const { hasAny } = useAcl();
-const currentBalance = ref({});
+
 const quasar = useQuasar();
 const route = useRoute();
 const state = useState();
@@ -33,28 +33,53 @@ const stateStore = useStateStore();
 const user = state.getUser();
 
 const clientRisk = ref([]);
-const companies = ref([]);
 const tableRef = ref();
-const companyId = ref(user.value.companyFk);
+const companyId = ref();
+const companyUser = ref(user.value.companyFk);
 const balances = ref([]);
 const vnFilterRef = ref({});
 const filter = computed(() => {
     return {
         clientId: route.params.id,
-        companyId: companyId.value ?? user.value.companyFk,
+        companyId: companyId.value ?? companyUser.value,
     };
 });
 
+const companyFilterColumn = {
+    align: 'left',
+    name: 'companyId',
+    label: t('Company'),
+    component: 'select',
+    attrs: {
+        url: 'Companies',
+        optionLabel: 'code',
+        optionValue: 'id',
+        sortBy: 'code',
+    },
+    columnFilter: {
+        event: {
+            remove: () => (companyId.value = null),
+            'update:modelValue': (newCompanyFk) => {
+                if (!newCompanyFk) return;
+                vnFilterRef.value.addFilter(newCompanyFk);
+                companyUser.value = newCompanyFk;
+            },
+            blur: () => !companyId.value && (companyId.value = companyUser.value),
+        },
+    },
+    visible: false,
+};
+
 const columns = computed(() => [
     {
-        align: 'right',
+        align: 'left',
         name: 'payed',
         label: t('Date'),
         format: ({ payed }) => toDate(payed),
         cardVisible: true,
     },
     {
-        align: 'right',
+        align: 'left',
         name: 'created',
         label: t('Creation date'),
         format: ({ created }) => toDateHourMin(created),
@@ -65,7 +90,12 @@ const columns = computed(() => [
         label: t('Employee'),
         columnField: {
             component: 'userLink',
-            attrs: ({ row }) => ({ workerId: row.workerFk, name: row.userName }),
+            attrs: ({ row }) => {
+                return {
+                    workerId: row.workerFk,
+                    name: row.userName,
+                };
+            },
         },
         cardVisible: true,
     },
@@ -77,13 +107,13 @@ const columns = computed(() => [
         class: 'extend',
     },
     {
-        align: 'right',
+        align: 'left',
         name: 'bankFk',
         label: t('Bank'),
         cardVisible: true,
     },
     {
-        align: 'right',
+        align: 'left',
         name: 'debit',
         label: t('Debit'),
         format: ({ debit }) => debit && toCurrency(debit),
@@ -136,20 +166,37 @@ const columns = computed(() => [
 
 onBeforeMount(() => {
     stateStore.rightDrawer = true;
+    companyId.value = companyUser.value;
 });
 
-async function getCurrentBalance(data) {
-    currentBalance.value[companyId.value] = {
-        amount: 0,
-        code: companies.value.find((c) => c.id === companyId.value)?.code,
+async function getClientRisks() {
+    const filter = {
+        where: { clientFk: route.params.id, companyFk: companyUser.value },
     };
+    const { data } = await getClientRisk(filter);
+    clientRisk.value = data;
+    return clientRisk.value;
+}
 
-    for (const balance of data) {
-        currentBalance.value[balance.companyFk] = {
-            code: balance.company.code,
-            amount: balance.amount,
-        };
+async function getCurrentBalance() {
+    const currentBalance = (await getClientRisks()).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);
     }
+    balances.value = data;
 }
 
 const showNewPaymentDialog = () => {
@@ -169,43 +216,25 @@ const showBalancePdf = ({ id }) => {
 </script>
 
 <template>
-    <FetchData
-        url="Companies"
-        auto-load
-        @on-fetch="(data) => (companies = data)"
-    ></FetchData>
-    <FetchData
-        v-if="companies.length > 0"
-        url="clientRisks"
-        :filter="{
-            include: { relation: 'company', scope: { fields: ['code'] } },
-            where: { clientFk: route.params.id },
-        }"
-        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">
-                    {{ currentBalance[companyId]?.code }}:
-                    {{ toCurrency(currentBalance[companyId]?.amount) }}
+                <div class="row justify-center" v-if="clientRisk?.length">
+                    {{ clientRisk[0].company.code }}:
+                    {{ toCurrency(clientRisk[0].amount) }}
                 </div>
             </div>
         </template>
         <template #st-actions>
             <div>
-                <VnSelect
-                    :label="t('Company')"
+                <VnFilter
                     ref="vnFilterRef"
                     v-model="companyId"
                     data-key="CustomerBalance"
-                    :options="companies"
-                    option-label="code"
-                    option-value="id"
-                ></VnSelect>
+                    :column="companyFilterColumn"
+                    search-url="balance"
+                />
             </div>
         </template>
     </VnSubToolbar>
@@ -219,6 +248,7 @@ const showBalancePdf = ({ id }) => {
         :right-search="false"
         :is-editable="false"
         :column-search="false"
+        @on-fetch="onFetch"
         :disable-option="{ card: true }"
         auto-load
     >
diff --git a/src/pages/Customer/Card/CustomerCredits.vue b/src/pages/Customer/Card/CustomerCredits.vue
index 377d95412..1fa7047e5 100644
--- a/src/pages/Customer/Card/CustomerCredits.vue
+++ b/src/pages/Customer/Card/CustomerCredits.vue
@@ -49,7 +49,9 @@ const columns = computed(() => [
         name: 'credit',
         create: true,
         visible: false,
-        attrs: {
+        columnCreate: {
+            component: 'number',
+            required: true,
             autofocus: true,
         },
     },
diff --git a/src/pages/Customer/Card/CustomerDescriptor.vue b/src/pages/Customer/Card/CustomerDescriptor.vue
index 43103bd68..40166aefe 100644
--- a/src/pages/Customer/Card/CustomerDescriptor.vue
+++ b/src/pages/Customer/Card/CustomerDescriptor.vue
@@ -150,7 +150,7 @@ const setData = (entity) => (data.value = useCardDescription(entity?.name, entit
             </QCardActions>
         </template>
         <template #actions="{ entity }">
-            <QCardActions class="flex justify-center">
+            <QCardActions class="flex justify-center" style="padding-inline: 0">
                 <QBtn
                     :to="{
                         name: 'TicketList',
@@ -168,6 +168,23 @@ const setData = (entity) => (data.value = useCardDescription(entity?.name, entit
                 >
                     <QTooltip>{{ t('Customer ticket list') }}</QTooltip>
                 </QBtn>
+                <QBtn
+                    :to="{
+                        name: 'TicketList',
+                        query: {
+                            table: JSON.stringify({
+                                clientFk: entity.id,
+                            }),
+                            createForm: JSON.stringify({ clientId: entity.id }),
+                        },
+                    }"
+                    size="md"
+                    color="primary"
+                    target="_blank"
+                    icon="vn:ticketAdd"
+                >
+                    <QTooltip>{{ t('New ticket') }}</QTooltip>
+                </QBtn>
                 <QBtn
                     :to="{
                         name: 'InvoiceOutList',
@@ -179,6 +196,23 @@ const setData = (entity) => (data.value = useCardDescription(entity?.name, entit
                 >
                     <QTooltip>{{ t('Customer invoice out list') }}</QTooltip>
                 </QBtn>
+                <QBtn
+                    :to="{
+                        name: 'OrderList',
+                        query: {
+                            table: JSON.stringify({
+                                clientFk: entity.id,
+                            }),
+                            createForm: JSON.stringify({ clientFk: entity.id }),
+                        },
+                    }"
+                    size="md"
+                    target="_blank"
+                    icon="vn:basketadd"
+                    color="primary"
+                >
+                    <QTooltip>{{ t('New order') }}</QTooltip>
+                </QBtn>
                 <QBtn
                     :to="{
                         name: 'AccountSummary',
@@ -215,6 +249,8 @@ es:
     Go to module index: Ir al índice del módulo
     Customer ticket list: Listado de tickets del cliente
     Customer invoice out list: Listado de facturas del cliente
+    New order: Nuevo pedido
+    New ticket: Nuevo ticket
     Go to user: Ir al usuario
     Go to supplier: Ir al proveedor
     Customer unpaid: Cliente impago
diff --git a/src/pages/Customer/Card/CustomerDescriptorMenu.vue b/src/pages/Customer/Card/CustomerDescriptorMenu.vue
index 89b10a4fe..a0fbc7f7b 100644
--- a/src/pages/Customer/Card/CustomerDescriptorMenu.vue
+++ b/src/pages/Customer/Card/CustomerDescriptorMenu.vue
@@ -8,9 +8,6 @@ import { useQuasar } from 'quasar';
 import useNotify from 'src/composables/useNotify';
 
 import VnSmsDialog from 'src/components/common/VnSmsDialog.vue';
-import TicketCreateDialog from 'src/pages/Ticket/TicketCreateDialog.vue';
-import OrderCreateDialog from 'src/pages/Order/Card/OrderCreateDialog.vue';
-import { ref } from 'vue';
 
 const $props = defineProps({
     customer: {
@@ -43,34 +40,9 @@ const sendSms = async (payload) => {
         notify(error.message, 'positive');
     }
 };
-
-const ticketCreateFormDialog = ref(null);
-const openTicketCreateForm = () => {
-    ticketCreateFormDialog.value.show();
-};
-const orderCreateFormDialog = ref(null);
-const openOrderCreateForm = () => {
-    orderCreateFormDialog.value.show();
-};
 </script>
 
 <template>
-    <QItem v-ripple clickable @click="openTicketCreateForm()">
-        <QItemSection>
-            {{ t('globals.pageTitles.createTicket') }}
-            <QDialog ref="ticketCreateFormDialog">
-                <TicketCreateDialog />
-            </QDialog>
-        </QItemSection>
-    </QItem>
-    <QItem v-ripple clickable @click="openOrderCreateForm()">
-        <QItemSection>
-            {{ t('globals.pageTitles.createOrder') }}
-            <QDialog ref="orderCreateFormDialog">
-                <OrderCreateDialog :client-fk="customer.id" />
-            </QDialog>
-        </QItemSection>
-    </QItem>
     <QItem v-ripple clickable>
         <QItemSection @click="showSmsDialog()">{{ t('Send SMS') }}</QItemSection>
     </QItem>
diff --git a/src/pages/Customer/Card/CustomerFiscalData.vue b/src/pages/Customer/Card/CustomerFiscalData.vue
index 1f5775715..6c5086149 100644
--- a/src/pages/Customer/Card/CustomerFiscalData.vue
+++ b/src/pages/Customer/Card/CustomerFiscalData.vue
@@ -53,11 +53,11 @@ function handleLocation(data, location) {
                         </QIcon>
                     </template>
                 </VnInput>
-                <VnInput :label="t('Tax number')" clearable v-model="data.fi" />
+                <VnInput :label="t('Tax number')" clearable v-model="data.fi" required />
             </VnRow>
 
             <VnRow>
-                <VnInput :label="t('Street')" clearable v-model="data.street" />
+                <VnInput :label="t('Street')" clearable v-model="data.street" required />
             </VnRow>
 
             <VnRow>
@@ -68,6 +68,7 @@ function handleLocation(data, location) {
                     option-label="vat"
                     option-value="id"
                     v-model="data.sageTaxTypeFk"
+                    :required="data.isTaxDataChecked"
                 />
                 <VnSelect
                     :label="t('Sage transaction type')"
@@ -76,6 +77,7 @@ function handleLocation(data, location) {
                     option-label="transaction"
                     option-value="id"
                     v-model="data.sageTransactionTypeFk"
+                    :required="data.isTaxDataChecked"
                 >
                     <template #option="scope">
                         <QItem v-bind="scope.itemProps">
@@ -96,6 +98,7 @@ function handleLocation(data, location) {
                     :roles-allowed-to-create="['deliveryAssistant', 'administrative']"
                     :acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]"
                     :location="data"
+                    :required="true"
                     @update:model-value="(location) => handleLocation(data, location)"
                 />
             </VnRow>
diff --git a/src/pages/Customer/Card/CustomerGreuges.vue b/src/pages/Customer/Card/CustomerGreuges.vue
index 1d8b8585f..dcf297d12 100644
--- a/src/pages/Customer/Card/CustomerGreuges.vue
+++ b/src/pages/Customer/Card/CustomerGreuges.vue
@@ -80,6 +80,11 @@ const columns = computed(() => [
         align: 'left',
         name: 'amount',
         label: t('Amount'),
+        columnCreate: {
+            component: 'number',
+            autofocus: true,
+            required: true,
+        },
         format: ({ amount }) => toCurrency(amount),
         create: true,
     },
diff --git a/src/pages/Customer/Card/CustomerRecoveries.vue b/src/pages/Customer/Card/CustomerRecoveries.vue
index 48576ca20..3a8cffff8 100644
--- a/src/pages/Customer/Card/CustomerRecoveries.vue
+++ b/src/pages/Customer/Card/CustomerRecoveries.vue
@@ -56,6 +56,7 @@ const columns = computed(() => [
         label: t('Period'),
         create: true,
         ...componentColumn('number'),
+        required: true,
     },
     {
         align: 'left',
diff --git a/src/pages/Customer/Card/CustomerSummary.vue b/src/pages/Customer/Card/CustomerSummary.vue
index 8b5f0f2c2..8e41119ef 100644
--- a/src/pages/Customer/Card/CustomerSummary.vue
+++ b/src/pages/Customer/Card/CustomerSummary.vue
@@ -1,13 +1,15 @@
 <script setup>
-import { computed, ref } from 'vue';
+import { computed, ref, onMounted } from 'vue';
 import { useRoute } from 'vue-router';
 import { useI18n } from 'vue-i18n';
 import VnUserLink from 'src/components/ui/VnUserLink.vue';
 
 import { toCurrency, toPercentage, toDate } from 'src/filters';
 import CardSummary from 'components/ui/CardSummary.vue';
+import { getUrl } from 'src/composables/getUrl';
 import VnLv from 'src/components/ui/VnLv.vue';
 import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue';
+import VnLinkMail from 'src/components/ui/VnLinkMail.vue';
 import CustomerSummaryTable from 'src/pages/Customer/components/CustomerSummaryTable.vue';
 import VnTitle from 'src/components/common/VnTitle.vue';
 import VnRow from 'src/components/ui/VnRow.vue';
@@ -25,6 +27,11 @@ const $props = defineProps({
 const entityId = computed(() => $props.id || route.params.id);
 const customer = computed(() => summary.value.entity);
 const summary = ref();
+const clientUrl = ref();
+
+onMounted(async () => {
+    clientUrl.value = (await getUrl('client/')) + entityId.value + '/';
+});
 
 const balanceDue = computed(() => {
     return (
@@ -67,6 +74,7 @@ const sumRisk = ({ clientRisks }) => {
         ref="summary"
         :url="`Clients/${entityId}/summary`"
         data-key="CustomerSummary"
+        module-name="Customer"
     >
         <template #body="{ entity }">
             <QCard class="vn-one">
@@ -89,7 +97,11 @@ const sumRisk = ({ clientRisks }) => {
                         <VnLinkPhone :phone-number="entity.mobile" />
                     </template>
                 </VnLv>
-                <VnLv :label="t('customer.summary.email')" :value="entity.email" copy />
+                <VnLv :value="entity.email" copy
+                    ><template #label>
+                        {{ t('customer.summary.email') }}
+                        <VnLinkMail email="entity.email"></VnLinkMail> </template
+                ></VnLv>
                 <VnLv
                     :label="t('customer.summary.salesPerson')"
                     :value="entity?.salesPersonUser?.name"
@@ -166,7 +178,7 @@ const sumRisk = ({ clientRisks }) => {
             <QCard class="vn-one">
                 <VnTitle
                     :url="`#/customer/${entityId}/billing-data`"
-                    :text="t('customer.summary.payMethodFk')"
+                    :text="t('customer.summary.billingData')"
                 />
                 <VnLv
                     :label="t('customer.summary.payMethod')"
@@ -222,6 +234,7 @@ const sumRisk = ({ clientRisks }) => {
             </QCard>
             <QCard class="vn-one" v-if="entity.account">
                 <VnTitle
+                    target="_blank"
                     :url="`${grafanaUrl}/d/adjlxzv5yjt34d/analisis-de-clientes-7c-crm?orgId=1&var-clientFk=${entityId}`"
                     :text="t('customer.summary.businessData')"
                     icon="vn:grafana"
@@ -235,6 +248,7 @@ const sumRisk = ({ clientRisks }) => {
                     :value="toCurrency(entity?.mana?.mana)"
                 />
                 <VnLv
+                    v-if="entity.claimsRatio"
                     :label="t('customer.summary.priceIncreasingRate')"
                     :value="toPercentage(priceIncreasingRate)"
                 />
@@ -243,12 +257,14 @@ const sumRisk = ({ clientRisks }) => {
                     :value="toCurrency(entity?.averageInvoiced?.invoiced)"
                 />
                 <VnLv
+                    v-if="entity.claimsRatio"
                     :label="t('customer.summary.claimRate')"
                     :value="toPercentage(claimRate)"
                 />
             </QCard>
             <QCard class="vn-one" v-if="entity.account">
                 <VnTitle
+                    target="_blank"
                     :url="`${grafanaUrl}/d/40buzE4Vk/comportamiento-pagos-clientes?orgId=1&var-clientFk=${entityId}`"
                     :text="t('customer.summary.payMethodFk')"
                     icon="vn:grafana"
@@ -268,10 +284,12 @@ const sumRisk = ({ clientRisks }) => {
                 />
 
                 <VnLv
+                    v-if="entity.creditInsurance"
                     :label="t('customer.summary.securedCredit')"
                     :value="toCurrency(entity.creditInsurance)"
                     :info="t('customer.summary.securedCreditInfo')"
                 />
+
                 <VnLv
                     :label="t('customer.summary.balance')"
                     :value="toCurrency(sumRisk(entity)) || toCurrency(0)"
@@ -301,7 +319,7 @@ const sumRisk = ({ clientRisks }) => {
                     :value="entity.recommendedCredit"
                 />
             </QCard>
-            <QCard>
+            <QCard class="vn-one">
                 <VnTitle :text="t('Latest tickets')" />
                 <CustomerSummaryTable />
             </QCard>
diff --git a/src/pages/Customer/CustomerList.vue b/src/pages/Customer/CustomerList.vue
index 63f5149e8..815ec57fa 100644
--- a/src/pages/Customer/CustomerList.vue
+++ b/src/pages/Customer/CustomerList.vue
@@ -68,7 +68,6 @@ const columns = computed(() => [
             fields: ['id', 'name'],
             where: { role: 'salesPerson' },
             optionFilter: 'firstName',
-            useLike: false,
         },
         create: false,
         columnField: {
@@ -429,9 +428,10 @@ function handleLocation(data, location) {
                 :params="{
                     departmentCodes: ['VT', 'shopping'],
                 }"
-                :fields="['id', 'nickname']"
+                :fields="['id', 'nickname', 'code']"
                 sort-by="nickname ASC"
-                :use-like="false"
+                option-label="nickname"
+                option-value="id"
                 emit-value
                 auto-load
             >
diff --git a/src/pages/Customer/components/CustomerAddressCreate.vue b/src/pages/Customer/components/CustomerAddressCreate.vue
index 659114744..e3fef8e5f 100644
--- a/src/pages/Customer/components/CustomerAddressCreate.vue
+++ b/src/pages/Customer/components/CustomerAddressCreate.vue
@@ -85,15 +85,26 @@ function handleLocation(data, location) {
             <QCheckbox :label="t('Default')" v-model="data.isDefaultAddress" />
 
             <VnRow>
-                <VnInput :label="t('Consignee')" clearable v-model="data.nickname" />
+                <VnInput
+                    :label="t('Consignee')"
+                    required
+                    clearable
+                    v-model="data.nickname"
+                />
 
-                <VnInput :label="t('Street address')" clearable v-model="data.street" />
+                <VnInput
+                    :label="t('Street address')"
+                    clearable
+                    v-model="data.street"
+                    required
+                />
             </VnRow>
 
             <VnLocation
                 :rules="validate('Worker.postcode')"
                 :acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]"
                 v-model="data.location"
+                :required="true"
                 @update:model-value="(location) => handleLocation(data, location)"
             />
 
diff --git a/src/pages/Customer/components/CustomerNewPayment.vue b/src/pages/Customer/components/CustomerNewPayment.vue
index 16dd28767..291f28642 100644
--- a/src/pages/Customer/components/CustomerNewPayment.vue
+++ b/src/pages/Customer/components/CustomerNewPayment.vue
@@ -3,7 +3,7 @@ import { onBeforeMount, reactive, ref } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useRoute } from 'vue-router';
 import axios from 'axios';
-
+import { getClientRisk } from '../composables/getClientRisk';
 import { useDialogPluginComponent } from 'quasar';
 
 import { usePrintService } from 'composables/usePrintService';
@@ -158,9 +158,7 @@ async function getAmountPaid() {
         },
     };
 
-    const { data } = await axios(`ClientRisks`, {
-        params: { filter: JSON.stringify(filter) },
-    });
+    const { data } = await getClientRisk(filter);
     initialData.amountPaid = (data?.length && data[0].amount) || undefined;
 }
 </script>
@@ -241,7 +239,7 @@ async function getAmountPaid() {
                             </QItem>
                         </template>
                     </VnSelect>
-                    <VnInput
+                    <VnInputNumber
                         :label="t('Amount')"
                         :required="true"
                         @update:model-value="calculateFromAmount($event)"
@@ -254,7 +252,7 @@ async function getAmountPaid() {
                         {{ t('Compensation') }}
                     </div>
                     <VnRow>
-                        <VnInput
+                        <VnInputNumber
                             :label="t('Compensation account')"
                             clearable
                             v-model="data.compensationAccount"
diff --git a/src/pages/Customer/components/CustomerSummaryTable.vue b/src/pages/Customer/components/CustomerSummaryTable.vue
index e9bb36be7..745cbf29e 100644
--- a/src/pages/Customer/components/CustomerSummaryTable.vue
+++ b/src/pages/Customer/components/CustomerSummaryTable.vue
@@ -63,7 +63,7 @@ const columns = computed(() => [
     },
     {
         align: 'left',
-        format: (row) => row.agencyMode.name,
+        format: (row) => dashIfEmpty(row.agencyMode?.name),
         columnClass: 'expand',
         label: t('Agency'),
     },
@@ -111,7 +111,11 @@ const columns = computed(() => [
             {
                 title: t('customer.summary.goToLines'),
                 icon: 'vn:lines',
-                action: ({ id }) => router.push({ params: { id }, name: 'TicketSale' }),
+                action: ({ id }) =>
+                    window.open(
+                        router.resolve({ params: { id }, name: 'TicketSale' }).href,
+                        '_blank'
+                    ),
                 isPrimary: true,
             },
             {
@@ -150,6 +154,8 @@ const setShippedColor = (date) => {
     if (difference == 0) return 'warning';
     if (difference < 0) return 'success';
 };
+const rowClick = ({ id }) =>
+    window.open(router.resolve({ params: { id }, name: 'TicketSummary' }).href, '_blank');
 
 const getItemPackagingType = (ticketSales) => {
     if (!ticketSales?.length) return '-';
@@ -177,13 +183,15 @@ const getItemPackagingType = (ticketSales) => {
         :column-search="false"
         url="Tickets"
         :columns="columns"
-        search-url="tickets"
+        append-params="false"
         :without-header="true"
         auto-load
+        :row-click="rowClick"
         order="shipped DESC, id"
         :disable-option="{ card: true, table: true }"
         class="full-width"
         :disable-infinite-scroll="true"
+        search-url="tickets"
     >
         <template #column-nickname="{ row }">
             <span class="link">
diff --git a/src/pages/Customer/composables/getClientRisk.js b/src/pages/Customer/composables/getClientRisk.js
new file mode 100644
index 000000000..ebaf545ee
--- /dev/null
+++ b/src/pages/Customer/composables/getClientRisk.js
@@ -0,0 +1,12 @@
+import axios from 'axios';
+
+export async function getClientRisk(_filter) {
+    const filter = {
+        ..._filter,
+        include: { relation: 'company', scope: { fields: ['code'] } },
+    };
+
+    return await axios(`ClientRisks`, {
+        params: { filter: JSON.stringify(filter) },
+    });
+}
diff --git a/test/cypress/integration/client/clientList.spec.js b/test/cypress/integration/client/clientList.spec.js
index 22bca15ac..93e53b9f6 100644
--- a/test/cypress/integration/client/clientList.spec.js
+++ b/test/cypress/integration/client/clientList.spec.js
@@ -28,7 +28,7 @@ describe('Client list', () => {
 
         cy.get('.q-mt-lg > .q-btn--standard').click();
 
-        cy.checkNotification('Data created');
+        cy.checkNotification('created');
         cy.url().should('include', '/summary');
     });
     it('Client list search client', () => {
@@ -43,4 +43,21 @@ describe('Client list', () => {
             cy.url().should('include', `/customer/${id}/summary`);
         });
     });
+
+    it('Client founded create ticket', () => {
+        const search = 'Jessica Jones';
+        cy.searchByLabel('Name', search);
+        cy.clickButtonsDescriptor(2);
+        cy.waitForElement('#formModel');
+        cy.waitForElement('.q-form');
+        cy.checkValueForm(1, search);
+    });
+    it('Client founded create order', () => {
+        const search = 'Jessica Jones';
+        cy.searchByLabel('Name', search);
+        cy.clickButtonsDescriptor(4);
+        cy.waitForElement('#formModel');
+        cy.waitForElement('.q-form');
+        cy.checkValueForm(2, search);
+    });
 });
diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js
index 76bdefd27..6fea4559a 100755
--- a/test/cypress/support/commands.js
+++ b/test/cypress/support/commands.js
@@ -264,6 +264,12 @@ Cypress.Commands.add('openActionsDescriptor', () => {
     cy.get('.header > :nth-child(3) > .q-btn__content > .q-icon').click();
 });
 
+Cypress.Commands.add('clickButtonsDescriptor', (id) => {
+    cy.get(`.actions > .q-card__actions> .q-btn:nth-child(${id})`)
+        .invoke('removeAttr', 'target')
+        .click();
+});
+
 Cypress.Commands.add('openUserPanel', () => {
     cy.get(
         '.column > .q-avatar > .q-avatar__content > .q-img > .q-img__container > .q-img__image'
@@ -274,14 +280,25 @@ Cypress.Commands.add('openActions', (row) => {
     cy.get('tbody > tr').eq(row).find('.actions > .q-btn').click();
 });
 
-Cypress.Commands.add('checkNotification', (text) => {
-    cy.get('.q-notification')
-        .should('be.visible')
-        .last()
-        .then(($lastNotification) => {
-            if (!Cypress.$($lastNotification).text().includes(text))
-                throw new Error(`Notification not found: "${text}"`);
-        });
+Cypress.Commands.add('checkNotification', (type) => {
+    const values = {
+        created: 'Data created',
+        updated: 'Data saved',
+        deleted: 'Data deleted',
+    };
+    cy.get('.q-notification__message').should('have.text', values[type]);
+});
+
+Cypress.Commands.add('checkValueForm', (id, search) => {
+    cy.get(
+        `.grid-create > :nth-child(${id}) > .q-field__inner>.q-field__control> .q-field__control-container>.q-field__native >.q-field__input`
+    ).should('have.value', search);
+});
+
+Cypress.Commands.add('checkValueSelectForm', (id, search) => {
+    cy.get(
+        `.grid-create > :nth-child(${id}) > .q-field > .q-field__inner > .q-field__control > .q-field__control-container>.q-field__native>.q-field__input`
+    ).should('have.value', search);
 });
 
 Cypress.Commands.add('searchByLabel', (label, value) => {

From 0b2c404ab336d4e0b663b4cdc41b3fb0b772b7a8 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Tue, 29 Oct 2024 02:15:21 +0100
Subject: [PATCH 121/150] perf: minor bugs detected

---
 src/components/VnTable/VnTable.vue |  4 ++++
 src/components/common/VnTitle.vue  |  2 +-
 src/components/ui/CardSummary.vue  | 20 +++-----------------
 src/composables/useArrayData.js    |  7 ++++---
 src/filters/index.js               |  2 ++
 src/filters/isDialogOpened.js      |  3 +++
 6 files changed, 17 insertions(+), 21 deletions(-)
 create mode 100644 src/filters/isDialogOpened.js

diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index c1680bf13..cdf450966 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -73,6 +73,10 @@ const $props = defineProps({
         type: Boolean,
         default: false,
     },
+    appendParams: {
+        type: Boolean,
+        default: true,
+    },
     hasSubToolbar: {
         type: Boolean,
         default: null,
diff --git a/src/components/common/VnTitle.vue b/src/components/common/VnTitle.vue
index 1fbd43972..89dd8cd0c 100644
--- a/src/components/common/VnTitle.vue
+++ b/src/components/common/VnTitle.vue
@@ -8,7 +8,7 @@ defineProps({
 <template>
     <div :class="$q.screen.gt.md ? 'q-pb-lg' : 'q-pb-md'">
         <div class="header-link" :style="{ cursor: url ? 'pointer' : 'default' }">
-            <a :href="url" :class="url ? 'link' : 'color-vn-text'">
+            <a :href="url" :class="url ? 'link' : 'color-vn-text'" v-bind="$attrs">
                 {{ text }}
                 <QIcon v-if="url" :name="icon" />
             </a>
diff --git a/src/components/ui/CardSummary.vue b/src/components/ui/CardSummary.vue
index 11dcbee3b..f469aa799 100644
--- a/src/components/ui/CardSummary.vue
+++ b/src/components/ui/CardSummary.vue
@@ -4,6 +4,7 @@ import { useRoute } from 'vue-router';
 import SkeletonSummary from 'components/ui/SkeletonSummary.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
 import { useArrayData } from 'src/composables/useArrayData';
+import { isDialogOpened } from 'src/filters';
 
 const props = defineProps({
     url: {
@@ -58,22 +59,6 @@ async function fetch() {
     emit('onFetch', Array.isArray(data) ? data[0] : data);
     isLoading.value = false;
 }
-
-const showRedirectToSummaryIcon = computed(() => {
-    const exist = existSummary(route.matched);
-    return !isSummary.value && route.meta.moduleName && exist;
-});
-
-function existSummary(routes) {
-    const hasSummary = routes.some((r) => r.name === `${route.meta.moduleName}Summary`);
-    if (hasSummary) return hasSummary;
-    for (const current of routes) {
-        if (current.path != '/' && current.children) {
-            const exist = existSummary(current.children);
-            if (exist) return exist;
-        }
-    }
-}
 </script>
 
 <template>
@@ -84,7 +69,7 @@ function existSummary(routes) {
                 <div class="summaryHeader bg-primary q-pa-sm text-weight-bolder">
                     <slot name="header-left">
                         <router-link
-                            v-if="showRedirectToSummaryIcon"
+                            v-if="isDialogOpened()"
                             class="header link"
                             :to="{
                                 name: `${moduleName ?? route.meta.moduleName}Summary`,
@@ -118,6 +103,7 @@ function existSummary(routes) {
 
 .cardSummary {
     width: 100%;
+    max-height: 70vh;
     .summaryHeader {
         text-align: center;
         font-size: 20px;
diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js
index a2eaa649a..0a060e4c6 100644
--- a/src/composables/useArrayData.js
+++ b/src/composables/useArrayData.js
@@ -3,6 +3,7 @@ import { useRouter, useRoute } from 'vue-router';
 import axios from 'axios';
 import { useArrayDataStore } from 'stores/useArrayDataStore';
 import { buildFilter } from 'filters/filterPanel';
+import { isDialogOpened } from 'src/filters';
 
 const arrayDataStore = useArrayDataStore();
 
@@ -114,8 +115,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
             for (const row of response.data) store.data.push(row);
         } else {
             store.data = response.data;
-            if (!document.querySelectorAll('[role="dialog"][aria-modal="true"]').length)
-                updateRouter && updateStateParams();
+            if (!isDialogOpened()) updateRouter && updateStateParams();
         }
 
         store.isLoading = false;
@@ -249,7 +249,8 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
     function updateStateParams() {
         if (!route) return;
         const newUrl = { path: route.path, query: { ...(route.query ?? {}) } };
-        newUrl.query[store.searchUrl] = JSON.stringify(store.currentFilter);
+        if (store.appendParams)
+            newUrl.query[store.searchUrl] = JSON.stringify(store.currentFilter);
 
         if (store.navigate) {
             const { customRouteRedirectName, searchText } = store.navigate;
diff --git a/src/filters/index.js b/src/filters/index.js
index 5f08f19c7..ce5c44706 100644
--- a/src/filters/index.js
+++ b/src/filters/index.js
@@ -12,8 +12,10 @@ import dateRange from './dateRange';
 import toHour from './toHour';
 import dashOrCurrency from './dashOrCurrency';
 import getParamWhere from './getParamWhere';
+import isDialogOpened from './isDialogOpened';
 
 export {
+    isDialogOpened,
     toLowerCase,
     toLowerCamel,
     toDate,
diff --git a/src/filters/isDialogOpened.js b/src/filters/isDialogOpened.js
new file mode 100644
index 000000000..9d6f3895e
--- /dev/null
+++ b/src/filters/isDialogOpened.js
@@ -0,0 +1,3 @@
+export default function isDialogOpened(query = '[role="dialog"]') {
+    return document.querySelectorAll(query).length > 0;
+}

From 4b00ab877c9d1a8455e368087cd74dff85f1dab0 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Tue, 29 Oct 2024 02:26:35 +0100
Subject: [PATCH 122/150] perf: use required instead :required="true"

---
 src/components/common/VnInput.vue       | 10 +++++-----
 src/components/common/VnInputDate.vue   |  9 +++++----
 src/components/common/VnInputNumber.vue |  7 ++++++-
 src/components/common/VnInputTime.vue   |  7 ++++---
 src/components/common/VnLocation.vue    | 14 ++++++++++++--
 src/components/common/VnSelect.vue      | 11 ++++++-----
 6 files changed, 38 insertions(+), 20 deletions(-)

diff --git a/src/components/common/VnInput.vue b/src/components/common/VnInput.vue
index 1246eedcd..4534ae4a0 100644
--- a/src/components/common/VnInput.vue
+++ b/src/components/common/VnInput.vue
@@ -2,6 +2,7 @@
 import { computed, ref } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useValidator } from 'src/composables/useValidator';
+import { useAttrs } from 'vue';
 
 const emit = defineEmits([
     'update:modelValue',
@@ -29,10 +30,11 @@ const $props = defineProps({
     },
 });
 const { validations } = useValidator();
+const $attrs = useAttrs();
 
 const { t } = useI18n();
-const requiredFieldRule = (val) => validations().required($attrs.required, val);
-
+const isRequired = computed(() => Object.keys($attrs).includes('required'));
+const requiredFieldRule = (val) => validations().required(isRequired.value, val);
 const vnInputRef = ref(null);
 const value = computed({
     get() {
@@ -60,8 +62,6 @@ const focus = () => {
 defineExpose({
     focus,
 });
-import { useAttrs } from 'vue';
-const $attrs = useAttrs();
 
 const mixinRules = [
     requiredFieldRule,
@@ -85,7 +85,7 @@ const mixinRules = [
             v-model="value"
             v-bind="{ ...$attrs, ...styleAttrs }"
             :type="$attrs.type"
-            :class="{ required: $attrs.required }"
+            :class="{ required: isRequired }"
             @keyup.enter="emit('keyup.enter')"
             :clearable="false"
             :rules="mixinRules"
diff --git a/src/components/common/VnInputDate.vue b/src/components/common/VnInputDate.vue
index 1aa797ab7..87833f411 100644
--- a/src/components/common/VnInputDate.vue
+++ b/src/components/common/VnInputDate.vue
@@ -6,6 +6,8 @@ import { useAttrs } from 'vue';
 import VnDate from './VnDate.vue';
 
 const model = defineModel({ type: [String, Date] });
+const $attrs = useAttrs();
+const { t } = useI18n();
 const $props = defineProps({
     isOutlined: {
         type: Boolean,
@@ -19,15 +21,14 @@ const $props = defineProps({
 import { useValidator } from 'src/composables/useValidator';
 const { validations } = useValidator();
 
-const { t } = useI18n();
-const requiredFieldRule = (val) => validations().required($attrs.required, val);
+const isRequired = computed(() => Object.keys($attrs).includes('required'));
+const requiredFieldRule = (val) => validations().required(isRequired.value, val);
 const vnInputDateRef = ref(null);
 
 const dateFormat = 'DD/MM/YYYY';
 const isPopupOpen = ref();
 const hover = ref();
 const mask = ref();
-const $attrs = useAttrs();
 
 const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])];
 
@@ -104,7 +105,7 @@ const manageDate = (date) => {
             :mask="mask"
             placeholder="dd/mm/aaaa"
             v-bind="{ ...$attrs, ...styleAttrs }"
-            :class="{ required: $attrs.required }"
+            :class="{ required: isRequired }"
             :rules="mixinRules"
             :clearable="false"
             @click="isPopupOpen = true"
diff --git a/src/components/common/VnInputNumber.vue b/src/components/common/VnInputNumber.vue
index ef4bb7512..1cad6c245 100644
--- a/src/components/common/VnInputNumber.vue
+++ b/src/components/common/VnInputNumber.vue
@@ -1,8 +1,13 @@
 <script setup>
 import VnInput from 'src/components/common/VnInput.vue';
+import { ref } from 'vue';
+import { useAttrs } from 'vue';
+
 const model = defineModel({ type: [Number, String] });
+const $attrs = useAttrs();
+const step = ref($attrs.step || 0.01);
 </script>
 
 <template>
-    <VnInput v-bind="$attrs" v-model.number="model" type="number" />
+    <VnInput v-bind="$attrs" v-model.number="model" type="number" :step="step" />
 </template>
diff --git a/src/components/common/VnInputTime.vue b/src/components/common/VnInputTime.vue
index 6d69bc4a5..13e90e021 100644
--- a/src/components/common/VnInputTime.vue
+++ b/src/components/common/VnInputTime.vue
@@ -6,6 +6,7 @@ import { useValidator } from 'src/composables/useValidator';
 import VnTime from './VnTime.vue';
 
 const { validations } = useValidator();
+const { t } = useI18n();
 const $attrs = useAttrs();
 const model = defineModel({ type: String });
 const props = defineProps({
@@ -20,8 +21,8 @@ const props = defineProps({
 });
 const vnInputTimeRef = ref(null);
 const initialDate = ref(model.value ?? Date.vnNew());
-const { t } = useI18n();
-const requiredFieldRule = (val) => validations().required($attrs.required, val);
+const isRequired = computed(() => Object.keys($attrs).includes('required'));
+const requiredFieldRule = (val) => validations().required(isRequired.value, val);
 const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])];
 const dateFormat = 'HH:mm';
 const isPopupOpen = ref();
@@ -78,7 +79,7 @@ function dateToTime(newDate) {
             placeholder="--:--"
             v-model="formattedTime"
             v-bind="{ ...$attrs, ...styleAttrs }"
-            :class="{ required: $attrs.required }"
+            :class="{ required: isRequired }"
             style="min-width: 100px"
             :rules="mixinRules"
             @click="isPopupOpen = false"
diff --git a/src/components/common/VnLocation.vue b/src/components/common/VnLocation.vue
index 5f94c466a..7c6e46fdd 100644
--- a/src/components/common/VnLocation.vue
+++ b/src/components/common/VnLocation.vue
@@ -2,10 +2,14 @@
 import CreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
 import VnSelectDialog from 'components/common/VnSelectDialog.vue';
 import { useI18n } from 'vue-i18n';
-import { ref } from 'vue';
+import { computed, ref } from 'vue';
 const { t } = useI18n();
 const emit = defineEmits(['update:model-value', 'update:options']);
+const { validations } = useValidator();
 
+import { useAttrs } from 'vue';
+import { useValidator } from 'src/composables/useValidator';
+const $attrs = useAttrs();
 const props = defineProps({
     location: {
         type: Object,
@@ -13,6 +17,10 @@ const props = defineProps({
     },
 });
 
+const isRequired = computed(() => Object.keys($attrs).includes('required'));
+const requiredFieldRule = (val) => validations().required(isRequired.value, val);
+
+const mixinRules = [requiredFieldRule];
 const locationProperties = [
     'postcode',
     (obj) =>
@@ -69,11 +77,13 @@ const handleModelValue = (data) => {
         :label="t('Location')"
         :placeholder="t('search_by_postalcode')"
         :input-debounce="300"
-        :class="{ required: $attrs.required }"
+        :class="{ required: isRequired }"
         v-bind="$attrs"
         clearable
         :emit-value="false"
         :tooltip="t('Create new location')"
+        :rules="mixinRules"
+        :lazy-rules="true"
     >
         <template #form>
             <CreateNewPostcode
diff --git a/src/components/common/VnSelect.vue b/src/components/common/VnSelect.vue
index b0aa648c1..4a4730784 100644
--- a/src/components/common/VnSelect.vue
+++ b/src/components/common/VnSelect.vue
@@ -4,7 +4,8 @@ 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 $attrs = useAttrs();
+const { t } = useI18n();
 const $props = defineProps({
     modelValue: {
         type: [String, Number, Object],
@@ -88,9 +89,9 @@ const $props = defineProps({
     },
 });
 const { validations } = useValidator();
-const requiredFieldRule = (val) => validations().required($attrs.required, val);
-const $attrs = useAttrs();
-const { t } = useI18n();
+
+const isRequired = computed(() => Object.keys($attrs).includes('required'));
+const requiredFieldRule = (val) => validations().required(isRequired.value, val);
 const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])];
 const { optionLabel, optionValue, optionFilter, optionFilterValue, options, modelValue } =
     toRefs($props);
@@ -257,7 +258,7 @@ defineExpose({ opts: myOptions });
         :fill-input="nullishToTrue($attrs['fill-input'])"
         ref="vnSelectRef"
         lazy-rules
-        :class="{ required: $attrs.required }"
+        :class="{ required: isRequired }"
         :rules="mixinRules"
         virtual-scroll-slice-size="options.length"
         hide-bottom-space

From d2680b0a1fcccef8b3b5daa9a2ee6b12ddacad34 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Tue, 29 Oct 2024 02:38:44 +0100
Subject: [PATCH 123/150] test: fix arrayData

---
 src/composables/useArrayData.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js
index 0a060e4c6..e33cb8b78 100644
--- a/src/composables/useArrayData.js
+++ b/src/composables/useArrayData.js
@@ -249,7 +249,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
     function updateStateParams() {
         if (!route) return;
         const newUrl = { path: route.path, query: { ...(route.query ?? {}) } };
-        if (store.appendParams)
+        if (store?.appendParams ?? true)
             newUrl.query[store.searchUrl] = JSON.stringify(store.currentFilter);
 
         if (store.navigate) {

From 96f894cc6c6b9be2dc235323a0a874a86b548eb3 Mon Sep 17 00:00:00 2001
From: carlossa <carlossa@verdnatura.es>
Date: Tue, 29 Oct 2024 08:17:18 +0100
Subject: [PATCH 124/150] fix: refs #7283 filter

---
 src/pages/Item/ItemList.vue    | 2 +-
 src/pages/Item/ItemRequest.vue | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index a6873d10c..a480cfff6 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -215,7 +215,7 @@ const columns = computed(() => [
         columnFilter: {
             name: 'workerFk',
             attrs: {
-                url: 'VnUsers/preview',
+                url: 'TicketRequests/getItemTypeWorker',
                 optionValue: 'id',
                 optionLabel: 'nickname',
             },
diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue
index 36da0368b..61156640e 100644
--- a/src/pages/Item/ItemRequest.vue
+++ b/src/pages/Item/ItemRequest.vue
@@ -104,7 +104,7 @@ const columns = computed(() => [
         columnFilter: {
             name: 'attenderFk',
             attrs: {
-                url: 'VnUsers/preview',
+                url: 'TicketRequests/getItemTypeWorker',
                 optionValue: 'id',
                 optionLabel: 'nickname',
             },

From 42fbdff5bd2e4d4e00d4514b35b861440e032ac1 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Tue, 29 Oct 2024 08:48:06 +0100
Subject: [PATCH 125/150] test: fix e2e

---
 src/components/ui/VnConfirm.vue                  | 1 +
 test/cypress/integration/outLogin/logout.spec.js | 7 ++-----
 2 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/src/components/ui/VnConfirm.vue b/src/components/ui/VnConfirm.vue
index ec9d0f48b..0abfc84bc 100644
--- a/src/components/ui/VnConfirm.vue
+++ b/src/components/ui/VnConfirm.vue
@@ -34,6 +34,7 @@ const emit = defineEmits(['confirm', 'cancel', ...useDialogPluginComponent.emits
 
 const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
     useDialogPluginComponent();
+defineExpose({ show: () => dialogRef.value.show(), hide: () => dialogRef.value.hide() });
 
 const title = props.title || t('Confirm');
 const message =
diff --git a/test/cypress/integration/outLogin/logout.spec.js b/test/cypress/integration/outLogin/logout.spec.js
index 8d4e90aac..d6c33a65b 100644
--- a/test/cypress/integration/outLogin/logout.spec.js
+++ b/test/cypress/integration/outLogin/logout.spec.js
@@ -13,7 +13,7 @@ describe('Logout', () => {
     });
     describe('not user', () => {
         beforeEach(() => {
-            cy.intercept('GET', '**DefaultViewConfigs**', {
+            cy.intercept('GET', '**StarredModules**', {
                 statusCode: 401,
                 body: {
                     error: {
@@ -29,10 +29,7 @@ describe('Logout', () => {
 
         it('when token not exists', () => {
             cy.get('.q-list > [href="#/item"]').click();
-            cy.get('.q-notification__message').should(
-                'have.text',
-                'Authorization Required'
-            );
+            cy.checkNotification('Authorization Required');
         });
     });
 });

From 5663064dd8ca211f328e96e0bba902685cec0f9a Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Tue, 29 Oct 2024 09:23:49 +0100
Subject: [PATCH 126/150] refactor: modified composable

---
 src/pages/Order/Card/OrderLines.vue         |  7 +++++--
 src/pages/Order/Card/OrderSummary.vue       |  9 +++++++--
 src/pages/Order/composables/confirmOrder.js | 21 ++-------------------
 3 files changed, 14 insertions(+), 23 deletions(-)

diff --git a/src/pages/Order/Card/OrderLines.vue b/src/pages/Order/Card/OrderLines.vue
index 90586dc15..58c5c9551 100644
--- a/src/pages/Order/Card/OrderLines.vue
+++ b/src/pages/Order/Card/OrderLines.vue
@@ -6,7 +6,7 @@ import { useQuasar } from 'quasar';
 import axios from 'axios';
 import { useStateStore } from 'stores/useStateStore';
 import { useArrayData } from 'composables/useArrayData';
-import { confirmOrder } from 'composables/confirmOrder';
+import { confirm } from 'src/pages/Order/composables/confirmOrder';
 import { toCurrency, toDate } from 'src/filters';
 
 import VnConfirm from 'components/ui/VnConfirm.vue';
@@ -32,7 +32,6 @@ const orderSummary = ref({
 });
 const getTotalRef = ref();
 const getVATRef = ref();
-const { confirm } = confirmOrder();
 const lineFilter = ref({
     include: [
         {
@@ -208,6 +207,10 @@ async function remove(item) {
 async function handleConfirm() {
     const result = await confirm(route.params.id);
     if (result) {
+        quasar.notify({
+            message: t('globals.dataSaved'),
+            type: 'positive',
+        });
         router.push({
             name: 'TicketList',
             query: {
diff --git a/src/pages/Order/Card/OrderSummary.vue b/src/pages/Order/Card/OrderSummary.vue
index 3669fe8d8..032af1993 100644
--- a/src/pages/Order/Card/OrderSummary.vue
+++ b/src/pages/Order/Card/OrderSummary.vue
@@ -2,9 +2,10 @@
 import { computed, ref } from 'vue';
 import { useRoute } from 'vue-router';
 import { useI18n } from 'vue-i18n';
+import { useQuasar } from 'quasar';
 import { dashIfEmpty, toCurrency, toDateHourMinSec } from 'src/filters';
 import { useArrayData } from 'composables/useArrayData';
-import { confirmOrder } from 'composables/confirmOrder';
+import { confirm } from 'src/pages/Order/composables/confirmOrder';
 import VnLv from 'components/ui/VnLv.vue';
 import CardSummary from 'components/ui/CardSummary.vue';
 import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
@@ -24,8 +25,8 @@ const $props = defineProps({
 
 const entityId = computed(() => $props.id || route.params.id);
 const summary = ref();
+const quasar = useQuasar();
 const descriptorData = useArrayData('orderData');
-const { confirm } = confirmOrder();
 const detailsColumns = ref([
     {
         name: 'item',
@@ -58,6 +59,10 @@ const detailsColumns = ref([
 async function handleConfirm() {
     const result = await confirm(route.params.id);
     if (result) {
+        quasar.notify({
+            message: t('globals.dataSaved'),
+            type: 'positive',
+        });
         summary.value.fetch({});
         descriptorData.fetch({});
     }
diff --git a/src/pages/Order/composables/confirmOrder.js b/src/pages/Order/composables/confirmOrder.js
index 33e05a1d7..b9eb5d7ac 100644
--- a/src/pages/Order/composables/confirmOrder.js
+++ b/src/pages/Order/composables/confirmOrder.js
@@ -1,22 +1,5 @@
 import axios from 'axios';
-import { useQuasar } from 'quasar';
-import { useI18n } from 'vue-i18n';
 
-export function confirmOrder() {
-    const quasar = useQuasar();
-    const { t } = useI18n();
-
-    async function confirm(route) {
-        const { data } = await axios.post(`Orders/${route}/confirm`);
-        if (data) {
-            quasar.notify({
-                message: t('globals.confirm'),
-                type: 'positive',
-            });
-            return data;
-        }
-        return null;
-    }
-
-    return { confirm };
+export async function confirm(routeId) {
+    return await axios.post(`Orders/${routeId}/confirm`);
 }

From 77cb2d84be6cb11f02effc49371a0ac70e580df2 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Tue, 29 Oct 2024 09:38:40 +0100
Subject: [PATCH 127/150] fix: refs #8078 handleSelection

---
 src/components/VnTable/VnTable.vue | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index 74a96e0e3..a15ba2f47 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -335,8 +335,9 @@ function handleScroll() {
 function handleSelection({ evt, added, rows: selectedRows }, rows) {
     if (evt?.shiftKey && added) {
         const rowIndex = selectedRows[0].$index;
+        selected.value.length = 0;
         for (const row of rows) {
-            if (row.$index > rowIndex) break;
+            if (row.$index == rowIndex) break;
             selected.value.push(row);
         }
     }
@@ -440,7 +441,6 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
                 :virtual-scroll="isTableMode"
                 @virtual-scroll="handleScroll"
                 @row-click="(_, row) => rowClickFunction && rowClickFunction(row)"
-                @update:selected="emit('update:selected', $event)"
                 @selection="(details) => handleSelection(details, rows)"
             >
                 <template #top-left v-if="!$props.withoutHeader">

From 19a7e526059a6b41cd7fb5902f0502aed294b4bd Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Tue, 29 Oct 2024 09:39:56 +0100
Subject: [PATCH 128/150] fix: refs #8078 handleSelection

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

diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index a15ba2f47..1e949120f 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -441,6 +441,7 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) {
                 :virtual-scroll="isTableMode"
                 @virtual-scroll="handleScroll"
                 @row-click="(_, row) => rowClickFunction && rowClickFunction(row)"
+                @update:selected="emit('update:selected', $event)"
                 @selection="(details) => handleSelection(details, rows)"
             >
                 <template #top-left v-if="!$props.withoutHeader">

From e39f85ff4bcd45993a65fba6b01a9ef1aac30df8 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Tue, 29 Oct 2024 09:56:06 +0100
Subject: [PATCH 129/150] fix: refs #8078  improve handleSelection

---
 src/components/VnTable/VnTable.vue | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index 1e949120f..dc8671369 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -335,10 +335,13 @@ function handleScroll() {
 function handleSelection({ evt, added, rows: selectedRows }, rows) {
     if (evt?.shiftKey && added) {
         const rowIndex = selectedRows[0].$index;
-        selected.value.length = 0;
+        const selectedIndexes = new Set(selected.value.map((row) => row.$index));
         for (const row of rows) {
             if (row.$index == rowIndex) break;
-            selected.value.push(row);
+            if (!selectedIndexes.has(row.$index)) {
+                selected.value.push(row);
+                selectedIndexes.add(row.$index);
+            }
         }
     }
 }

From 476ef1dedd7907586b27d80433427f2faf6fefe4 Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Tue, 29 Oct 2024 13:14:42 +0100
Subject: [PATCH 130/150] feat: refs #7206 added inactive label and corrected
 minor errors

---
 src/components/ui/VnConfirm.vue               |  1 +
 .../components/CustomerSummaryTable.vue       |  2 +-
 src/pages/Order/Card/OrderCreateDialog.vue    |  1 -
 src/pages/Order/OrderList.vue                 | 32 ++++++++++++++++---
 src/pages/Ticket/Card/TicketSummary.vue       |  5 ---
 src/pages/Ticket/TicketList.vue               | 29 +++++++++++------
 6 files changed, 50 insertions(+), 20 deletions(-)

diff --git a/src/components/ui/VnConfirm.vue b/src/components/ui/VnConfirm.vue
index ec9d0f48b..d6b1ac0a3 100644
--- a/src/components/ui/VnConfirm.vue
+++ b/src/components/ui/VnConfirm.vue
@@ -31,6 +31,7 @@ const props = defineProps({
 });
 
 const emit = defineEmits(['confirm', 'cancel', ...useDialogPluginComponent.emits]);
+defineExpose({ show: () => dialogRef.value.show(), hide: () => dialogRef.value.hide() });
 
 const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
     useDialogPluginComponent();
diff --git a/src/pages/Customer/components/CustomerSummaryTable.vue b/src/pages/Customer/components/CustomerSummaryTable.vue
index e9bb36be7..e9e30506b 100644
--- a/src/pages/Customer/components/CustomerSummaryTable.vue
+++ b/src/pages/Customer/components/CustomerSummaryTable.vue
@@ -63,7 +63,7 @@ const columns = computed(() => [
     },
     {
         align: 'left',
-        format: (row) => row.agencyMode.name,
+        format: (row, dashIfEmpty) => dashIfEmpty(row.agencyMode?.name),
         columnClass: 'expand',
         label: t('Agency'),
     },
diff --git a/src/pages/Order/Card/OrderCreateDialog.vue b/src/pages/Order/Card/OrderCreateDialog.vue
index bcc62aa43..1239d195b 100644
--- a/src/pages/Order/Card/OrderCreateDialog.vue
+++ b/src/pages/Order/Card/OrderCreateDialog.vue
@@ -10,7 +10,6 @@ import VnSelect from 'components/common/VnSelect.vue';
 import VnInputDate from 'components/common/VnInputDate.vue';
 import { useDialogPluginComponent } from 'quasar';
 import { reactive } from 'vue';
-import FetchData from 'components/FetchData.vue';
 
 const { t } = useI18n();
 const state = useState();
diff --git a/src/pages/Order/OrderList.vue b/src/pages/Order/OrderList.vue
index 6b6b41828..e01790c6b 100644
--- a/src/pages/Order/OrderList.vue
+++ b/src/pages/Order/OrderList.vue
@@ -233,7 +233,20 @@ onMounted(() => {
                 v-model="data.clientFk"
                 :label="t('module.customer')"
                 @update:model-value="(id) => fetchClientAddress(id, data)"
-            />
+            >
+                <template #option="scope">
+                    <QItem v-bind="scope.itemProps">
+                        <QItemSection>
+                            <QItemLabel>
+                                {{ scope.opt.name }}
+                            </QItemLabel>
+                            <QItemLabel caption>
+                                {{ `#${scope.opt.id}` }}
+                            </QItemLabel>
+                        </QItemSection>
+                    </QItem>
+                </template>
+            </VnSelect>
             <VnSelect
                 v-model="data.addressId"
                 :options="addressesList"
@@ -245,10 +258,21 @@ onMounted(() => {
                 <template #option="scope">
                     <QItem v-bind="scope.itemProps">
                         <QItemSection>
-                            <QItemLabel>
-                                {{ scope.opt?.nickname }}: {{ scope.opt?.street }},
-                                {{ scope.opt?.city }}</QItemLabel
+                            <QItemLabel
+                                :class="{
+                                    'color-vn-label': !scope.opt?.isActive,
+                                }"
                             >
+                                {{
+                                    `${
+                                        !scope.opt?.isActive
+                                            ? t('basicData.inactive')
+                                            : ''
+                                    } `
+                                }}
+                                {{ scope.opt?.nickname }}: {{ scope.opt?.street }},
+                                {{ scope.opt?.city }}
+                            </QItemLabel>
                         </QItemSection>
                     </QItem>
                 </template>
diff --git a/src/pages/Ticket/Card/TicketSummary.vue b/src/pages/Ticket/Card/TicketSummary.vue
index 5fb99b849..61751357c 100644
--- a/src/pages/Ticket/Card/TicketSummary.vue
+++ b/src/pages/Ticket/Card/TicketSummary.vue
@@ -19,7 +19,6 @@ import VnTitle from 'src/components/common/VnTitle.vue';
 import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
 import ZoneDescriptorProxy from 'src/pages/Zone/Card/ZoneDescriptorProxy.vue';
 import VnSelect from 'src/components/common/VnSelect.vue';
-import TicketDescriptorMenu from './TicketDescriptorMenu.vue';
 import VnToSummary from 'src/components/ui/VnToSummary.vue';
 
 const route = useRoute();
@@ -87,10 +86,6 @@ async function changeState(value) {
 function toTicketUrl(section) {
     return '#/ticket/' + entityId.value + '/' + section;
 }
-function isOnTicketCard() {
-    const currentPath = route.path;
-    return currentPath.startsWith('/ticket');
-}
 </script>
 
 <template>
diff --git a/src/pages/Ticket/TicketList.vue b/src/pages/Ticket/TicketList.vue
index dd1f2d69a..4317efb83 100644
--- a/src/pages/Ticket/TicketList.vue
+++ b/src/pages/Ticket/TicketList.vue
@@ -268,8 +268,7 @@ const fetchAddresses = async (formData) => {
         if (!formData.clientId) return;
 
         const filter = {
-            fields: ['nickname', 'street', 'city', 'id'],
-            where: { isActive: true },
+            fields: ['nickname', 'street', 'city', 'id', 'isActive'],
             order: 'nickname ASC',
         };
         const params = { filter: JSON.stringify(filter) };
@@ -635,24 +634,36 @@ function setReference(data) {
             </VnRow>
             <VnRow>
                 <VnSelect
-                    url="Addresses"
-                    :label="t('ticket.create.address')"
+                    :label="t('basicData.address')"
                     v-model="data.addressId"
                     :options="addressesOptions"
                     option-value="id"
                     option-label="nickname"
                     hide-selected
+                    map-options
                     :disable="!data.clientId"
+                    :sort-by="'isActive DESC'"
                     @update:model-value="() => fetchAvailableAgencies(data)"
                 >
                     <template #option="scope">
                         <QItem v-bind="scope.itemProps">
                             <QItemSection>
-                                <QItemLabel>
-                                    {{ scope.opt.nickname }}
-                                </QItemLabel>
-                                <QItemLabel caption>
-                                    {{ `${scope.opt.street}, ${scope.opt.city}` }}
+                                <QItemLabel
+                                    :class="{
+                                        'color-vn-label': !scope.opt?.isActive,
+                                    }"
+                                >
+                                    {{
+                                        `${
+                                            !scope.opt?.isActive
+                                                ? t('basicData.inactive')
+                                                : ''
+                                        } `
+                                    }}
+                                    <span>
+                                        {{ scope.opt?.nickname }}:
+                                        {{ scope.opt?.street }}, {{ scope.opt?.city }}
+                                    </span>
                                 </QItemLabel>
                             </QItemSection>
                         </QItem>

From 282999c49e52e5e36369d82b28b5a55ba3469c3c Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Tue, 29 Oct 2024 13:23:15 +0100
Subject: [PATCH 131/150] fix: refs #7206 deleted duplicate code

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

diff --git a/src/components/ui/VnConfirm.vue b/src/components/ui/VnConfirm.vue
index 081812093..d6b1ac0a3 100644
--- a/src/components/ui/VnConfirm.vue
+++ b/src/components/ui/VnConfirm.vue
@@ -35,7 +35,6 @@ defineExpose({ show: () => dialogRef.value.show(), hide: () => dialogRef.value.h
 
 const { dialogRef, onDialogHide, onDialogOK, onDialogCancel } =
     useDialogPluginComponent();
-defineExpose({ show: () => dialogRef.value.show(), hide: () => dialogRef.value.hide() });
 
 const title = props.title || t('Confirm');
 const message =

From ed8225bf6c0285ef9f89af4ae8d1706b3edbc555 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Tue, 29 Oct 2024 14:23:06 +0100
Subject: [PATCH 132/150] feat: #7782 waitUntil domContentLoad

---
 .../vnComponent/vnLocation.spec.js            |  2 +-
 test/cypress/support/commands.js              | 12 +++-
 test/cypress/support/waitUntil.js             | 59 +++++++++++++++++++
 3 files changed, 69 insertions(+), 4 deletions(-)
 create mode 100644 test/cypress/support/waitUntil.js

diff --git a/test/cypress/integration/vnComponent/vnLocation.spec.js b/test/cypress/integration/vnComponent/vnLocation.spec.js
index c1b0cf929..924b16adc 100644
--- a/test/cypress/integration/vnComponent/vnLocation.spec.js
+++ b/test/cypress/integration/vnComponent/vnLocation.spec.js
@@ -12,7 +12,7 @@ describe('VnLocation', () => {
             cy.viewport(1280, 720);
             cy.login('developer');
             cy.visit('/#/supplier/567/fiscal-data', { timeout: 7000 });
-            cy.waitForElement('.q-card');
+            cy.domContentLoad();
             cy.get(createLocationButton).click();
         });
         it('should filter provinces based on selected country', () => {
diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js
index 76bdefd27..5bb89ecf9 100755
--- a/test/cypress/support/commands.js
+++ b/test/cypress/support/commands.js
@@ -27,6 +27,10 @@
 // DO NOT REMOVE
 // Imports Quasar Cypress AE predefined commands
 // import { registerCommands } from '@quasar/quasar-app-extension-testing-e2e-cypress';
+Cypress.Commands.add('waitUntil', { prevSubject: 'optional' }, require('./waitUntil'));
+Cypress.Commands.add('resetDB', () => {
+    cy.exec('pnpm run resetDatabase');
+});
 Cypress.Commands.add('login', (user) => {
     //cy.visit('/#/login');
     cy.request({
@@ -50,10 +54,12 @@ Cypress.Commands.add('login', (user) => {
     });
 });
 
-Cypress.Commands.add('waitForElement', (element, timeout = 5000) => {
-    cy.get(element, { timeout }).should('be.visible');
+Cypress.Commands.add('domContentLoad', (element, timeout = 5000) => {
+    cy.waitUntil(() => cy.document().then((doc) => doc.readyState === 'complete'));
+});
+Cypress.Commands.add('waitForElement', (element, timeout = 5000) => {
+    cy.waitUntil(() => cy.get(element).then(($el) => $el.is(':visible')));
 });
-
 Cypress.Commands.add('getValue', (selector) => {
     cy.get(selector).then(($el) => {
         if ($el.find('.q-checkbox__inner').length > 0) {
diff --git a/test/cypress/support/waitUntil.js b/test/cypress/support/waitUntil.js
new file mode 100644
index 000000000..5fb47a2d8
--- /dev/null
+++ b/test/cypress/support/waitUntil.js
@@ -0,0 +1,59 @@
+const waitUntil = (subject, checkFunction, originalOptions = {}) => {
+    if (!(checkFunction instanceof Function)) {
+        throw new Error(
+            '`checkFunction` parameter should be a function. Found: ' + checkFunction
+        );
+    }
+
+    const defaultOptions = {
+        // base options
+        interval: 200,
+        timeout: 5000,
+        errorMsg: 'Timed out retrying',
+
+        // log options
+        description: 'waitUntil',
+        log: true,
+        customMessage: undefined,
+        logger: Cypress.log,
+        verbose: false,
+        customCheckMessage: undefined,
+    };
+    const options = { ...defaultOptions, ...originalOptions };
+
+    // filter out a falsy passed "customMessage" value
+    options.customMessage = [options.customMessage, originalOptions].filter(Boolean);
+
+    const endTime = Date.now() + options.timeout;
+
+    const check = (result) => {
+        if (result) {
+            return result;
+        }
+        if (Date.now() >= endTime) {
+            const msg =
+                options.errorMsg instanceof Function
+                    ? options.errorMsg(result, options)
+                    : options.errorMsg;
+            throw new Error(msg);
+        }
+        cy.wait(options.interval, { log: false }).then(() => {
+            return resolveValue();
+        });
+    };
+
+    const resolveValue = () => {
+        const result = checkFunction(subject);
+
+        const isAPromise = Boolean(result && result.then);
+        if (isAPromise) {
+            return result.then(check);
+        } else {
+            return check(result);
+        }
+    };
+
+    return resolveValue();
+};
+
+export default waitUntil;

From b817aa92c2b4f09dd4c282d2313551acf8f5be0b Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Tue, 29 Oct 2024 14:23:22 +0100
Subject: [PATCH 133/150] feat: #7782 npm run resetDatabase

---
 package.json | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/package.json b/package.json
index a61c8f21a..71e95667c 100644
--- a/package.json
+++ b/package.json
@@ -7,10 +7,11 @@
     "private": true,
     "packageManager": "pnpm@8.15.1",
     "scripts": {
+        "resetDatabase": "cd ../salix && gulp docker",
         "lint": "eslint --ext .js,.vue ./",
         "format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore",
         "test:e2e": "cypress open",
-        "test:e2e:ci": "cd ../salix && gulp docker && cd ../salix-front && cypress run",
+        "test:e2e:ci": "npm run resetDatabase && cd ../salix-front && cypress run",
         "test": "echo \"See package.json => scripts for available tests.\" && exit 0",
         "test:unit": "vitest",
         "test:unit:ci": "vitest run",

From 2090b78ce6005df77178925f6e04a3b8c39bce86 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Tue, 29 Oct 2024 14:25:34 +0100
Subject: [PATCH 134/150] feat: #7782 cypress.config watchForFileChanges

---
 cypress.config.js | 1 +
 1 file changed, 1 insertion(+)

diff --git a/cypress.config.js b/cypress.config.js
index e2046d6c4..1b3e0190f 100644
--- a/cypress.config.js
+++ b/cypress.config.js
@@ -11,6 +11,7 @@ module.exports = defineConfig({
         video: false,
         specPattern: 'test/cypress/integration/**/*.spec.js',
         experimentalRunAllSpecs: true,
+        watchForFileChanges: true,
         component: {
             componentFolder: 'src',
             testFiles: '**/*.spec.js',

From fbbbc331a951734b09cc7bd084209facdb1143cb Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Tue, 29 Oct 2024 14:27:24 +0100
Subject: [PATCH 135/150] feat: #7782 add cypress report

---
 cypress.config.js |   9 ++
 package.json      |   1 +
 pnpm-lock.yaml    | 379 ++++++++++++++++++++++++++++++++++++++++++++--
 3 files changed, 380 insertions(+), 9 deletions(-)

diff --git a/cypress.config.js b/cypress.config.js
index 1b3e0190f..f8e771093 100644
--- a/cypress.config.js
+++ b/cypress.config.js
@@ -12,12 +12,21 @@ module.exports = defineConfig({
         specPattern: 'test/cypress/integration/**/*.spec.js',
         experimentalRunAllSpecs: true,
         watchForFileChanges: true,
+        reporter: 'cypress-mochawesome-reporter',
+        reporterOptions: {
+            charts: true,
+            reportPageTitle: 'Cypress Inline Reporter',
+            embeddedScreenshots: true,
+            reportDir: 'test/cypress/reports',
+            inlineAssets: true,
+        },
         component: {
             componentFolder: 'src',
             testFiles: '**/*.spec.js',
             supportFile: 'test/cypress/support/unit.js',
         },
         setupNodeEvents(on, config) {
+            require('cypress-mochawesome-reporter/plugin')(on);
             // implement node event listeners here
         },
     },
diff --git a/package.json b/package.json
index 71e95667c..e2e75f253 100644
--- a/package.json
+++ b/package.json
@@ -43,6 +43,7 @@
         "@vue/test-utils": "^2.4.4",
         "autoprefixer": "^10.4.14",
         "cypress": "^13.6.6",
+        "cypress-mochawesome-reporter": "^3.8.2",
         "eslint": "^8.41.0",
         "eslint-config-prettier": "^8.8.0",
         "eslint-plugin-cypress": "^2.13.3",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index e336c39bb..83dfa0469 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -70,6 +70,9 @@ devDependencies:
   cypress:
     specifier: ^13.6.6
     version: 13.6.6
+  cypress-mochawesome-reporter:
+    specifier: ^3.8.2
+    version: 3.8.2(cypress@13.6.6)(mocha@10.7.3)
   eslint:
     specifier: ^8.41.0
     version: 8.56.0
@@ -829,8 +832,8 @@ packages:
       vue-i18n:
         optional: true
     dependencies:
-      '@intlify/message-compiler': 10.0.0-beta.5
-      '@intlify/shared': 10.0.0-beta.5
+      '@intlify/message-compiler': 10.0.0
+      '@intlify/shared': 10.0.0
       jsonc-eslint-parser: 1.4.1
       source-map: 0.6.1
       vue-i18n: 9.9.1(vue@3.4.19)
@@ -844,11 +847,11 @@ packages:
       '@intlify/message-compiler': 9.9.1
       '@intlify/shared': 9.9.1
 
-  /@intlify/message-compiler@10.0.0-beta.5:
-    resolution: {integrity: sha512-hLLchnM1dmtSEruerkzvU9vePsLqBXz3RU85SCx/Vd12fFQiymP+/5Rn9MJ8MyfLmIOLDEx4PRh+/GkIQP6oog==}
+  /@intlify/message-compiler@10.0.0:
+    resolution: {integrity: sha512-OcaWc63NC/9p1cMdgoNKBj4d61BH8sUW1Hfs6YijTd9656ZR4rNqXAlRnBrfS5ABq0vjQjpa8VnyvH9hK49yBw==}
     engines: {node: '>= 16'}
     dependencies:
-      '@intlify/shared': 10.0.0-beta.5
+      '@intlify/shared': 10.0.0
       source-map-js: 1.0.2
     dev: true
 
@@ -859,8 +862,8 @@ packages:
       '@intlify/shared': 9.9.1
       source-map-js: 1.0.2
 
-  /@intlify/shared@10.0.0-beta.5:
-    resolution: {integrity: sha512-g9bq5Y1bOcC9qxtNk4UWtF3sXm6Wh0fGISb7vD5aLyF7yQv7ZFjxQjJzBP2GqG/9+PAGYutqjP1GGadNqFtyAQ==}
+  /@intlify/shared@10.0.0:
+    resolution: {integrity: sha512-6ngLfI7DOTew2dcF9WMJx+NnMWghMBhIiHbGg+wRvngpzD5KZJZiJVuzMsUQE1a5YebEmtpTEfUrDp/NqVGdiw==}
     engines: {node: '>= 16'}
     dev: true
 
@@ -884,7 +887,7 @@ packages:
         optional: true
     dependencies:
       '@intlify/bundle-utils': 4.0.0(vue-i18n@9.9.1)
-      '@intlify/shared': 10.0.0-beta.5
+      '@intlify/shared': 10.0.0
       '@rollup/pluginutils': 4.2.1
       '@vue/compiler-sfc': 3.4.19
       debug: 4.3.4(supports-color@8.1.1)
@@ -1999,6 +2002,10 @@ packages:
     dependencies:
       fill-range: 7.0.1
 
+  /browser-stdout@1.3.1:
+    resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==}
+    dev: true
+
   /browserslist@4.23.0:
     resolution: {integrity: sha512-QW8HiM1shhT2GuzkvklfjcKDiWFXHOeFCIA/huJPwHsslwcydgk7X+z2zXpEijP98UCY7HbubZt5J2Zgvf0CaQ==}
     engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
@@ -2106,6 +2113,16 @@ packages:
       upper-case: 1.1.3
     dev: true
 
+  /camelcase@5.3.1:
+    resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==}
+    engines: {node: '>=6'}
+    dev: true
+
+  /camelcase@6.3.0:
+    resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==}
+    engines: {node: '>=10'}
+    dev: true
+
   /camelcase@7.0.1:
     resolution: {integrity: sha512-xlx1yCK2Oc1APsPXDL2LdlNP6+uu8OCDdhOBSVT279M/S+y75O30C2VuD8T2ogdePBBl7PfPF4504tnLgX3zfw==}
     engines: {node: '>=14.16'}
@@ -2255,6 +2272,22 @@ packages:
     engines: {node: '>= 10'}
     dev: true
 
+  /cliui@6.0.0:
+    resolution: {integrity: sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==}
+    dependencies:
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+      wrap-ansi: 6.2.0
+    dev: true
+
+  /cliui@7.0.4:
+    resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==}
+    dependencies:
+      string-width: 4.2.3
+      strip-ansi: 6.0.1
+      wrap-ansi: 7.0.0
+    dev: true
+
   /cliui@8.0.1:
     resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==}
     engines: {node: '>=12'}
@@ -2558,6 +2591,23 @@ packages:
   /csstype@3.1.3:
     resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
 
+  /cypress-mochawesome-reporter@3.8.2(cypress@13.6.6)(mocha@10.7.3):
+    resolution: {integrity: sha512-oJZkNzhNmN9ZD+LmZyFuPb8aWaIijyHyqYh52YOBvR6B6ckfJNCHP3A98a+/nG0H4t46CKTNwo+wNpMa4d2kjA==}
+    engines: {node: '>=14'}
+    hasBin: true
+    peerDependencies:
+      cypress: '>=6.2.0'
+    dependencies:
+      commander: 10.0.1
+      cypress: 13.6.6
+      fs-extra: 10.1.0
+      mochawesome: 7.1.3(mocha@10.7.3)
+      mochawesome-merge: 4.3.0
+      mochawesome-report-generator: 6.2.0
+    transitivePeerDependencies:
+      - mocha
+    dev: true
+
   /cypress@13.6.6:
     resolution: {integrity: sha512-S+2S9S94611hXimH9a3EAYt81QM913ZVA03pUmGDfLTFa5gyp85NJ8dJGSlEAEmyRsYkioS1TtnWtbv/Fzt11A==}
     engines: {node: ^16.0.0 || ^18.0.0 || >=20.0.0}
@@ -2627,6 +2677,10 @@ packages:
       time-zone: 1.0.0
     dev: true
 
+  /dateformat@4.6.3:
+    resolution: {integrity: sha512-2P0p0pFGzHS5EMnhdxQi7aJN+iMheud0UhG4dlE1DLAlvL8JHjJJTX/CSm4JXwV0Ka5nGk3zC5mcb5bUQUxxMA==}
+    dev: true
+
   /dayjs@1.11.10:
     resolution: {integrity: sha512-vjAczensTgRcqDERK0SR2XMwsF/tSvnvlv6VcF2GIhg6Sx4yOIt/irsr1RDJsKiIyBzJDpCoXiWWq28MqH2cnQ==}
     dev: true
@@ -2676,6 +2730,29 @@ packages:
       ms: 2.1.2
       supports-color: 8.1.1
 
+  /debug@4.3.7(supports-color@8.1.1):
+    resolution: {integrity: sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==}
+    engines: {node: '>=6.0'}
+    peerDependencies:
+      supports-color: '*'
+    peerDependenciesMeta:
+      supports-color:
+        optional: true
+    dependencies:
+      ms: 2.1.3
+      supports-color: 8.1.1
+    dev: true
+
+  /decamelize@1.2.0:
+    resolution: {integrity: sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==}
+    engines: {node: '>=0.10.0'}
+    dev: true
+
+  /decamelize@4.0.0:
+    resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==}
+    engines: {node: '>=10'}
+    dev: true
+
   /decompress-response@6.0.0:
     resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==}
     engines: {node: '>=10'}
@@ -2758,6 +2835,11 @@ packages:
     resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==}
     engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16}
 
+  /diff@5.2.0:
+    resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==}
+    engines: {node: '>=0.3.1'}
+    dev: true
+
   /doctrine@3.0.0:
     resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==}
     engines: {node: '>=6.0.0'}
@@ -3550,6 +3632,14 @@ packages:
     transitivePeerDependencies:
       - supports-color
 
+  /find-up@4.1.0:
+    resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==}
+    engines: {node: '>=8'}
+    dependencies:
+      locate-path: 5.0.0
+      path-exists: 4.0.0
+    dev: true
+
   /find-up@5.0.0:
     resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
     engines: {node: '>=10'}
@@ -3646,6 +3736,15 @@ packages:
     resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==}
     dev: true
 
+  /fs-extra@10.1.0:
+    resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==}
+    engines: {node: '>=12'}
+    dependencies:
+      graceful-fs: 4.2.11
+      jsonfile: 6.1.0
+      universalify: 2.0.1
+    dev: true
+
   /fs-extra@11.2.0:
     resolution: {integrity: sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==}
     engines: {node: '>=14.14'}
@@ -3654,6 +3753,15 @@ packages:
       jsonfile: 6.1.0
       universalify: 2.0.1
 
+  /fs-extra@7.0.1:
+    resolution: {integrity: sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==}
+    engines: {node: '>=6 <7 || >=8'}
+    dependencies:
+      graceful-fs: 4.2.11
+      jsonfile: 4.0.0
+      universalify: 0.1.2
+    dev: true
+
   /fs-extra@9.1.0:
     resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==}
     engines: {node: '>=10'}
@@ -3675,6 +3783,10 @@ packages:
     dev: true
     optional: true
 
+  /fsu@1.1.1:
+    resolution: {integrity: sha512-xQVsnjJ/5pQtcKh+KjUoZGzVWn4uNkchxTF6Lwjr4Gf7nQr8fmUfhKJ62zE77+xQg9xnxi5KUps7XGs+VC986A==}
+    dev: true
+
   /function-bind@1.1.2:
     resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
 
@@ -3775,6 +3887,18 @@ packages:
       once: 1.4.0
       path-is-absolute: 1.0.1
 
+  /glob@8.1.0:
+    resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==}
+    engines: {node: '>=12'}
+    deprecated: Glob versions prior to v9 are no longer supported
+    dependencies:
+      fs.realpath: 1.0.0
+      inflight: 1.0.6
+      inherits: 2.0.4
+      minimatch: 5.1.6
+      once: 1.4.0
+    dev: true
+
   /global-directory@4.0.1:
     resolution: {integrity: sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==}
     engines: {node: '>=18'}
@@ -4189,6 +4313,11 @@ packages:
     resolution: {integrity: sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==}
     engines: {node: '>=8'}
 
+  /is-plain-obj@2.1.0:
+    resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==}
+    engines: {node: '>=8'}
+    dev: true
+
   /is-plain-obj@3.0.0:
     resolution: {integrity: sha512-gwsOE28k+23GP1B6vFl1oVh/WOzmawBrKwo5Ev6wMKzPkaXaCDIQKzLnvsA42DRlbVTWorkgTKIviAKCWkfUwA==}
     engines: {node: '>=10'}
@@ -4361,6 +4490,12 @@ packages:
     resolution: {integrity: sha512-AilxAyFOAcK5wA1+LeaySVBrHsGQvUFCDWXKpZjzaL0PqW+xfBOttn8GNtWKFWqneyMZj41MWF9Kl6iPWLwgOA==}
     dev: true
 
+  /jsonfile@4.0.0:
+    resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==}
+    optionalDependencies:
+      graceful-fs: 4.2.11
+    dev: true
+
   /jsonfile@6.1.0:
     resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==}
     dependencies:
@@ -4452,6 +4587,13 @@ packages:
     engines: {node: '>=14'}
     dev: true
 
+  /locate-path@5.0.0:
+    resolution: {integrity: sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==}
+    engines: {node: '>=8'}
+    dependencies:
+      p-locate: 4.1.0
+    dev: true
+
   /locate-path@6.0.0:
     resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
     engines: {node: '>=10'}
@@ -4486,10 +4628,26 @@ packages:
     resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==}
     dev: true
 
+  /lodash.isempty@4.4.0:
+    resolution: {integrity: sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==}
+    dev: true
+
+  /lodash.isfunction@3.0.9:
+    resolution: {integrity: sha512-AirXNj15uRIMMPihnkInB4i3NHeb4iBtNg9WRWuK2o31S+ePwwNmDPaTL3o7dTJ+VXNZim7rFs4rxN4YU1oUJw==}
+    dev: true
+
+  /lodash.isobject@3.0.2:
+    resolution: {integrity: sha512-3/Qptq2vr7WeJbB4KHUSKlq8Pl7ASXi3UG6CMbBm8WRtXi8+GHm7mKaU3urfpSEzWe2wCIChs6/sdocUsTKJiA==}
+    dev: true
+
   /lodash.isplainobject@4.0.6:
     resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
     dev: true
 
+  /lodash.isstring@4.0.1:
+    resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==}
+    dev: true
+
   /lodash.kebabcase@4.1.1:
     resolution: {integrity: sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==}
     dev: true
@@ -4552,6 +4710,13 @@ packages:
       wrap-ansi: 6.2.0
     dev: true
 
+  /loose-envify@1.4.0:
+    resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
+    hasBin: true
+    dependencies:
+      js-tokens: 4.0.0
+    dev: true
+
   /loupe@2.3.7:
     resolution: {integrity: sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==}
     dependencies:
@@ -4722,6 +4887,79 @@ packages:
       ufo: 1.4.0
     dev: true
 
+  /mocha@10.7.3:
+    resolution: {integrity: sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==}
+    engines: {node: '>= 14.0.0'}
+    hasBin: true
+    dependencies:
+      ansi-colors: 4.1.3
+      browser-stdout: 1.3.1
+      chokidar: 3.6.0
+      debug: 4.3.7(supports-color@8.1.1)
+      diff: 5.2.0
+      escape-string-regexp: 4.0.0
+      find-up: 5.0.0
+      glob: 8.1.0
+      he: 1.2.0
+      js-yaml: 4.1.0
+      log-symbols: 4.1.0
+      minimatch: 5.1.6
+      ms: 2.1.3
+      serialize-javascript: 6.0.2
+      strip-json-comments: 3.1.1
+      supports-color: 8.1.1
+      workerpool: 6.5.1
+      yargs: 16.2.0
+      yargs-parser: 20.2.9
+      yargs-unparser: 2.0.0
+    dev: true
+
+  /mochawesome-merge@4.3.0:
+    resolution: {integrity: sha512-1roR6g+VUlfdaRmL8dCiVpKiaUhbPVm1ZQYUM6zHX46mWk+tpsKVZR6ba98k2zc8nlPvYd71yn5gyH970pKBSw==}
+    engines: {node: '>=10.0.0'}
+    hasBin: true
+    dependencies:
+      fs-extra: 7.0.1
+      glob: 7.2.3
+      yargs: 15.4.1
+    dev: true
+
+  /mochawesome-report-generator@6.2.0:
+    resolution: {integrity: sha512-Ghw8JhQFizF0Vjbtp9B0i//+BOkV5OWcQCPpbO0NGOoxV33o+gKDYU0Pr2pGxkIHnqZ+g5mYiXF7GMNgAcDpSg==}
+    hasBin: true
+    dependencies:
+      chalk: 4.1.2
+      dateformat: 4.6.3
+      escape-html: 1.0.3
+      fs-extra: 10.1.0
+      fsu: 1.1.1
+      lodash.isfunction: 3.0.9
+      opener: 1.5.2
+      prop-types: 15.8.1
+      tcomb: 3.2.29
+      tcomb-validation: 3.4.1
+      validator: 13.11.0
+      yargs: 17.7.2
+    dev: true
+
+  /mochawesome@7.1.3(mocha@10.7.3):
+    resolution: {integrity: sha512-Vkb3jR5GZ1cXohMQQ73H3cZz7RoxGjjUo0G5hu0jLaW+0FdUxUwg3Cj29bqQdh0rFcnyV06pWmqmi5eBPnEuNQ==}
+    peerDependencies:
+      mocha: '>=7'
+    dependencies:
+      chalk: 4.1.2
+      diff: 5.2.0
+      json-stringify-safe: 5.0.1
+      lodash.isempty: 4.4.0
+      lodash.isfunction: 3.0.9
+      lodash.isobject: 3.0.2
+      lodash.isstring: 4.0.1
+      mocha: 10.7.3
+      mochawesome-report-generator: 6.2.0
+      strip-ansi: 6.0.1
+      uuid: 8.3.2
+    dev: true
+
   /ms@2.0.0:
     resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
 
@@ -4870,6 +5108,11 @@ packages:
       is-wsl: 2.2.0
     dev: false
 
+  /opener@1.5.2:
+    resolution: {integrity: sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==}
+    hasBin: true
+    dev: true
+
   /optionator@0.9.3:
     resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
     engines: {node: '>= 0.8.0'}
@@ -4915,6 +5158,13 @@ packages:
     engines: {node: '>=12.20'}
     dev: false
 
+  /p-limit@2.3.0:
+    resolution: {integrity: sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==}
+    engines: {node: '>=6'}
+    dependencies:
+      p-try: 2.2.0
+    dev: true
+
   /p-limit@3.1.0:
     resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
     engines: {node: '>=10'}
@@ -4929,6 +5179,13 @@ packages:
       yocto-queue: 1.0.0
     dev: true
 
+  /p-locate@4.1.0:
+    resolution: {integrity: sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==}
+    engines: {node: '>=8'}
+    dependencies:
+      p-limit: 2.3.0
+    dev: true
+
   /p-locate@5.0.0:
     resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
     engines: {node: '>=10'}
@@ -4950,6 +5207,11 @@ packages:
       aggregate-error: 3.1.0
     dev: true
 
+  /p-try@2.2.0:
+    resolution: {integrity: sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==}
+    engines: {node: '>=6'}
+    dev: true
+
   /package-json@8.1.1:
     resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==}
     engines: {node: '>=14.16'}
@@ -5139,6 +5401,14 @@ packages:
     engines: {node: '>=0.4.0'}
     dev: false
 
+  /prop-types@15.8.1:
+    resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
+    dependencies:
+      loose-envify: 1.4.0
+      object-assign: 4.1.1
+      react-is: 16.13.1
+    dev: true
+
   /proto-list@1.2.4:
     resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
 
@@ -5242,6 +5512,10 @@ packages:
       strip-json-comments: 2.0.1
     dev: false
 
+  /react-is@16.13.1:
+    resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+    dev: true
+
   /react-is@17.0.2:
     resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==}
     dev: true
@@ -5328,6 +5602,10 @@ packages:
     engines: {node: '>=0.10.0'}
     dev: true
 
+  /require-main-filename@2.0.0:
+    resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
+    dev: true
+
   /requires-port@1.0.0:
     resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==}
 
@@ -5573,6 +5851,10 @@ packages:
     transitivePeerDependencies:
       - supports-color
 
+  /set-blocking@2.0.0:
+    resolution: {integrity: sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==}
+    dev: true
+
   /set-function-length@1.2.1:
     resolution: {integrity: sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g==}
     engines: {node: '>= 0.4'}
@@ -5829,6 +6111,16 @@ packages:
       readable-stream: 3.6.2
     dev: true
 
+  /tcomb-validation@3.4.1:
+    resolution: {integrity: sha512-urVVMQOma4RXwiVCa2nM2eqrAomHROHvWPuj6UkDGz/eb5kcy0x6P0dVt6kzpUZtYMNoAqJLWmz1BPtxrtjtrA==}
+    dependencies:
+      tcomb: 3.2.29
+    dev: true
+
+  /tcomb@3.2.29:
+    resolution: {integrity: sha512-di2Hd1DB2Zfw6StGv861JoAF5h/uQVu/QJp2g8KVbtfKnoHdBQl5M32YWq6mnSYBQ1vFFrns5B1haWJL7rKaOQ==}
+    dev: true
+
   /text-extensions@2.4.0:
     resolution: {integrity: sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==}
     engines: {node: '>=8'}
@@ -6048,6 +6340,11 @@ packages:
       crypto-random-string: 4.0.0
     dev: false
 
+  /universalify@0.1.2:
+    resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==}
+    engines: {node: '>= 4.0.0'}
+    dev: true
+
   /universalify@0.2.0:
     resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==}
     engines: {node: '>= 4.0.0'}
@@ -6137,7 +6434,6 @@ packages:
   /validator@13.11.0:
     resolution: {integrity: sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==}
     engines: {node: '>= 0.10'}
-    dev: false
 
   /vary@1.1.2:
     resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==}
@@ -6484,6 +6780,10 @@ packages:
     engines: {node: '>=12'}
     dev: true
 
+  /which-module@2.0.1:
+    resolution: {integrity: sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==}
+    dev: true
+
   /which@2.0.2:
     resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
     engines: {node: '>= 8'}
@@ -6511,6 +6811,10 @@ packages:
     resolution: {integrity: sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==}
     dev: true
 
+  /workerpool@6.5.1:
+    resolution: {integrity: sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==}
+    dev: true
+
   /wrap-ansi@6.2.0:
     resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==}
     engines: {node: '>=8'}
@@ -6559,6 +6863,10 @@ packages:
     engines: {node: '>=12'}
     dev: true
 
+  /y18n@4.0.3:
+    resolution: {integrity: sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==}
+    dev: true
+
   /y18n@5.0.8:
     resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==}
     engines: {node: '>=10'}
@@ -6584,11 +6892,64 @@ packages:
     engines: {node: '>= 6'}
     dev: true
 
+  /yargs-parser@18.1.3:
+    resolution: {integrity: sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==}
+    engines: {node: '>=6'}
+    dependencies:
+      camelcase: 5.3.1
+      decamelize: 1.2.0
+    dev: true
+
+  /yargs-parser@20.2.9:
+    resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==}
+    engines: {node: '>=10'}
+    dev: true
+
   /yargs-parser@21.1.1:
     resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==}
     engines: {node: '>=12'}
     dev: true
 
+  /yargs-unparser@2.0.0:
+    resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==}
+    engines: {node: '>=10'}
+    dependencies:
+      camelcase: 6.3.0
+      decamelize: 4.0.0
+      flat: 5.0.2
+      is-plain-obj: 2.1.0
+    dev: true
+
+  /yargs@15.4.1:
+    resolution: {integrity: sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==}
+    engines: {node: '>=8'}
+    dependencies:
+      cliui: 6.0.0
+      decamelize: 1.2.0
+      find-up: 4.1.0
+      get-caller-file: 2.0.5
+      require-directory: 2.1.1
+      require-main-filename: 2.0.0
+      set-blocking: 2.0.0
+      string-width: 4.2.3
+      which-module: 2.0.1
+      y18n: 4.0.3
+      yargs-parser: 18.1.3
+    dev: true
+
+  /yargs@16.2.0:
+    resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==}
+    engines: {node: '>=10'}
+    dependencies:
+      cliui: 7.0.4
+      escalade: 3.1.2
+      get-caller-file: 2.0.5
+      require-directory: 2.1.1
+      string-width: 4.2.3
+      y18n: 5.0.8
+      yargs-parser: 20.2.9
+    dev: true
+
   /yargs@17.7.2:
     resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==}
     engines: {node: '>=12'}

From 4de23c31b3f34f1849c4300e93729b244db9e01d Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Tue, 29 Oct 2024 15:01:49 +0100
Subject: [PATCH 136/150] feat: use composable to unify logic

---
 src/components/common/VnInput.vue     | 13 +++++--------
 src/components/common/VnInputDate.vue |  7 +++----
 src/components/common/VnInputTime.vue |  6 ++----
 src/components/common/VnLocation.vue  | 12 ++++--------
 src/components/common/VnSelect.vue    |  6 ++----
 src/composables/useRequired.js        | 13 +++++++++++++
 6 files changed, 29 insertions(+), 28 deletions(-)
 create mode 100644 src/composables/useRequired.js

diff --git a/src/components/common/VnInput.vue b/src/components/common/VnInput.vue
index 4534ae4a0..769e7ca44 100644
--- a/src/components/common/VnInput.vue
+++ b/src/components/common/VnInput.vue
@@ -1,9 +1,11 @@
 <script setup>
-import { computed, ref } from 'vue';
+import { computed, ref, useAttrs } from 'vue';
 import { useI18n } from 'vue-i18n';
-import { useValidator } from 'src/composables/useValidator';
-import { useAttrs } from 'vue';
+import { useRequired } from 'src/composables/useRequired';
 
+const $attrs = useAttrs();
+const { isRequired, requiredFieldRule } = useRequired($attrs);
+const { t } = useI18n();
 const emit = defineEmits([
     'update:modelValue',
     'update:options',
@@ -29,12 +31,7 @@ const $props = defineProps({
         default: true,
     },
 });
-const { validations } = useValidator();
-const $attrs = useAttrs();
 
-const { t } = useI18n();
-const isRequired = computed(() => Object.keys($attrs).includes('required'));
-const requiredFieldRule = (val) => validations().required(isRequired.value, val);
 const vnInputRef = ref(null);
 const value = computed({
     get() {
diff --git a/src/components/common/VnInputDate.vue b/src/components/common/VnInputDate.vue
index 87833f411..b822092a5 100644
--- a/src/components/common/VnInputDate.vue
+++ b/src/components/common/VnInputDate.vue
@@ -4,10 +4,13 @@ import { date } from 'quasar';
 import { useI18n } from 'vue-i18n';
 import { useAttrs } from 'vue';
 import VnDate from './VnDate.vue';
+import { useRequired } from 'src/composables/useRequired';
 
+const { isRequired, requiredFieldRule } = useRequired($attrs);
 const model = defineModel({ type: [String, Date] });
 const $attrs = useAttrs();
 const { t } = useI18n();
+
 const $props = defineProps({
     isOutlined: {
         type: Boolean,
@@ -18,11 +21,7 @@ const $props = defineProps({
         default: true,
     },
 });
-import { useValidator } from 'src/composables/useValidator';
-const { validations } = useValidator();
 
-const isRequired = computed(() => Object.keys($attrs).includes('required'));
-const requiredFieldRule = (val) => validations().required(isRequired.value, val);
 const vnInputDateRef = ref(null);
 
 const dateFormat = 'DD/MM/YYYY';
diff --git a/src/components/common/VnInputTime.vue b/src/components/common/VnInputTime.vue
index 13e90e021..a0967fdf6 100644
--- a/src/components/common/VnInputTime.vue
+++ b/src/components/common/VnInputTime.vue
@@ -2,10 +2,10 @@
 import { computed, ref, useAttrs } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { date } from 'quasar';
-import { useValidator } from 'src/composables/useValidator';
 import VnTime from './VnTime.vue';
+import { useRequired } from 'src/composables/useRequired';
 
-const { validations } = useValidator();
+const { isRequired, requiredFieldRule } = useRequired($attrs);
 const { t } = useI18n();
 const $attrs = useAttrs();
 const model = defineModel({ type: String });
@@ -21,8 +21,6 @@ const props = defineProps({
 });
 const vnInputTimeRef = ref(null);
 const initialDate = ref(model.value ?? Date.vnNew());
-const isRequired = computed(() => Object.keys($attrs).includes('required'));
-const requiredFieldRule = (val) => validations().required(isRequired.value, val);
 const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])];
 const dateFormat = 'HH:mm';
 const isPopupOpen = ref();
diff --git a/src/components/common/VnLocation.vue b/src/components/common/VnLocation.vue
index 7c6e46fdd..16f9fd580 100644
--- a/src/components/common/VnLocation.vue
+++ b/src/components/common/VnLocation.vue
@@ -2,13 +2,12 @@
 import CreateNewPostcode from 'src/components/CreateNewPostcodeForm.vue';
 import VnSelectDialog from 'components/common/VnSelectDialog.vue';
 import { useI18n } from 'vue-i18n';
-import { computed, ref } from 'vue';
+import { ref } from 'vue';
+import { useAttrs } from 'vue';
+import { useRequired } from 'src/composables/useRequired';
 const { t } = useI18n();
 const emit = defineEmits(['update:model-value', 'update:options']);
-const { validations } = useValidator();
-
-import { useAttrs } from 'vue';
-import { useValidator } from 'src/composables/useValidator';
+const { isRequired, requiredFieldRule } = useRequired($attrs);
 const $attrs = useAttrs();
 const props = defineProps({
     location: {
@@ -17,9 +16,6 @@ const props = defineProps({
     },
 });
 
-const isRequired = computed(() => Object.keys($attrs).includes('required'));
-const requiredFieldRule = (val) => validations().required(isRequired.value, val);
-
 const mixinRules = [requiredFieldRule];
 const locationProperties = [
     'postcode',
diff --git a/src/components/common/VnSelect.vue b/src/components/common/VnSelect.vue
index 4a4730784..662efcfe9 100644
--- a/src/components/common/VnSelect.vue
+++ b/src/components/common/VnSelect.vue
@@ -2,10 +2,11 @@
 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';
+import { useRequired } from 'src/composables/useRequired';
 const emit = defineEmits(['update:modelValue', 'update:options', 'remove']);
 const $attrs = useAttrs();
 const { t } = useI18n();
+const { isRequired, requiredFieldRule } = useRequired($attrs);
 const $props = defineProps({
     modelValue: {
         type: [String, Number, Object],
@@ -88,10 +89,7 @@ const $props = defineProps({
         default: false,
     },
 });
-const { validations } = useValidator();
 
-const isRequired = computed(() => Object.keys($attrs).includes('required'));
-const requiredFieldRule = (val) => validations().required(isRequired.value, val);
 const mixinRules = [requiredFieldRule, ...($attrs.rules ?? [])];
 const { optionLabel, optionValue, optionFilter, optionFilterValue, options, modelValue } =
     toRefs($props);
diff --git a/src/composables/useRequired.js b/src/composables/useRequired.js
new file mode 100644
index 000000000..e650c91f5
--- /dev/null
+++ b/src/composables/useRequired.js
@@ -0,0 +1,13 @@
+import { useValidator } from 'src/composables/useValidator';
+
+export function useRequired($attrs) {
+    const { validations } = useValidator();
+
+    const isRequired = Object.keys($attrs).includes('required');
+    const requiredFieldRule = (val) => validations().required(isRequired, val);
+
+    return {
+        isRequired,
+        requiredFieldRule,
+    };
+}

From 1761cc23e0166f3c94c4e7b8e72c8883301bb347 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Wed, 30 Oct 2024 08:29:04 +0100
Subject: [PATCH 137/150] feat: use composable to unify logic

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

diff --git a/src/components/common/VnInputDate.vue b/src/components/common/VnInputDate.vue
index b822092a5..fcc04ddf7 100644
--- a/src/components/common/VnInputDate.vue
+++ b/src/components/common/VnInputDate.vue
@@ -6,9 +6,9 @@ import { useAttrs } from 'vue';
 import VnDate from './VnDate.vue';
 import { useRequired } from 'src/composables/useRequired';
 
+const $attrs = useAttrs();
 const { isRequired, requiredFieldRule } = useRequired($attrs);
 const model = defineModel({ type: [String, Date] });
-const $attrs = useAttrs();
 const { t } = useI18n();
 
 const $props = defineProps({
diff --git a/src/components/common/VnInputTime.vue b/src/components/common/VnInputTime.vue
index a0967fdf6..6724c00b5 100644
--- a/src/components/common/VnInputTime.vue
+++ b/src/components/common/VnInputTime.vue
@@ -5,9 +5,9 @@ import { date } from 'quasar';
 import VnTime from './VnTime.vue';
 import { useRequired } from 'src/composables/useRequired';
 
+const $attrs = useAttrs();
 const { isRequired, requiredFieldRule } = useRequired($attrs);
 const { t } = useI18n();
-const $attrs = useAttrs();
 const model = defineModel({ type: String });
 const props = defineProps({
     timeOnly: {
diff --git a/src/components/common/VnLocation.vue b/src/components/common/VnLocation.vue
index 16f9fd580..6de402a26 100644
--- a/src/components/common/VnLocation.vue
+++ b/src/components/common/VnLocation.vue
@@ -7,8 +7,8 @@ import { useAttrs } from 'vue';
 import { useRequired } from 'src/composables/useRequired';
 const { t } = useI18n();
 const emit = defineEmits(['update:model-value', 'update:options']);
-const { isRequired, requiredFieldRule } = useRequired($attrs);
 const $attrs = useAttrs();
+const { isRequired, requiredFieldRule } = useRequired($attrs);
 const props = defineProps({
     location: {
         type: Object,

From 08204aa5f085d9045644a7048d537ab70b9737f0 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Mon, 4 Nov 2024 13:02:39 +0100
Subject: [PATCH 138/150] refactor(InvoiceInBasicData): use VnDms

---
 src/components/FormModel.vue                  |   1 +
 src/components/common/VnDms.vue               |   9 +-
 .../InvoiceIn/Card/InvoiceInBasicData.vue     | 440 ++++--------------
 src/pages/InvoiceIn/Card/InvoiceInCard.vue    |  17 +
 4 files changed, 114 insertions(+), 353 deletions(-)

diff --git a/src/components/FormModel.vue b/src/components/FormModel.vue
index 9ac2d38a5..c668769e5 100644
--- a/src/components/FormModel.vue
+++ b/src/components/FormModel.vue
@@ -272,6 +272,7 @@ defineExpose({
     hasChanges,
     reset,
     fetch,
+    formData,
 });
 </script>
 <template>
diff --git a/src/components/common/VnDms.vue b/src/components/common/VnDms.vue
index 920b7f137..946cd8264 100644
--- a/src/components/common/VnDms.vue
+++ b/src/components/common/VnDms.vue
@@ -31,6 +31,10 @@ const $props = defineProps({
         type: String,
         default: null,
     },
+    description: {
+        type: String,
+        default: null,
+    },
 });
 
 const warehouses = ref();
@@ -43,7 +47,8 @@ const dms = ref({});
 onMounted(() => {
     defaultData();
     if (!$props.formInitialData)
-        dms.value.description = t($props.model + 'Description', dms.value);
+        dms.value.description =
+            $props.description ?? t($props.model + 'Description', dms.value);
 });
 function onFileChange(files) {
     dms.value.hasFileAttached = !!files;
@@ -54,7 +59,6 @@ function mapperDms(data) {
     const formData = new FormData();
     const { files } = data;
     if (files) formData.append(files?.name, files);
-    delete data.files;
 
     const dms = {
         hasFile: !!data.hasFile,
@@ -78,6 +82,7 @@ async function save() {
     const body = mapperDms(dms.value);
     const response = await axios.post(getUrl(), body[0], body[1]);
     emit('onDataSaved', body[1].params, response);
+    delete dms.value.files;
     return response;
 }
 
diff --git a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue
index c9468557f..f9498f52e 100644
--- a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue
+++ b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue
@@ -3,8 +3,6 @@ import { ref, computed } from 'vue';
 import { useRoute } from 'vue-router';
 import { useI18n } from 'vue-i18n';
 import { useQuasar } from 'quasar';
-import axios from 'axios';
-import { useArrayData } from 'src/composables/useArrayData';
 import { downloadFile } from 'src/composables/downloadFile';
 import FormModel from 'components/FormModel.vue';
 import VnSelect from 'src/components/common/VnSelect.vue';
@@ -12,15 +10,15 @@ import FetchData from 'src/components/FetchData.vue';
 import VnRow from 'components/ui/VnRow.vue';
 import VnInputDate from 'src/components/common/VnInputDate.vue';
 import VnInput from 'src/components/common/VnInput.vue';
+import VnDms from 'src/components/common/VnDms.vue';
+import VnConfirm from 'src/components/ui/VnConfirm.vue';
+import axios from 'axios';
 
-const quasar = useQuasar();
 const { t } = useI18n();
 
-const dms = ref({});
 const route = useRoute();
+const quasar = useQuasar();
 const editDownloadDisabled = ref(false);
-const arrayData = useArrayData();
-const invoiceIn = computed(() => arrayData.store.data);
 const userConfig = ref(null);
 const invoiceId = computed(() => +route.params.id);
 
@@ -36,98 +34,25 @@ const warehousesRef = ref();
 const allowTypesRef = ref();
 const allowedContentTypes = ref([]);
 const sageWithholdings = ref([]);
-const inputFileRef = ref();
-const editDmsRef = ref();
-const createDmsRef = ref();
+const documentDialogRef = ref({});
+const invoiceInRef = ref({});
 
-async function checkFileExists(dmsId) {
-    if (!dmsId) return;
-    try {
-        await axios.get(`Dms/${dmsId}`, { fields: ['id'] });
-        editDownloadDisabled.value = false;
-    } catch (e) {
-        editDownloadDisabled.value = true;
-    }
-}
-
-async function setEditDms(dmsId) {
-    const { data } = await axios.get(`Dms/${dmsId}`);
-    dms.value = {
-        warehouseId: data.warehouseFk,
-        companyId: data.companyFk,
-        dmsTypeId: data.dmsTypeFk,
-        ...data,
-    };
-
-    if (!allowedContentTypes.value.length) await allowTypesRef.value.fetch();
-
-    editDmsRef.value.show();
-}
-
-async function setCreateDms() {
-    const { data } = await axios.get('DmsTypes/findOne', {
-        where: { code: 'invoiceIn' },
-    });
-    dms.value = {
-        reference: invoiceIn.value.supplierRef,
-        warehouseId: userConfig.value.warehouseFk,
-        companyId: userConfig.value.companyFk,
-        dmsTypeId: data.id,
-        description: invoiceIn.value.supplier.name,
-        hasFile: true,
-        hasFileAttached: true,
-        files: null,
-    };
-
-    createDmsRef.value.show();
-}
-
-async function onSubmit() {
-    try {
-        const isEdit = !!dms.value.id;
-        const errors = {
-            companyId: `The company can't be empty`,
-            warehouseId: `The warehouse can't be empty`,
-            dmsTypeId: `The DMS Type can't be empty`,
-            description: `The description can't be empty`,
-        };
-
-        Object.keys(errors).forEach((key) => {
-            if (!dms.value[key]) throw Error(t(errors[key]));
+function deleteFile(dmsFk) {
+    quasar
+        .dialog({
+            component: VnConfirm,
+            componentProps: {
+                title: t('globals.confirmDeletion'),
+                message: t('globals.confirmDeletionMessage'),
+            },
+        })
+        .onOk(async () => {
+            await axios.post(`dms/${dmsFk}/removeFile`);
+            invoiceInRef.value.formData.dmsFk = null;
+            invoiceInRef.value.formData.dms = undefined;
+            invoiceInRef.value.hasChanges = true;
+            invoiceInRef.value.save();
         });
-
-        if (!isEdit && !dms.value.files) throw Error(t(`The files can't be empty`));
-
-        const formData = new FormData();
-
-        if (dms.value.files) {
-            for (let i = 0; i < dms.value.files.length; i++)
-                formData.append(dms.value.files[i].name, dms.value.files[i]);
-            dms.value.hasFileAttached = true;
-        }
-        const url = isEdit ? `dms/${dms.value.id}/updateFile` : 'Dms/uploadFile';
-        const { data } = await axios.post(url, formData, {
-            params: dms.value,
-        });
-
-        if (data.length) invoiceIn.value.dmsFk = data[0].id;
-
-        if (!isEdit) {
-            createDmsRef.value.hide();
-        } else {
-            editDmsRef.value.hide();
-        }
-
-        quasar.notify({
-            message: t('globals.dataSaved'),
-            type: 'positive',
-        });
-    } catch (error) {
-        quasar.notify({
-            message: t(`${error.message}`),
-            type: 'negative',
-        });
-    }
 }
 </script>
 <template>
@@ -181,10 +106,12 @@ async function onSubmit() {
         @on-fetch="(data) => (sageWithholdings = data)"
     />
     <FormModel
+        ref="invoiceInRef"
         model="InvoiceIn"
         :go-to="`/invoice-in/${invoiceId}/vat`"
-        auto-load
         :url-update="`InvoiceIns/${invoiceId}/updateInvoiceIn`"
+        @on-fetch="(data) => (documentDialogRef.supplierName = data.supplier.nickname)"
+        auto-load
     >
         <template #form="{ data }">
             <VnRow>
@@ -242,16 +169,18 @@ async function onSubmit() {
                         </QItem>
                     </template>
                 </VnSelect>
-                <VnInput
-                    :label="t('Document')"
-                    v-model="data.dmsFk"
-                    clearable
-                    clear-icon="close"
-                    @update:model-value="checkFileExists(data.dmsFk)"
-                >
-                    <template #prepend>
+
+                <div class="row no-wrap">
+                    <VnInput
+                        :label="t('Document')"
+                        v-model="data.dmsFk"
+                        clearable
+                        clear-icon="close"
+                        class="full-width"
+                        :disable="true"
+                    />
+                    <div v-if="data.dmsFk" class="row no-wrap q-pa-xs q-gutter-x-xs">
                         <QBtn
-                            v-if="data.dmsFk"
                             :class="{
                                 'no-pointer-events': editDownloadDisabled,
                             }"
@@ -262,33 +191,51 @@ async function onSubmit() {
                             round
                             @click="downloadFile(data.dmsFk)"
                         />
-                    </template>
-                    <template #append>
                         <QBtn
                             :class="{
                                 'no-pointer-events': editDownloadDisabled,
                             }"
                             :disable="editDownloadDisabled"
-                            v-if="data.dmsFk"
                             icon="edit"
                             round
                             padding="xs"
-                            @click="setEditDms(data.dmsFk)"
+                            @click="
+                                () => {
+                                    documentDialogRef.show = true;
+                                    documentDialogRef.dms = data.dms;
+                                }
+                            "
                         >
                             <QTooltip>{{ t('Edit document') }}</QTooltip>
                         </QBtn>
                         <QBtn
-                            v-else
-                            icon="add_circle"
-                            round
-                            shortcut="+"
+                            :class="{
+                                'no-pointer-events': editDownloadDisabled,
+                            }"
+                            :disable="editDownloadDisabled"
+                            icon="delete"
+                            :title="t('Delete file')"
                             padding="xs"
-                            @click="setCreateDms()"
-                        >
-                            <QTooltip>{{ t('Create document') }}</QTooltip>
-                        </QBtn>
-                    </template>
-                </VnInput>
+                            round
+                            @click="deleteFile(data.dmsFk)"
+                        />
+                    </div>
+                    <QBtn
+                        v-else
+                        icon="add_circle"
+                        round
+                        shortcut="+"
+                        padding="xs"
+                        @click="
+                            () => {
+                                documentDialogRef.show = true;
+                                delete documentDialogRef.dms;
+                            }
+                        "
+                    >
+                        <QTooltip>{{ t('Create document') }}</QTooltip>
+                    </QBtn>
+                </div>
             </VnRow>
             <VnRow>
                 <VnSelect
@@ -319,237 +266,28 @@ async function onSubmit() {
             </VnRow>
         </template>
     </FormModel>
-    <QDialog ref="editDmsRef">
-        <QForm @submit="onSubmit()" class="all-pointer-events">
-            <QCard class="q-pa-sm">
-                <QCardSection class="row items-center q-pb-none">
-                    <span class="text-primary text-h6">
-                        <QIcon name="edit" class="q-mr-xs" />
-                        {{ t('Edit document') }}
-                    </span>
-                    <QSpace />
-                    <QBtn icon="close" flat round dense v-close-popup />
-                </QCardSection>
-                <QCardSection class="q-py-none">
-                    <QItem>
-                        <VnInput
-                            class="full-width q-pa-xs"
-                            :label="t('Reference')"
-                            v-model="dms.reference"
-                            clearable
-                            clear-icon="close"
-                        />
-                        <VnSelect
-                            class="full-width q-pa-xs"
-                            :label="t('Company')"
-                            v-model="dms.companyId"
-                            :options="companies"
-                            option-value="id"
-                            option-label="code"
-                            :required="true"
-                        />
-                    </QItem>
-                    <QItem>
-                        <VnSelect
-                            class="full-width q-pa-xs"
-                            :label="t('Warehouse')"
-                            v-model="dms.warehouseId"
-                            :options="warehouses"
-                            option-value="id"
-                            option-label="name"
-                            :required="true"
-                        />
-                        <VnSelect
-                            class="full-width q-pa-xs"
-                            :label="t('Type')"
-                            v-model="dms.dmsTypeId"
-                            :options="dmsTypes"
-                            option-value="id"
-                            option-label="name"
-                            :required="true"
-                        />
-                    </QItem>
-                    <QItem>
-                        <VnInput
-                            :label="t('Description')"
-                            v-model="dms.description"
-                            :required="true"
-                            type="textarea"
-                            class="full-width q-pa-xs"
-                            size="lg"
-                            autogrow
-                            clearable
-                            clear-icon="close"
-                        />
-                    </QItem>
-                    <QItem>
-                        <QFile
-                            ref="inputFileRef"
-                            class="full-width q-pa-xs"
-                            :label="t('File')"
-                            v-model="dms.files"
-                            multiple
-                            :accept="allowedContentTypes.join(',')"
-                            clearable
-                            clear-icon="close"
-                        >
-                            <template #append>
-                                <QBtn
-                                    icon="attach_file_add"
-                                    flat
-                                    round
-                                    padding="xs"
-                                    @click="inputFileRef.pickFiles()"
-                                >
-                                    <QTooltip>
-                                        {{ t('globals.selectFile') }}
-                                    </QTooltip>
-                                </QBtn>
-                                <QBtn icon="info" flat round padding="xs">
-                                    <QTooltip max-width="30rem">
-                                        {{
-                                            `${t(
-                                                'Allowed content types'
-                                            )}: ${allowedContentTypes.join(', ')}`
-                                        }}
-                                    </QTooltip>
-                                </QBtn>
-                            </template>
-                        </QFile>
-                    </QItem>
-                    <QItem>
-                        <QCheckbox
-                            :label="t('Generate identifier for original file')"
-                            v-model="dms.hasFile"
-                        />
-                    </QItem>
-                </QCardSection>
-                <QCardActions class="justify-end">
-                    <QBtn
-                        flat
-                        :label="t('globals.close')"
-                        color="primary"
-                        v-close-popup
-                    />
-                    <QBtn :label="t('globals.save')" color="primary" @click="onSubmit" />
-                </QCardActions>
-            </QCard>
-        </QForm>
-    </QDialog>
-    <QDialog ref="createDmsRef">
-        <QForm @submit="onSubmit()" class="all-pointer-events">
-            <QCard class="q-pa-sm">
-                <QCardSection class="row items-center q-pb-none">
-                    <span class="text-primary text-h6">
-                        <QIcon name="edit" class="q-mr-xs" />
-                        {{ t('Create document') }}
-                    </span>
-                    <QSpace />
-                    <QBtn icon="close" flat round dense v-close-popup />
-                </QCardSection>
-                <QCardSection class="q-pb-none">
-                    <QItem>
-                        <VnInput
-                            class="full-width q-pa-xs"
-                            :label="t('Reference')"
-                            v-model="dms.reference"
-                        />
-                        <VnSelect
-                            class="full-width q-pa-xs"
-                            :label="`${t('Company')}*`"
-                            v-model="dms.companyId"
-                            :options="companies"
-                            option-value="id"
-                            option-label="code"
-                            :required="true"
-                        />
-                    </QItem>
-                    <QItem>
-                        <VnSelect
-                            class="full-width q-pa-xs"
-                            :label="`${t('Warehouse')}*`"
-                            v-model="dms.warehouseId"
-                            :options="warehouses"
-                            option-value="id"
-                            option-label="name"
-                            :required="true"
-                        />
-                        <VnSelect
-                            class="full-width q-pa-xs"
-                            :label="`${t('Type')}*`"
-                            v-model="dms.dmsTypeId"
-                            :options="dmsTypes"
-                            option-value="id"
-                            option-label="name"
-                            :required="true"
-                        />
-                    </QItem>
-                    <QItem>
-                        <VnInput
-                            class="full-width q-pa-xs"
-                            type="textarea"
-                            size="lg"
-                            autogrow
-                            :label="`${t('Description')}*`"
-                            v-model="dms.description"
-                            clearable
-                            clear-icon="close"
-                            :rules="[(val) => val.length || t('Required field')]"
-                        />
-                    </QItem>
-                    <QItem>
-                        <QFile
-                            ref="inputFileRef"
-                            class="full-width q-pa-xs"
-                            :label="t('File')"
-                            v-model="dms.files"
-                            multiple
-                            :accept="allowedContentTypes.join(',')"
-                            clearable
-                            clear-icon="close"
-                        >
-                            <template #append>
-                                <QBtn
-                                    icon="attach_file_add"
-                                    flat
-                                    round
-                                    padding="xs"
-                                    @click="inputFileRef.pickFiles()"
-                                >
-                                    <QTooltip>
-                                        {{ t('globals.selectFile') }}
-                                    </QTooltip>
-                                </QBtn>
-                                <QBtn icon="info" flat round padding="xs">
-                                    <QTooltip max-width="30rem">
-                                        {{
-                                            `${t(
-                                                'Allowed content types'
-                                            )}: ${allowedContentTypes.join(', ')}`
-                                        }}
-                                    </QTooltip>
-                                </QBtn>
-                            </template>
-                        </QFile>
-                    </QItem>
-                    <QItem>
-                        <QCheckbox
-                            :label="t('Generate identifier for original file')"
-                            v-model="dms.hasFile"
-                        />
-                    </QItem>
-                </QCardSection>
-                <QCardActions align="right">
-                    <QBtn
-                        flat
-                        :label="t('globals.close')"
-                        color="primary"
-                        v-close-popup
-                    />
-                    <QBtn :label="t('globals.save')" color="primary" @click="onSubmit" />
-                </QCardActions>
-            </QCard>
-        </QForm>
+    <QDialog v-model="documentDialogRef.show">
+        <VnDms
+            model="dms"
+            default-dms-code="invoiceIn"
+            :form-initial-data="documentDialogRef.dms"
+            :url="
+                documentDialogRef.dms
+                    ? `Dms/${documentDialogRef.dms.id}/updateFile`
+                    : 'Dms/uploadFile'
+            "
+            :description="documentDialogRef.supplierName"
+            @on-data-saved="
+                (_, { data }) => {
+                    let dmsData = data;
+                    if (Array.isArray(data)) dmsData = data[0];
+                    invoiceInRef.formData.dmsFk = dmsData.id;
+                    invoiceInRef.formData.dms = dmsData;
+                    invoiceInRef.hasChanges = true;
+                    invoiceInRef.save();
+                }
+            "
+        />
     </QDialog>
 </template>
 <style lang="scss" scoped>
diff --git a/src/pages/InvoiceIn/Card/InvoiceInCard.vue b/src/pages/InvoiceIn/Card/InvoiceInCard.vue
index 0fe2a2368..b16183e52 100644
--- a/src/pages/InvoiceIn/Card/InvoiceInCard.vue
+++ b/src/pages/InvoiceIn/Card/InvoiceInCard.vue
@@ -20,6 +20,23 @@ const filter = {
         { relation: 'invoiceInDueDay' },
         { relation: 'company' },
         { relation: 'currency' },
+        {
+            relation: 'dms',
+            scope: {
+                fields: [
+                    'dmsTypeFk',
+                    'reference',
+                    'hardCopyNumber',
+                    'workerFk',
+                    'description',
+                    'hasFile',
+                    'file',
+                    'created',
+                    'companyFk',
+                    'warehouseFk',
+                ],
+            },
+        },
     ],
 };
 

From e050a077d4fdb77e62598c9466eb282fcb67e189 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Mon, 4 Nov 2024 13:41:37 +0100
Subject: [PATCH 139/150] test: refactor e2e

---
 src/components/common/VnDms.vue               |  1 +
 src/components/ui/VnConfirm.vue               |  1 +
 .../InvoiceIn/Card/InvoiceInBasicData.vue     |  7 +++-
 .../invoiceIn/invoiceInBasicData.spec.js      | 33 +++++++++++--------
 4 files changed, 28 insertions(+), 14 deletions(-)

diff --git a/src/components/common/VnDms.vue b/src/components/common/VnDms.vue
index 946cd8264..3f9e65740 100644
--- a/src/components/common/VnDms.vue
+++ b/src/components/common/VnDms.vue
@@ -170,6 +170,7 @@ function addDefaultData(data) {
                     @update:model-value="onFileChange(dms.files)"
                     class="required"
                     :display-value="dms.file"
+                    data-cy="VnDms_inputFile"
                 >
                     <template #append>
                         <QIcon
diff --git a/src/components/ui/VnConfirm.vue b/src/components/ui/VnConfirm.vue
index d6b1ac0a3..0b1913383 100644
--- a/src/components/ui/VnConfirm.vue
+++ b/src/components/ui/VnConfirm.vue
@@ -103,6 +103,7 @@ function cancel() {
                     @click="confirm()"
                     unelevated
                     autofocus
+                    data-cy="VnConfirm_confirm"
                 />
             </QCardActions>
         </QCard>
diff --git a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue
index f9498f52e..209681b7c 100644
--- a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue
+++ b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue
@@ -179,7 +179,11 @@ function deleteFile(dmsFk) {
                         class="full-width"
                         :disable="true"
                     />
-                    <div v-if="data.dmsFk" class="row no-wrap q-pa-xs q-gutter-x-xs">
+                    <div
+                        v-if="data.dmsFk"
+                        class="row no-wrap q-pa-xs q-gutter-x-xs"
+                        data-cy="dms-buttons"
+                    >
                         <QBtn
                             :class="{
                                 'no-pointer-events': editDownloadDisabled,
@@ -232,6 +236,7 @@ function deleteFile(dmsFk) {
                                 delete documentDialogRef.dms;
                             }
                         "
+                        data-cy="dms-create"
                     >
                         <QTooltip>{{ t('Create document') }}</QTooltip>
                     </QBtn>
diff --git a/test/cypress/integration/invoiceIn/invoiceInBasicData.spec.js b/test/cypress/integration/invoiceIn/invoiceInBasicData.spec.js
index e1939fe5a..2016fca6d 100644
--- a/test/cypress/integration/invoiceIn/invoiceInBasicData.spec.js
+++ b/test/cypress/integration/invoiceIn/invoiceInBasicData.spec.js
@@ -2,9 +2,8 @@
 describe('InvoiceInBasicData', () => {
     const formInputs = '.q-form > .q-card input';
     const firstFormSelect = '.q-card > .vn-row:nth-child(1) > .q-select';
-    const documentBtns = '.q-form .q-field button';
+    const documentBtns = '[data-cy="dms-buttons"] button';
     const dialogInputs = '.q-dialog input';
-    const dialogActionBtns = '.q-card__actions button';
 
     beforeEach(() => {
         cy.login('developer');
@@ -21,27 +20,35 @@ describe('InvoiceInBasicData', () => {
         cy.get(formInputs).eq(1).invoke('val').should('eq', '4739');
     });
 
-    it('should edit the dms data', () => {
+    it('should edit, remove and create the dms data', () => {
         const firtsInput = 'Ticket:65';
         const secondInput = "I don't know what posting here!";
 
+        //edit
         cy.get(documentBtns).eq(1).click();
         cy.get(dialogInputs).eq(0).type(`{selectall}${firtsInput}`);
         cy.get('textarea').type(`{selectall}${secondInput}`);
-        cy.get(dialogActionBtns).eq(1).click();
-
+        cy.get('[data-cy="FormModelPopup_save"]').click();
         cy.get(documentBtns).eq(1).click();
         cy.get(dialogInputs).eq(0).invoke('val').should('eq', firtsInput);
         cy.get('textarea').invoke('val').should('eq', secondInput);
-    });
+        cy.get('[data-cy="FormModelPopup_save"]').click();
+        cy.checkNotification('Data saved');
 
-    it('should throw an error creating a new dms if a file is not attached', () => {
-        cy.get(formInputs).eq(7).type('{selectall}{backspace}');
-        cy.get(documentBtns).eq(0).click();
-        cy.get(dialogActionBtns).eq(1).click();
-        cy.get('.q-notification__message').should(
-            'have.text',
-            "The files can't be empty"
+        //remove
+        cy.get(documentBtns).eq(2).click();
+        cy.get('[data-cy="VnConfirm_confirm"]').click();
+        cy.checkNotification('Data saved');
+
+        //create
+        cy.get('[data-cy="dms-create"]').eq(0).click();
+        cy.get('[data-cy="VnDms_inputFile"').selectFile(
+            'test/cypress/fixtures/image.jpg',
+            {
+                force: true,
+            }
         );
+        cy.get('[data-cy="FormModelPopup_save"]').click();
+        cy.checkNotification('Data saved');
     });
 });

From 1f9d850fbeff23c8d44367704f9945c15920a709 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Tue, 5 Nov 2024 10:02:29 +0100
Subject: [PATCH 140/150] chore: refs #8078 rollback ref

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

diff --git a/src/pages/Ticket/Card/TicketDescriptorMenu.vue b/src/pages/Ticket/Card/TicketDescriptorMenu.vue
index 8efd0f1e1..bf4a1efb4 100644
--- a/src/pages/Ticket/Card/TicketDescriptorMenu.vue
+++ b/src/pages/Ticket/Card/TicketDescriptorMenu.vue
@@ -659,7 +659,7 @@ async function uploadDocuware(force) {
             </QList>
         </QMenu>
     </QItem>
-    <QItem @click="$refs.weightDialog.dialogRef.show()" v-ripple clickable>
+    <QItem @click="$refs.weightDialog.show()" v-ripple clickable>
         <QItemSection avatar>
             <QIcon name="weight" />
         </QItemSection>

From 86e6bef90f18d98c391a2046c9223a140e289f59 Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Tue, 5 Nov 2024 10:37:35 +0100
Subject: [PATCH 141/150] chore: refs #8078 fiz tests

---
 test/vitest/__tests__/components/VnTable.spec.js | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/test/vitest/__tests__/components/VnTable.spec.js b/test/vitest/__tests__/components/VnTable.spec.js
index d11c2b6c3..162df727d 100644
--- a/test/vitest/__tests__/components/VnTable.spec.js
+++ b/test/vitest/__tests__/components/VnTable.spec.js
@@ -20,12 +20,12 @@ describe('VnTable', () => {
     describe('handleSelection()', () => {
         const rows = [{ $index: 0 }, { $index: 1 }, { $index: 2 }];
         const selectedRows = [{ $index: 1 }];
-        it('should add rows to selected when shift key is pressed and rows are added', () => {
+        it('should add rows to selected when shift key is pressed and rows are added except last one', () => {
             vm.handleSelection(
                 { evt: { shiftKey: true }, added: true, rows: selectedRows },
                 rows
             );
-            expect(vm.selected).toEqual([{ $index: 0 }, { $index: 1 }]);
+            expect(vm.selected).toEqual([{ $index: 0 }]);
         });
 
         it('should not add rows to selected when shift key is not pressed', () => {

From 4a95a0e39ae71f8ae8210bfe1d4cabc6021d9e0e Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Tue, 5 Nov 2024 11:59:38 +0100
Subject: [PATCH 142/150] chore: refs #7273 sticky add btn & refactor

---
 src/pages/Item/Card/ItemTags.vue | 37 +++++++++++++++++---------------
 1 file changed, 20 insertions(+), 17 deletions(-)

diff --git a/src/pages/Item/Card/ItemTags.vue b/src/pages/Item/Card/ItemTags.vue
index 6f31d0cf8..e59085402 100644
--- a/src/pages/Item/Card/ItemTags.vue
+++ b/src/pages/Item/Card/ItemTags.vue
@@ -109,7 +109,11 @@ const insertTag = (rows) => {
             >
                 <template #body="{ rows, validate }">
                     <QCard class="q-px-lg q-pt-md q-pb-sm">
-                        <VnRow v-for="(row, index) in rows" :key="index">
+                        <VnRow
+                            v-for="(row, index) in rows"
+                            :key="index"
+                            class="items-center"
+                        >
                             <VnSelect
                                 :label="t('itemTags.tag')"
                                 :options="tagOptions"
@@ -153,13 +157,14 @@ const insertTag = (rows) => {
                                 :required="true"
                                 :rules="validate('itemTag.priority')"
                             />
-                            <div class="row justify-center items-center" style="flex: 0">
+                            <div class="row justify-center" style="flex: 0">
                                 <QIcon
                                     @click="itemTagsRef.remove([row])"
                                     class="fill-icon-on-hover"
                                     color="primary"
                                     name="delete"
                                     size="sm"
+                                    dense
                                 >
                                     <QTooltip>
                                         {{ t('itemTags.removeTag') }}
@@ -167,22 +172,20 @@ const insertTag = (rows) => {
                                 </QIcon>
                             </div>
                         </VnRow>
-                        <VnRow class="justify-center items-center">
-                            <QBtn
-                                @click="insertTag(rows)"
-                                class="cursor-pointer"
-                                color="primary"
-                                flat
-                                icon="add"
-                                shortcut="+"
-                                style="flex: 0"
-                            >
-                                <QTooltip>
-                                    {{ t('itemTags.addTag') }}
-                                </QTooltip>
-                            </QBtn>
-                        </VnRow>
                     </QCard>
+                    <QPageSticky position="bottom-right" :offset="[25, 25]">
+                        <QBtn
+                            @click="insertTag(rows)"
+                            color="primary"
+                            icon="add"
+                            shortcut="+"
+                            fab
+                        >
+                            <QTooltip>
+                                {{ t('itemTags.addTag') }}
+                            </QTooltip>
+                        </QBtn>
+                    </QPageSticky>
                 </template>
             </CrudModel>
         </QPage>

From 9959fbf005f9576fda495ac7b8bcd04542c617ef Mon Sep 17 00:00:00 2001
From: jorgep <jorgep@verdnatura.es>
Date: Tue, 5 Nov 2024 12:23:11 +0100
Subject: [PATCH 143/150] fix: refs #7273 use same filter

---
 src/pages/Item/ItemList.vue | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/src/pages/Item/ItemList.vue b/src/pages/Item/ItemList.vue
index a480cfff6..f789d2cd0 100644
--- a/src/pages/Item/ItemList.vue
+++ b/src/pages/Item/ItemList.vue
@@ -12,6 +12,8 @@ import ItemSummary from '../Item/Card/ItemSummary.vue';
 import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
 import ItemDescriptorProxy from './Card/ItemDescriptorProxy.vue';
 import { cloneItem } from 'src/pages/Item/composables/cloneItem';
+import RightMenu from 'src/components/common/RightMenu.vue';
+import ItemListFilter from './ItemListFilter.vue';
 
 const entityId = computed(() => route.params.id);
 const { openCloneDialog } = cloneItem();
@@ -311,6 +313,11 @@ const columns = computed(() => [
         :label="t('item.searchbar.label')"
         :info="t('You can search by id')"
     />
+    <RightMenu>
+        <template #right-panel>
+            <ItemListFilter data-key="ItemList" />
+        </template>
+    </RightMenu>
     <VnTable
         ref="tableRef"
         data-key="ItemList"
@@ -329,6 +336,7 @@ const columns = computed(() => [
         auto-load
         redirect="Item"
         :is-editable="false"
+        :right-search="false"
         :filer="itemFilter"
     >
         <template #column-id="{ row }">

From abd7b76636c53f69c5d4ff6e0cec87f037170fa8 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Tue, 5 Nov 2024 13:50:25 +0100
Subject: [PATCH 144/150] feat(VnInput): empty to null

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

diff --git a/src/components/common/VnInput.vue b/src/components/common/VnInput.vue
index 1246eedcd..93cf332a3 100644
--- a/src/components/common/VnInput.vue
+++ b/src/components/common/VnInput.vue
@@ -27,6 +27,10 @@ const $props = defineProps({
         type: Boolean,
         default: true,
     },
+    emptyToNull: {
+        type: Boolean,
+        default: true,
+    },
 });
 const { validations } = useValidator();
 
@@ -39,6 +43,7 @@ const value = computed({
         return $props.modelValue;
     },
     set(value) {
+        if ($props.emptyToNull && value === '') value = null;
         emit('update:modelValue', value);
     },
 });

From a2bbf4474d4a801ece1cb52d57f61f55ecfe2ebb Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Wed, 6 Nov 2024 13:52:59 +0100
Subject: [PATCH 145/150] fix(InvoiceOutGlobal): parallelism

---
 src/pages/InvoiceOut/InvoiceOutGlobalForm.vue |  7 ++--
 src/stores/invoiceOutGlobal.js                | 37 +++++++++----------
 2 files changed, 21 insertions(+), 23 deletions(-)

diff --git a/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue b/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue
index 5bcb21001..e85f1f44c 100644
--- a/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue
+++ b/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue
@@ -13,7 +13,7 @@ const { t } = useI18n();
 const invoiceOutGlobalStore = useInvoiceOutGlobalStore();
 
 // invoiceOutGlobalStore state and getters
-const { initialDataLoading, formInitialData, invoicing, status } =
+const { initialDataLoading, formInitialData, status } =
     storeToRefs(invoiceOutGlobalStore);
 
 // invoiceOutGlobalStore actions
@@ -151,9 +151,8 @@ onMounted(async () => {
                 rounded
             />
         </div>
-
         <QBtn
-            v-if="!invoicing"
+            v-if="!getStatus || getStatus === 'stopping'"
             :label="t('invoiceOut')"
             type="submit"
             color="primary"
@@ -163,7 +162,7 @@ onMounted(async () => {
             dense
         />
         <QBtn
-            v-if="invoicing"
+            v-else
             :label="t('stop')"
             color="primary"
             class="q-mt-md full-width"
diff --git a/src/stores/invoiceOutGlobal.js b/src/stores/invoiceOutGlobal.js
index 42acac013..b17f41564 100644
--- a/src/stores/invoiceOutGlobal.js
+++ b/src/stores/invoiceOutGlobal.js
@@ -93,7 +93,7 @@ export const useInvoiceOutGlobalStore = defineStore({
 
         async makeInvoice(formData, clientsToInvoice) {
             this.invoicing = true;
-            this.status = 'packageInvoicing';
+            const promises = [];
             try {
                 this.printer = formData.printer;
                 const params = {
@@ -118,10 +118,11 @@ export const useInvoiceOutGlobalStore = defineStore({
                     );
                     throw new Error("There aren't addresses to invoice");
                 }
-
-                for (const address of this.addresses) {
-                    await this.invoiceClient(address, formData);
+                this.status = 'invoicing';
+                for (let index = 0; index < this.parallelism; index++) {
+                    promises.push(this.invoiceClient(formData, index));
                 }
+                await Promise.all(promises);
             } catch (err) {
                 this.handleError(err);
             }
@@ -171,17 +172,15 @@ export const useInvoiceOutGlobalStore = defineStore({
             }
         },
 
-        async invoiceClient(address, formData) {
+        async invoiceClient(formData, index) {
+            const address = this.addresses[index];
+            if (!address || !this.status || this.status == 'stopping') {
+                this.status = 'stopping';
+                this.invoicing = false;
+                return;
+            }
+            console.log('address: ', address);
             try {
-                if (this.nRequests === this.parallelism || this.isInvoicing) return;
-
-                if (this.status === 'stopping') {
-                    if (this.nRequests) return;
-                    this.invoicing = false;
-                    this.status = 'done';
-                    return;
-                }
-
                 const params = {
                     clientId: address.clientId,
                     addressId: address.id,
@@ -191,13 +190,11 @@ export const useInvoiceOutGlobalStore = defineStore({
                     serialType: formData.serialType,
                 };
 
-                this.status = 'invoicing';
                 this.invoicing = true;
 
                 const { data } = await axios.post('InvoiceOuts/invoiceClient', params);
 
                 if (data) await this.makePdfAndNotify(data, address);
-                this.addressIndex++;
                 this.isInvoicing = false;
             } catch (err) {
                 if (err?.response?.status >= 400 && err?.response?.status < 500) {
@@ -205,13 +202,16 @@ export const useInvoiceOutGlobalStore = defineStore({
                     return;
                 } else {
                     this.invoicing = false;
-                    this.status = 'done';
                     notify(
                         'invoiceOut.globalInvoices.errors.criticalInvoiceError',
                         'negative'
                     );
                     throw new Error('Critical invoicing error, process stopped');
                 }
+            } finally {
+                this.addressIndex++;
+                if (this.status != 'stopping')
+                    await this.invoiceClient(formData, this.addressIndex);
             }
         },
 
@@ -234,7 +234,6 @@ export const useInvoiceOutGlobalStore = defineStore({
 
         handleError(err) {
             this.invoicing = false;
-            this.status = null;
             throw err;
         },
 
@@ -279,7 +278,7 @@ export const useInvoiceOutGlobalStore = defineStore({
                 return 0;
             }
             let porcentaje = (state.addressIndex / this.getNAddresses) * 100;
-            return porcentaje;
+            return porcentaje?.toFixed(2);
         },
         getAddressNumber(state) {
             return state.addressIndex;

From ead241e7daa826608dfe522349f4691a06dbc2c9 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Wed, 6 Nov 2024 13:53:58 +0100
Subject: [PATCH 146/150] chore: remove console.log

---
 src/stores/invoiceOutGlobal.js | 1 -
 1 file changed, 1 deletion(-)

diff --git a/src/stores/invoiceOutGlobal.js b/src/stores/invoiceOutGlobal.js
index b17f41564..35f834f3d 100644
--- a/src/stores/invoiceOutGlobal.js
+++ b/src/stores/invoiceOutGlobal.js
@@ -179,7 +179,6 @@ export const useInvoiceOutGlobalStore = defineStore({
                 this.invoicing = false;
                 return;
             }
-            console.log('address: ', address);
             try {
                 const params = {
                     clientId: address.clientId,

From 6c9627c6c51c5dfeb6d61906e0193165a11e5fe3 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Wed, 6 Nov 2024 17:51:21 +0100
Subject: [PATCH 147/150] fix(worker): add searchurls

---
 src/pages/Worker/Card/WorkerBalance.vue       | 19 ++++++++++++++++---
 src/pages/Worker/Card/WorkerFormation.vue     |  1 +
 src/pages/Worker/Card/WorkerMedical.vue       |  1 +
 .../Card/WorkerNotificationsManager.vue       |  1 +
 src/pages/Worker/Card/WorkerPda.vue           |  1 +
 5 files changed, 20 insertions(+), 3 deletions(-)

diff --git a/src/pages/Worker/Card/WorkerBalance.vue b/src/pages/Worker/Card/WorkerBalance.vue
index c5367a281..25ab92c9b 100644
--- a/src/pages/Worker/Card/WorkerBalance.vue
+++ b/src/pages/Worker/Card/WorkerBalance.vue
@@ -3,11 +3,12 @@ import { ref, computed } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useRoute } from 'vue-router';
 import VnTable from 'components/VnTable/VnTable.vue';
+import FetchData from 'src/components/FetchData.vue';
 const tableRef = ref();
+const payrollComponents = ref([]);
 const { t } = useI18n();
 const route = useRoute();
 const entityId = computed(() => route.params.id);
-
 const columns = computed(() => [
     {
         align: 'left',
@@ -25,8 +26,9 @@ const columns = computed(() => [
         create: true,
         component: 'select',
         attrs: {
-            url: 'payrollComponents',
-            fields: ['id', 'name'],
+            options: payrollComponents,
+            optionLabel: 'name',
+            optionValue: 'id',
         },
         cardVisible: true,
     },
@@ -73,6 +75,16 @@ const columns = computed(() => [
 ]);
 </script>
 <template>
+    <FetchData
+        url="PayrollComponents"
+        :filter="{
+            fields: ['id', 'name'],
+            where: { name: { neq: '' } },
+            order: 'name ASC',
+        }"
+        @on-fetch="(data) => (payrollComponents = data)"
+        auto-load
+    />
     <VnTable
         ref="tableRef"
         data-key="WorkerBalance"
@@ -94,6 +106,7 @@ const columns = computed(() => [
         :is-editable="true"
         :use-model="true"
         :default-remove="false"
+        search-url="balance"
     />
 </template>
 <i18n>
diff --git a/src/pages/Worker/Card/WorkerFormation.vue b/src/pages/Worker/Card/WorkerFormation.vue
index c11dd019e..596691d2e 100644
--- a/src/pages/Worker/Card/WorkerFormation.vue
+++ b/src/pages/Worker/Card/WorkerFormation.vue
@@ -134,6 +134,7 @@ const columns = computed(() => [
         :is-editable="true"
         :use-model="true"
         :default-remove="false"
+        search-url="formation"
     />
 </template>
 <style lang="scss" scoped>
diff --git a/src/pages/Worker/Card/WorkerMedical.vue b/src/pages/Worker/Card/WorkerMedical.vue
index fab1416c9..9620c23c2 100644
--- a/src/pages/Worker/Card/WorkerMedical.vue
+++ b/src/pages/Worker/Card/WorkerMedical.vue
@@ -100,5 +100,6 @@ const columns = [
         :is-editable="true"
         :use-model="true"
         :default-remove="false"
+        search-url="medical"
     />
 </template>
diff --git a/src/pages/Worker/Card/WorkerNotificationsManager.vue b/src/pages/Worker/Card/WorkerNotificationsManager.vue
index 731e073cd..4ebe9b0dd 100644
--- a/src/pages/Worker/Card/WorkerNotificationsManager.vue
+++ b/src/pages/Worker/Card/WorkerNotificationsManager.vue
@@ -70,6 +70,7 @@ function setNotifications(data) {
         :default-remove="false"
         :default-save="false"
         @on-fetch="setNotifications"
+        search-url="notifications"
     >
         <template #body>
             <div
diff --git a/src/pages/Worker/Card/WorkerPda.vue b/src/pages/Worker/Card/WorkerPda.vue
index a53aac270..a5e8ae3fa 100644
--- a/src/pages/Worker/Card/WorkerPda.vue
+++ b/src/pages/Worker/Card/WorkerPda.vue
@@ -63,6 +63,7 @@ function reloadData() {
             url="DeviceProductionUsers"
             :filter="{ where: { userFk: routeId } }"
             order="id"
+            search-url="pda"
             auto-load
         >
             <template #body="{ rows }">

From 2bf7fa46ca8a546e4e5fb379b502407e55c92d0f Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 7 Nov 2024 12:53:17 +0100
Subject: [PATCH 148/150] perf: remove appendParams

---
 src/components/VnTable/VnTable.vue                     | 6 +-----
 src/composables/useArrayData.js                        | 3 +--
 src/pages/Customer/components/CustomerSummaryTable.vue | 3 +--
 3 files changed, 3 insertions(+), 9 deletions(-)

diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index cdf450966..a4948156a 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -62,7 +62,7 @@ const $props = defineProps({
         default: 'flex-one',
     },
     searchUrl: {
-        type: String,
+        type: [String, Boolean],
         default: 'table',
     },
     isEditable: {
@@ -73,10 +73,6 @@ const $props = defineProps({
         type: Boolean,
         default: false,
     },
-    appendParams: {
-        type: Boolean,
-        default: true,
-    },
     hasSubToolbar: {
         type: Boolean,
         default: null,
diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js
index e33cb8b78..747c6ab64 100644
--- a/src/composables/useArrayData.js
+++ b/src/composables/useArrayData.js
@@ -247,9 +247,8 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) {
     }
 
     function updateStateParams() {
-        if (!route) return;
         const newUrl = { path: route.path, query: { ...(route.query ?? {}) } };
-        if (store?.appendParams ?? true)
+        if (store?.searchUrl)
             newUrl.query[store.searchUrl] = JSON.stringify(store.currentFilter);
 
         if (store.navigate) {
diff --git a/src/pages/Customer/components/CustomerSummaryTable.vue b/src/pages/Customer/components/CustomerSummaryTable.vue
index 745cbf29e..59e82e252 100644
--- a/src/pages/Customer/components/CustomerSummaryTable.vue
+++ b/src/pages/Customer/components/CustomerSummaryTable.vue
@@ -183,7 +183,6 @@ const getItemPackagingType = (ticketSales) => {
         :column-search="false"
         url="Tickets"
         :columns="columns"
-        append-params="false"
         :without-header="true"
         auto-load
         :row-click="rowClick"
@@ -191,7 +190,7 @@ const getItemPackagingType = (ticketSales) => {
         :disable-option="{ card: true, table: true }"
         class="full-width"
         :disable-infinite-scroll="true"
-        search-url="tickets"
+        :search-url="false"
     >
         <template #column-nickname="{ row }">
             <span class="link">

From e210ad7de4d0eaff62cdcd176fdef8026003adac Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 7 Nov 2024 13:06:33 +0100
Subject: [PATCH 149/150] Merge branch 'dev' into fix_customer_issues

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

diff --git a/src/pages/Customer/components/CustomerSummaryTable.vue b/src/pages/Customer/components/CustomerSummaryTable.vue
index e0ef6a5ef..59e82e252 100644
--- a/src/pages/Customer/components/CustomerSummaryTable.vue
+++ b/src/pages/Customer/components/CustomerSummaryTable.vue
@@ -63,11 +63,7 @@ const columns = computed(() => [
     },
     {
         align: 'left',
-<<<<<<< HEAD
         format: (row) => dashIfEmpty(row.agencyMode?.name),
-=======
-        format: (row, dashIfEmpty) => dashIfEmpty(row.agencyMode?.name),
->>>>>>> dev
         columnClass: 'expand',
         label: t('Agency'),
     },

From 0b5be9e67fef0e164c3d6ca5c429892263d8c1d4 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Thu, 7 Nov 2024 17:19:35 +0100
Subject: [PATCH 150/150] chore: correct checkNotification

---
 test/cypress/support/commands.js | 15 ++++++++-------
 1 file changed, 8 insertions(+), 7 deletions(-)

diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js
index 73c786680..b536121b1 100755
--- a/test/cypress/support/commands.js
+++ b/test/cypress/support/commands.js
@@ -280,13 +280,14 @@ Cypress.Commands.add('openActions', (row) => {
     cy.get('tbody > tr').eq(row).find('.actions > .q-btn').click();
 });
 
-Cypress.Commands.add('checkNotification', (type) => {
-    const values = {
-        created: 'Data created',
-        updated: 'Data saved',
-        deleted: 'Data deleted',
-    };
-    cy.get('.q-notification__message').should('have.text', values[type]);
+Cypress.Commands.add('checkNotification', (text) => {
+    cy.get('.q-notification')
+        .should('be.visible')
+        .last()
+        .then(($lastNotification) => {
+            if (!Cypress.$($lastNotification).text().includes(text))
+                throw new Error(`Notification not found: "${text}"`);
+        });
 });
 
 Cypress.Commands.add('checkValueForm', (id, search) => {