From 248e02f53008e6ad58dff144bbf81e7329caabc0 Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Mon, 5 Feb 2024 08:25:35 -0300
Subject: [PATCH 01/42] WIP

---
 src/i18n/en/index.js                          |   1 +
 src/i18n/es/index.js                          |   1 +
 .../InvoiceOut/Card/InvoiceOutSummary.vue     |  43 +++---
 src/pages/InvoiceOut/InvoiceOutList.vue       |  68 +++-------
 .../InvoiceOut/InvoiceOutNegativeBases.vue    | 123 +++---------------
 5 files changed, 67 insertions(+), 169 deletions(-)

diff --git a/src/i18n/en/index.js b/src/i18n/en/index.js
index baaf414d6..9585bf7fb 100644
--- a/src/i18n/en/index.js
+++ b/src/i18n/en/index.js
@@ -531,6 +531,7 @@ export default {
             company: 'Company',
             dued: 'Due date',
             shortDued: 'Due date',
+            amount: 'Amount',
         },
         card: {
             issued: 'Issued',
diff --git a/src/i18n/es/index.js b/src/i18n/es/index.js
index 0ea506b55..30e982ea2 100644
--- a/src/i18n/es/index.js
+++ b/src/i18n/es/index.js
@@ -531,6 +531,7 @@ export default {
             company: 'Empresa',
             dued: 'Fecha vencimineto',
             shortDued: 'F. vencimiento',
+            amount: 'Importe',
         },
         card: {
             issued: 'Fecha emisión',
diff --git a/src/pages/InvoiceOut/Card/InvoiceOutSummary.vue b/src/pages/InvoiceOut/Card/InvoiceOutSummary.vue
index 5a56a3957..afdc4ea7d 100644
--- a/src/pages/InvoiceOut/Card/InvoiceOutSummary.vue
+++ b/src/pages/InvoiceOut/Card/InvoiceOutSummary.vue
@@ -2,11 +2,15 @@
 import { onMounted, ref, computed } from 'vue';
 import { useRoute } from 'vue-router';
 import { useI18n } from 'vue-i18n';
-import axios from 'axios';
-import { toCurrency, toDate } from 'src/filters';
+
 import CardSummary from 'components/ui/CardSummary.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
+import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
+import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
+
 import { getUrl } from 'src/composables/getUrl';
+import { toCurrency, toDate } from 'src/filters';
+import axios from 'axios';
 
 onMounted(async () => {
     fetch();
@@ -66,30 +70,34 @@ const taxColumns = ref([
 
 const ticketsColumns = ref([
     {
-        name: 'item',
-        label: 'invoiceOut.summary.ticketId',
+        name: 'id',
+        label: t('invoiceOut.summary.ticketId'),
         field: (row) => row.id,
         sortable: true,
+        align: 'left',
     },
     {
-        name: 'quantity',
-        label: 'invoiceOut.summary.nickname',
+        name: 'alias',
+        label: t('invoiceOut.summary.nickname'),
         field: (row) => row.nickname,
         sortable: true,
+        align: 'left',
     },
     {
         name: 'landed',
-        label: 'invoiceOut.summary.shipped',
+        label: t('invoiceOut.summary.shipped'),
         field: (row) => row.shipped,
         format: (value) => toDate(value),
         sortable: true,
+        align: 'left',
     },
     {
         name: 'landed',
-        label: 'invoiceOut.summary.totalWithVat',
+        label: t('invoiceOut.summary.totalWithVat'),
         field: (row) => row.totalWithVat,
         format: (value) => toCurrency(value),
         sortable: true,
+        align: 'left',
     },
 ]);
 </script>
@@ -139,17 +147,22 @@ const ticketsColumns = ref([
                     </template>
                 </QTable>
             </QCard>
-            <QCard class="vn-three">
+            <QCard class="vn-max">
                 <div class="header">
                     {{ t('invoiceOut.summary.tickets') }}
                 </div>
                 <QTable v-if="tickets" :columns="ticketsColumns" :rows="tickets" flat>
-                    <template #header="props">
-                        <QTr :props="props">
-                            <QTh v-for="col in props.cols" :key="col.name" :props="props">
-                                {{ t(col.label) }}
-                            </QTh>
-                        </QTr>
+                    <template #body-cell-id="{ row }">
+                        <QTd>
+                            <QBtn flat dense color="blue"> {{ row.id }}</QBtn>
+                            <TicketDescriptorProxy :id="row.id" />
+                        </QTd>
+                    </template>
+                    <template #body-cell-alias="{ row }">
+                        <QTd>
+                            <QBtn flat dense color="blue"> {{ row.nickname }}</QBtn>
+                            <CustomerDescriptorProxy :id="row.clientFk" />
+                        </QTd>
                     </template>
                 </QTable>
             </QCard>
diff --git a/src/pages/InvoiceOut/InvoiceOutList.vue b/src/pages/InvoiceOut/InvoiceOutList.vue
index 8d3583da3..00e008af5 100644
--- a/src/pages/InvoiceOut/InvoiceOutList.vue
+++ b/src/pages/InvoiceOut/InvoiceOutList.vue
@@ -1,17 +1,20 @@
 <script setup>
-import { onMounted, onUnmounted, ref, watch } from 'vue';
+import { onMounted, onUnmounted, ref } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useRouter } from 'vue-router';
 import { exportFile, useQuasar } from 'quasar';
-import { useStateStore } from 'stores/useStateStore';
+
 import VnPaginate from 'src/components/ui/VnPaginate.vue';
 import InvoiceOutSummaryDialog from './Card/InvoiceOutSummaryDialog.vue';
-import { toDate, toCurrency } from 'src/filters/index';
 import VnSearchbar from 'src/components/ui/VnSearchbar.vue';
-import InvoiceOutFilter from './InvoiceOutFilter.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
 import CardList from 'src/components/ui/CardList.vue';
 import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
+import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue';
+
+import InvoiceOutFilter from './InvoiceOutFilter.vue';
+import { toDate, toCurrency } from 'src/filters/index';
+import { useStateStore } from 'stores/useStateStore';
 
 const { t } = useI18n();
 const selectedCards = ref(new Map());
@@ -139,39 +142,7 @@ const downloadCsv = () => {
                             :disable="selectedCards.size === 0"
                             :label="t('globals.download')"
                         />
-                        <!-- <QBtnDropdown
-                            class="q-mr-xl"
-                            color="primary"
-                            :disable="!manageCheckboxes && arrayElements.length < 1"
-                            :label="t('globals.download')"
-                            v-else
-                        >
-                            <QList>
-                                <QItem clickable v-close-popup @click="downloadCsv(rows)">
-                                    <QItemSection>
-                                        <QItemLabel>
-                                            {{
-                                                t('globals.allRows', {
-                                                    numberRows: rows.length,
-                                                })
-                                            }}
-                                        </QItemLabel>
-                                    </QItemSection>
-                                </QItem>
 
-                                <QItem clickable v-close-popup @click="downloadCsv(rows)">
-                                    <QItemSection>
-                                        <QItemLabel>
-                                            {{
-                                                t('globals.selectRows', {
-                                                    numberRows: rows.length,
-                                                })
-                                            }}
-                                        </QItemLabel>
-                                    </QItemSection>
-                                </QItem>
-                            </QList>
-                        </QBtnDropdown> -->
                         <QCheckbox
                             left-label
                             :label="t('globals.markAll')"
@@ -196,18 +167,24 @@ const downloadCsv = () => {
                         >
                             <template #list-items>
                                 <VnLv
-                                    :label="t('invoiceOut.list.shortIssued')"
-                                    :title-label="t('invoiceOut.list.issued')"
+                                    :label="t('invoiceOut.list.amount')"
                                     :value="toDate(row.issued)"
                                 />
                                 <VnLv
                                     :label="t('invoiceOut.list.amount')"
                                     :value="toCurrency(row.amount)"
                                 />
-                                <VnLv
-                                    :label="t('invoiceOut.list.client')"
-                                    :value="row.clientSocialName"
-                                />
+
+                                <VnLv :label="t('invoiceOut.list.client')">
+                                    <template #value>
+                                        <span class="link" @click.stop>
+                                            {{ row?.clientSocialName }}
+                                            <CustomerDescriptorProxy
+                                                :id="row?.clientFk"
+                                            />
+                                        </span>
+                                    </template>
+                                </VnLv>
                                 <VnLv
                                     :label="t('invoiceOut.list.shortCreated')"
                                     :title-label="t('invoiceOut.list.created')"
@@ -224,13 +201,6 @@ const downloadCsv = () => {
                                 />
                             </template>
                             <template #actions>
-                                <QBtn
-                                    :label="t('components.smartCard.openCard')"
-                                    @click.stop="navigate(row.id)"
-                                    class="bg-vn-dark"
-                                    outline
-                                    type="reset"
-                                />
                                 <QBtn
                                     :label="t('components.smartCard.openSummary')"
                                     @click.stop="viewSummary(row.id)"
diff --git a/src/pages/InvoiceOut/InvoiceOutNegativeBases.vue b/src/pages/InvoiceOut/InvoiceOutNegativeBases.vue
index c832d577c..8b7da4b9b 100644
--- a/src/pages/InvoiceOut/InvoiceOutNegativeBases.vue
+++ b/src/pages/InvoiceOut/InvoiceOutNegativeBases.vue
@@ -1,17 +1,17 @@
 <script setup>
 import { ref, computed, onBeforeMount } from 'vue';
 import { useI18n } from 'vue-i18n';
-import { QCheckbox, QBtn } from 'quasar';
 
 import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
 import InvoiceOutNegativeFilter from './InvoiceOutNegativeBasesFilter.vue';
 import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
+import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue';
+import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
 
 import { toCurrency } from 'src/filters';
 import { useInvoiceOutGlobalStore } from 'src/stores/invoiceOutGlobal.js';
 import { useStateStore } from 'stores/useStateStore';
 import { useArrayData } from 'composables/useArrayData';
-import VnUserLink from 'src/components/ui/VnUserLink.vue';
 
 const invoiceOutGlobalStore = useInvoiceOutGlobalStore();
 const stateStore = useStateStore();
@@ -47,76 +47,6 @@ onBeforeMount(async () => {
 
 const rows = computed(() => arrayData.value.store.data);
 
-const selectedCustomerId = ref(0);
-const selectedWorkerId = ref(0);
-
-const tableColumnComponents = {
-    company: {
-        component: 'span',
-        props: () => {},
-        event: () => {},
-    },
-    country: {
-        component: 'span',
-        props: () => {},
-        event: () => {},
-    },
-    clientId: {
-        component: QBtn,
-        props: () => ({ flat: true, color: 'blue' }),
-        event: (prop) => selectCustomerId(prop.value),
-    },
-    client: {
-        component: 'span',
-        props: () => {},
-        event: () => {},
-    },
-    amount: {
-        component: 'span',
-        props: () => {},
-        event: () => {},
-    },
-    base: {
-        component: 'span',
-        props: () => {},
-        event: () => {},
-    },
-    ticketId: {
-        component: 'span',
-        props: () => {},
-        event: () => {},
-    },
-    active: {
-        component: QCheckbox,
-        props: (prop) => ({
-            disable: true,
-            'model-value': Boolean(prop.value),
-        }),
-        event: () => {},
-    },
-    hasToInvoice: {
-        component: QCheckbox,
-        props: (prop) => ({
-            disable: true,
-            'model-value': Boolean(prop.value),
-        }),
-        event: () => {},
-    },
-    verifiedData: {
-        component: QCheckbox,
-        props: (prop) => ({
-            disable: true,
-            'model-value': Boolean(prop.value),
-        }),
-        event: () => {},
-    },
-    comercial: {
-        component: QBtn,
-        props: () => ({ flat: true, color: 'blue' }),
-        event: (prop) => selectWorkerId(prop.row.comercialId),
-    },
-};
-
 const columns = computed(() => [
     {
         label: t('invoiceOut.negativeBases.company'),
@@ -205,14 +135,6 @@ const downloadCSV = async () => {
         params
     );
 };
-
-const selectCustomerId = (id) => {
-    selectedCustomerId.value = id;
-};
-
-const selectWorkerId = (id) => {
-    selectedWorkerId.value = id;
-};
 </script>
 
 <template>
@@ -236,31 +158,22 @@ const selectWorkerId = (id) => {
             :pagination="{ rowsPerPage: 0 }"
             class="full-width q-mt-md"
         >
-            <template #body-cell="props">
-                <QTd :props="props">
-                    <component
-                        :is="tableColumnComponents[props.col.name].component"
-                        class="col-content"
-                        v-bind="tableColumnComponents[props.col.name].props(props)"
-                        @click="tableColumnComponents[props.col.name].event(props)"
-                    >
-                        <template
-                            v-if="
-                                props.col.name !== 'active' &&
-                                props.col.name !== 'hasToInvoice' &&
-                                props.col.name !== 'verifiedData'
-                            "
-                            >{{ props.value }}
-                        </template>
-                        <CustomerDescriptorProxy
-                            v-if="props.col.name === 'clientId'"
-                            :id="selectedCustomerId"
-                        />
-                        <VnUserLink
-                            v-if="props.col.name === 'comercial'"
-                            :worker-id="selectedWorkerId"
-                        />
-                    </component>
+            <template #body-cell-clientId="{ row }">
+                <QTd>
+                    <QBtn flat dense color="blue"> {{ row.clientId }}</QBtn>
+                    <CustomerDescriptorProxy :id="row.clientId" />
+                </QTd>
+            </template>
+            <template #body-cell-ticketId="{ row }">
+                <QTd>
+                    <QBtn flat dense color="blue"> {{ row.ticketFk }}</QBtn>
+                    <TicketDescriptorProxy :id="row.ticketFk" />
+                </QTd>
+            </template>
+            <template #body-cell-comercial="{ row }">
+                <QTd>
+                    <QBtn flat dense color="blue">{{ row.comercialName }}</QBtn>
+                    <WorkerDescriptorProxy :id="row.comercialId" />
                 </QTd>
             </template>
         </QTable>

From df620566ef36a875fd67ff4bb2b2a34fad244e5f Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Mon, 12 Feb 2024 08:11:44 -0300
Subject: [PATCH 02/42] Invoice out globals corrections

---
 src/i18n/en/index.js                          |  2 +-
 src/i18n/es/index.js                          |  2 +-
 src/pages/InvoiceOut/InvoiceOutGlobalForm.vue | 17 ++---------
 src/stores/invoiceOutGlobal.js                | 30 ++++++++-----------
 4 files changed, 18 insertions(+), 33 deletions(-)

diff --git a/src/i18n/en/index.js b/src/i18n/en/index.js
index 9585bf7fb..5d6b1f65b 100644
--- a/src/i18n/en/index.js
+++ b/src/i18n/en/index.js
@@ -565,7 +565,7 @@ export default {
                 fillDates: 'Invoice date and the max date should be filled',
                 invoiceDateLessThanMaxDate: 'Invoice date can not be less than max date',
                 invoiceWithFutureDate: 'Exists an invoice with a future date',
-                noTicketsToInvoice: 'There are not clients to invoice',
+                noTicketsToInvoice: 'There are not tickets to invoice',
                 criticalInvoiceError: 'Critical invoicing error, process stopped',
             },
             table: {
diff --git a/src/i18n/es/index.js b/src/i18n/es/index.js
index 30e982ea2..5c60e840d 100644
--- a/src/i18n/es/index.js
+++ b/src/i18n/es/index.js
@@ -567,7 +567,7 @@ export default {
                 invoiceDateLessThanMaxDate:
                     'La fecha de la factura no puede ser menor que la fecha máxima',
                 invoiceWithFutureDate: 'Existe una factura con una fecha futura',
-                noTicketsToInvoice: 'No hay clientes para facturar',
+                noTicketsToInvoice: 'No existen tickets para facturar',
                 criticalInvoiceError: 'Error crítico en la facturación, proceso detenido',
             },
             table: {
diff --git a/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue b/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue
index c61b9f7ff..a000d6d4f 100644
--- a/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue
+++ b/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue
@@ -13,13 +13,8 @@ const { t } = useI18n();
 const invoiceOutGlobalStore = useInvoiceOutGlobalStore();
 
 // invoiceOutGlobalStore state and getters
-const {
-    initialDataLoading,
-    formInitialData,
-
-    invoicing,
-    status,
-} = storeToRefs(invoiceOutGlobalStore);
+const { initialDataLoading, formInitialData, invoicing, status } =
+    storeToRefs(invoiceOutGlobalStore);
 
 // invoiceOutGlobalStore actions
 const { makeInvoice, setStatusValue } = invoiceOutGlobalStore;
@@ -32,13 +27,7 @@ const printersOptions = ref([]);
 
 const clientsOptions = ref([]);
 
-const formData = ref({
-    companyFk: null,
-    invoiceDate: null,
-    maxShipped: null,
-    clientId: null,
-    printer: null,
-});
+const formData = ref({});
 
 const optionsInitialData = computed(() => {
     return (
diff --git a/src/stores/invoiceOutGlobal.js b/src/stores/invoiceOutGlobal.js
index 7151aac5f..9929a0127 100644
--- a/src/stores/invoiceOutGlobal.js
+++ b/src/stores/invoiceOutGlobal.js
@@ -73,6 +73,9 @@ export const useInvoiceOutGlobalStore = defineStore({
                 const stringDate = data.issued.substring(0, 10);
                 this.minInvoicingDate = stringDate;
                 this.formInitialData.invoiceDate = stringDate;
+
+                this.minInvoicingDate = new Date(data.issued);
+                this.formInitialData.invoiceDate = this.minInvoicingDate;
             } catch (err) {
                 console.error('Error fetching invoice out global initial data');
                 throw new Error();
@@ -103,12 +106,9 @@ export const useInvoiceOutGlobalStore = defineStore({
 
                 if (clientsToInvoice == 'all') params.clientId = undefined;
 
-                const addressesResponse = await await axios.post(
-                    'InvoiceOuts/clientsToInvoice',
-                    params
-                );
+                const { data } = await axios.post('InvoiceOuts/clientsToInvoice', params);
 
-                this.addresses = addressesResponse.data;
+                this.addresses = data;
 
                 if (!this.addresses || !this.addresses.length > 0) {
                     notify(
@@ -118,9 +118,9 @@ export const useInvoiceOutGlobalStore = defineStore({
                     throw new Error("There aren't addresses to invoice");
                 }
 
-                this.addresses.forEach(async (address) => {
+                for (const address of this.addresses) {
                     await this.invoiceClient(address, formData);
-                });
+                }
             } catch (err) {
                 this.handleError(err);
             }
@@ -186,13 +186,10 @@ export const useInvoiceOutGlobalStore = defineStore({
                 this.status = 'invoicing';
                 this.invoicing = true;
 
-                const invoiceResponse = await axios.post(
-                    'InvoiceOuts/invoiceClient',
-                    params
-                );
+                const { data } = await axios.post('InvoiceOuts/invoiceClient', params);
 
-                if (invoiceResponse.data) {
-                    this.makePdfAndNotify(invoiceResponse.data, address);
+                if (data) {
+                    await this.makePdfAndNotify(data, address);
                 }
 
                 this.isInvoicing = false;
@@ -203,7 +200,7 @@ export const useInvoiceOutGlobalStore = defineStore({
                     err.response.status >= 400 &&
                     err.response.status < 500
                 ) {
-                    this.invoiceClientError(address, err.response);
+                    this.invoiceClientError(address, err.response?.data?.error?.message);
                     this.addressIndex++;
                     return;
                 } else {
@@ -227,12 +224,11 @@ export const useInvoiceOutGlobalStore = defineStore({
                 this.nPdfs++;
                 this.nRequests--;
             } catch (err) {
-                this.invoiceClientError(client, err, true);
+                this.invoiceClientError(client, err.response?.data?.error?.message, true);
             }
         },
 
-        invoiceClientError(client, response, isWarning) {
-            const message = response.data?.error?.message || response.message;
+        invoiceClientError(client, message, isWarning) {
             this.errors.unshift({ client, message, isWarning });
         },
 

From 8adee21f2beba0b1be5164fc6ff777117461b46e Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Wed, 14 Feb 2024 08:05:06 -0300
Subject: [PATCH 03/42] WIP

---
 .../Card/InvoiceOutDescriptorMenu.vue         | 69 +++++++++++++++----
 .../InvoiceOut/Card/InvoiceOutSummary.vue     |  4 +-
 2 files changed, 59 insertions(+), 14 deletions(-)

diff --git a/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue b/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue
index ab8b6470b..94c05f772 100644
--- a/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue
+++ b/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue
@@ -6,35 +6,80 @@ const { t } = useI18n();
 
 <template>
     <QItem v-ripple clickable>
-        <QItemSection>{{ t('Transfer invoice to') }}</QItemSection>
+        <QItemSection>{{ t('Transfer invoice to...') }}</QItemSection>
     </QItem>
     <QItem v-ripple clickable>
-        <QItemSection>{{ t('See invoice') }}</QItemSection>
+        <QItemSection>{{ t('Show invoice...') }}</QItemSection>
+        <QItemSection side>
+            <QIcon name="keyboard_arrow_right" />
+        </QItemSection>
+        <QMenu anchor="top end" self="top start">
+            <QList>
+                <QItem v-ripple clickable>
+                    <QItemSection>{{ t('As PDF') }}</QItemSection>
+                </QItem>
+                <QItem v-ripple clickable>
+                    <QItemSection>{{ t('As CSV') }}</QItemSection>
+                </QItem>
+            </QList>
+        </QMenu>
     </QItem>
     <QItem v-ripple clickable>
-        <QItemSection>{{ t('Send invoice') }}</QItemSection>
+        <QItemSection>{{ t('Send invoice...') }}</QItemSection>
+        <QItemSection side>
+            <QIcon name="keyboard_arrow_right" />
+        </QItemSection>
+        <QMenu anchor="top end" self="top start">
+            <QList>
+                <QItem v-ripple clickable>
+                    <QItemSection>{{ t('Send PDF') }}</QItemSection>
+                </QItem>
+                <QItem v-ripple clickable>
+                    <QItemSection>{{ t('Send CSV') }}</QItemSection>
+                </QItem>
+            </QList>
+        </QMenu>
     </QItem>
     <QItem v-ripple clickable>
         <QItemSection>{{ t('Delete invoice') }}</QItemSection>
     </QItem>
     <QItem v-ripple clickable>
-        <QItemSection>{{ t('Post invoice') }}</QItemSection>
+        <QItemSection>{{ t('Book invoice') }}</QItemSection>
     </QItem>
     <QItem v-ripple clickable>
-        <QItemSection>{{ t('Regenerate invoice PDF') }}</QItemSection>
+        <QItemSection>{{ t('Generate PDF invoice') }}</QItemSection>
     </QItem>
     <QItem v-ripple clickable>
-        <QItemSection>{{ t('Pass') }}</QItemSection>
+        <QItemSection>{{ t('Refund...') }}</QItemSection>
+        <QItemSection side>
+            <QIcon name="keyboard_arrow_right" />
+        </QItemSection>
+        <QMenu anchor="top end" self="top start">
+            <QList>
+                <QItem v-ripple clickable>
+                    <QItemSection>{{ t('With warehouse') }}</QItemSection>
+                </QItem>
+                <QItem v-ripple clickable>
+                    <QItemSection>{{ t('Without warehouse') }}</QItemSection>
+                </QItem>
+            </QList>
+        </QMenu>
     </QItem>
 </template>
 
 <i18n>
 es:
-    Transfer invoice to: Transferir factura a
-    See invoice: Ver factura
-    Send invoice: Enviar factura
+    Transfer invoice to...: Transferir factura a...
+    Show invoice...: Ver factura...
+    Send invoice...: Enviar factura...
     Delete invoice: Eliminar factura
-    Post invoice: Asentar factura
-    Regenerate invoice PDF: Regenerar PDF factura
-    Pass: Abono
+    Book invoice: Asentar factura
+    Generate PDF invoice: Generar PDF factura
+    Refund...: Abono
+    As PDF: como PDF
+    As CSV: como CSV
+    Send PDF: Enviar PDF
+    Send CSV: Enviar CSV
+    With warehouse: Con almacén
+    Without warehouse: Sin almacén
 </i18n>
diff --git a/src/pages/InvoiceOut/Card/InvoiceOutSummary.vue b/src/pages/InvoiceOut/Card/InvoiceOutSummary.vue
index afdc4ea7d..5721818e4 100644
--- a/src/pages/InvoiceOut/Card/InvoiceOutSummary.vue
+++ b/src/pages/InvoiceOut/Card/InvoiceOutSummary.vue
@@ -154,13 +154,13 @@ const ticketsColumns = ref([
                 <QTable v-if="tickets" :columns="ticketsColumns" :rows="tickets" flat>
                     <template #body-cell-id="{ row }">
                         <QTd>
-                            <QBtn flat dense color="blue"> {{ row.id }}</QBtn>
+                            <QBtn flat color="primary"> {{ row.id }}</QBtn>
                             <TicketDescriptorProxy :id="row.id" />
                         </QTd>
                     </template>
                     <template #body-cell-alias="{ row }">
                         <QTd>
-                            <QBtn flat dense color="blue"> {{ row.nickname }}</QBtn>
+                            <QBtn flat dense color="primary"> {{ row.nickname }}</QBtn>
                             <CustomerDescriptorProxy :id="row.clientFk" />
                         </QTd>
                     </template>

From ac555a01468560efa8a08841aa51299d9ff4c4bb Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Thu, 15 Feb 2024 11:36:53 -0300
Subject: [PATCH 04/42] Transfer invoice form

---
 src/components/TransferInvoiceForm.vue        | 190 ++++++++++++++++++
 .../InvoiceOut/Card/InvoiceOutDescriptor.vue  |  11 +-
 .../Card/InvoiceOutDescriptorMenu.vue         |  18 +-
 3 files changed, 214 insertions(+), 5 deletions(-)
 create mode 100644 src/components/TransferInvoiceForm.vue

diff --git a/src/components/TransferInvoiceForm.vue b/src/components/TransferInvoiceForm.vue
new file mode 100644
index 000000000..e62c7638c
--- /dev/null
+++ b/src/components/TransferInvoiceForm.vue
@@ -0,0 +1,190 @@
+<script setup>
+import { ref, reactive } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { useRouter } from 'vue-router';
+
+import VnRow from 'components/ui/VnRow.vue';
+import FetchData from 'components/FetchData.vue';
+import VnSelectFilter from 'components/common/VnSelectFilter.vue';
+import axios from 'axios';
+import useNotify from 'src/composables/useNotify.js';
+
+const $props = defineProps({
+    invoiceOutData: {
+        type: Object,
+        default: () => {},
+    },
+});
+
+const { t } = useI18n();
+const router = useRouter();
+const { notify } = useNotify();
+
+const transferInvoiceParams = reactive({
+    id: $props.invoiceOutData?.id,
+    refFk: $props.invoiceOutData?.ref,
+});
+const closeButton = ref(null);
+const isLoading = ref(false);
+const clientsOptions = ref([]);
+const rectificativeTypeOptions = ref([]);
+const siiTypeInvoiceOutsOptions = ref([]);
+const invoiceCorrectionTypesOptions = ref([]);
+
+const closeForm = () => {
+    if (closeButton.value) closeButton.value.click();
+};
+
+const transferInvoice = async () => {
+    try {
+        const { data } = await axios.post(
+            'InvoiceOuts/transferInvoice',
+            transferInvoiceParams
+        );
+        notify(t('Transferred invoice'), 'positive');
+        closeForm();
+        router.push('InvoiceOutSummary', { id: data.id });
+    } catch (err) {
+        console.error('Error transfering invoice', err);
+    }
+};
+</script>
+
+<template>
+    <FetchData
+        url="Clients"
+        @on-fetch="(data) => (clientsOptions = data)"
+        :filter="{ fields: ['id', 'name'], order: 'id', limit: 30 }"
+        auto-load
+    />
+    <FetchData
+        url="CplusRectificationTypes"
+        :filter="{ order: 'description' }"
+        @on-fetch="(data) => (rectificativeTypeOptions = data)"
+        auto-load
+    />
+    <FetchData
+        url="SiiTypeInvoiceOuts"
+        :filter="{ where: { code: { like: 'R%' } } }"
+        @on-fetch="(data) => (siiTypeInvoiceOutsOptions = data)"
+        auto-load
+    />
+    <FetchData
+        url="InvoiceCorrectionTypes"
+        @on-fetch="(data) => (invoiceCorrectionTypesOptions = data)"
+        auto-load
+    />
+    <QForm @submit="transferInvoice()" class="all-pointer-events">
+        <QCard class="column" style="padding: 32px; z-index: 100">
+            <span ref="closeButton" class="close-icon" v-close-popup>
+                <QIcon name="close" size="sm" />
+            </span>
+            <h1 class="title">{{ t('Transfer invoice') }}</h1>
+            <VnRow class="row q-gutter-md q-mb-md">
+                <div class="col">
+                    <VnSelectFilter
+                        :label="t('Client')"
+                        :options="clientsOptions"
+                        hide-selected
+                        option-label="name"
+                        option-value="id"
+                        v-model="transferInvoiceParams.newClientFk"
+                        :required="true"
+                    >
+                        <template #option="scope">
+                            <QItem v-bind="scope.itemProps">
+                                <QItemSection>
+                                    <QItemLabel>
+                                        #{{ scope.opt?.id }} -
+                                        {{ scope.opt?.name }}
+                                    </QItemLabel>
+                                </QItemSection>
+                            </QItem>
+                        </template>
+                    </VnSelectFilter>
+                </div>
+                <div class="col">
+                    <VnSelectFilter
+                        :label="t('Rectificative type')"
+                        :options="rectificativeTypeOptions"
+                        hide-selected
+                        option-label="description"
+                        option-value="id"
+                        v-model="transferInvoiceParams.cplusRectificationTypeFk"
+                        :required="true"
+                    />
+                </div>
+            </VnRow>
+            <VnRow class="row q-gutter-md q-mb-md">
+                <div class="col">
+                    <VnSelectFilter
+                        :label="t('Class')"
+                        :options="siiTypeInvoiceOutsOptions"
+                        hide-selected
+                        option-label="description"
+                        option-value="id"
+                        v-model="transferInvoiceParams.siiTypeInvoiceOutFk"
+                        :required="true"
+                    >
+                        <template #option="scope">
+                            <QItem v-bind="scope.itemProps">
+                                <QItemSection>
+                                    <QItemLabel>
+                                        {{ scope.opt?.code }} -
+                                        {{ scope.opt?.description }}
+                                    </QItemLabel>
+                                </QItemSection>
+                            </QItem>
+                        </template>
+                    </VnSelectFilter>
+                </div>
+                <div class="col">
+                    <VnSelectFilter
+                        :label="t('Type')"
+                        :options="invoiceCorrectionTypesOptions"
+                        hide-selected
+                        option-label="description"
+                        option-value="id"
+                        v-model="transferInvoiceParams.invoiceCorrectionTypeFk"
+                        :required="true"
+                    />
+                </div>
+            </VnRow>
+            <div class="q-mt-lg row justify-end">
+                <QBtn
+                    :label="t('Transfer client')"
+                    type="submit"
+                    color="primary"
+                    :disabled="isLoading"
+                    :loading="isLoading"
+                />
+            </div>
+        </QCard>
+    </QForm>
+</template>
+
+<i18n>
+es:
+    Transfer invoice: Transferir factura
+    Transfer client: Transferir cliente
+    Client: Cliente
+    Rectificative type: Tipo rectificativa
+    Class: Clase
+    Type: Tipo
+    Transferred invoice: Factura transferida
+</i18n>
+
+<style lang="scss" scoped>
+.title {
+    font-size: 17px;
+    font-weight: bold;
+    line-height: 20px;
+}
+
+.close-icon {
+    position: absolute;
+    top: 20px;
+    right: 20px;
+    cursor: pointer;
+}
+</style>
diff --git a/src/pages/InvoiceOut/Card/InvoiceOutDescriptor.vue b/src/pages/InvoiceOut/Card/InvoiceOutDescriptor.vue
index 9c1bb3649..e01706e84 100644
--- a/src/pages/InvoiceOut/Card/InvoiceOutDescriptor.vue
+++ b/src/pages/InvoiceOut/Card/InvoiceOutDescriptor.vue
@@ -2,13 +2,15 @@
 import { ref, computed } from 'vue';
 import { useRoute } from 'vue-router';
 import { useI18n } from 'vue-i18n';
-import { toCurrency, toDate } from 'src/filters';
+
 import CardDescriptor from 'components/ui/CardDescriptor.vue';
 import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
-import useCardDescription from 'src/composables/useCardDescription';
 import InvoiceOutDescriptorMenu from './InvoiceOutDescriptorMenu.vue';
 
+import useCardDescription from 'src/composables/useCardDescription';
+import { toCurrency, toDate } from 'src/filters';
+
 const $props = defineProps({
     id: {
         type: Number,
@@ -23,7 +25,6 @@ const { t } = useI18n();
 const entityId = computed(() => {
     return $props.id || route.params.id;
 });
-const descriptor = ref();
 
 const filter = {
     include: [
@@ -42,6 +43,8 @@ const filter = {
     ],
 };
 
+const descriptor = ref();
+
 function ticketFilter(invoice) {
     return JSON.stringify({ refFk: invoice.ref });
 }
@@ -61,7 +64,7 @@ const setData = (entity) => (data.value = useCardDescription(entity.ref, entity.
         data-key="invoiceOutData"
     >
         <template #menu="{ entity }">
-            <InvoiceOutDescriptorMenu :invoice-out="entity" />
+            <InvoiceOutDescriptorMenu :invoice-out-data="entity" />
         </template>
         <template #body="{ entity }">
             <VnLv :label="t('invoiceOut.card.issued')" :value="toDate(entity.issued)" />
diff --git a/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue b/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue
index 94c05f772..a7e94951a 100644
--- a/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue
+++ b/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue
@@ -1,11 +1,23 @@
 <script setup>
+import { ref } from 'vue';
 import { useI18n } from 'vue-i18n';
 
+import TransferInvoiceForm from 'src/components/TransferInvoiceForm.vue';
+
+const $props = defineProps({
+    invoiceOutData: {
+        type: Object,
+        default: () => {},
+    },
+});
+
 const { t } = useI18n();
+
+const transferInvoiceDialogRef = ref();
 </script>
 
 <template>
-    <QItem v-ripple clickable>
+    <QItem v-ripple clickable @click="transferInvoiceDialogRef.show()">
         <QItemSection>{{ t('Transfer invoice to...') }}</QItemSection>
     </QItem>
     <QItem v-ripple clickable>
@@ -65,6 +77,10 @@ const { t } = useI18n();
             </QList>
         </QMenu>
     </QItem>
+
+    <QDialog ref="transferInvoiceDialogRef">
+        <TransferInvoiceForm :invoice-out-data="invoiceOutData" />
+    </QDialog>
 </template>
 
 <i18n>

From af12512de9d2804f2176c8a68e04ddc11083b934 Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Thu, 15 Feb 2024 18:10:54 -0300
Subject: [PATCH 05/42] WIP

---
 src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue b/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue
index a7e94951a..3521ba0d7 100644
--- a/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue
+++ b/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue
@@ -14,6 +14,10 @@ const $props = defineProps({
 const { t } = useI18n();
 
 const transferInvoiceDialogRef = ref();
+
+const showInvoicePdf = () => {};
+
+const showInvoiceCsv = () => {};
 </script>
 
 <template>
@@ -27,10 +31,10 @@ const transferInvoiceDialogRef = ref();
         </QItemSection>
         <QMenu anchor="top end" self="top start">
             <QList>
-                <QItem v-ripple clickable>
+                <QItem v-ripple clickable @click="showInvoicePdf()">
                     <QItemSection>{{ t('As PDF') }}</QItemSection>
                 </QItem>
-                <QItem v-ripple clickable>
+                <QItem v-ripple clickable @click="showInvoiceCsv()">
                     <QItemSection>{{ t('As CSV') }}</QItemSection>
                 </QItem>
             </QList>

From 41e5110a8a4bbf6b276e0ab16a4ec987834f0487 Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Fri, 16 Feb 2024 08:10:47 -0300
Subject: [PATCH 06/42] Invoice out list and summary changes

---
 src/pages/InvoiceOut/InvoiceOutList.vue | 69 ++++++++++++-------------
 1 file changed, 34 insertions(+), 35 deletions(-)

diff --git a/src/pages/InvoiceOut/InvoiceOutList.vue b/src/pages/InvoiceOut/InvoiceOutList.vue
index 7d19359d4..7bdf2ffbd 100644
--- a/src/pages/InvoiceOut/InvoiceOutList.vue
+++ b/src/pages/InvoiceOut/InvoiceOutList.vue
@@ -2,7 +2,6 @@
 import { onMounted, onUnmounted, ref } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useRouter } from 'vue-router';
-import { exportFile, useQuasar } from 'quasar';
 
 import VnPaginate from 'src/components/ui/VnPaginate.vue';
 import InvoiceOutSummary from './Card/InvoiceOutSummary.vue';
@@ -16,12 +15,14 @@ import InvoiceOutFilter from './InvoiceOutFilter.vue';
 import { toDate, toCurrency } from 'src/filters/index';
 import { useStateStore } from 'stores/useStateStore';
 import { useSummaryDialog } from 'src/composables/useSummaryDialog';
+import { useSession } from 'src/composables/useSession';
 
 const { t } = useI18n();
 const selectedCards = ref(new Map());
-const quasar = useQuasar();
 const router = useRouter();
 const stateStore = useStateStore();
+const session = useSession();
+const token = session.getToken();
 const { viewSummary } = useSummaryDialog();
 
 onMounted(() => (stateStore.rightDrawer = true));
@@ -55,35 +56,31 @@ const toggleAllCards = (cardsData) => {
     }
 };
 
-const downloadCsv = () => {
-    if (selectedCards.value.size === 0) return;
-    const selectedCardsArray = Array.from(selectedCards.value.values());
-    let file;
-    for (var i = 0; i < selectedCardsArray.length; i++) {
-        if (i == 0) file += Object.keys(selectedCardsArray[i]).join(';') + '\n';
-        file +=
-            Object.keys(selectedCardsArray[i])
-                .map(function (key) {
-                    return selectedCardsArray[i][key];
-                })
-                .join(';') + '\n';
-    }
-    const status = exportFile('file.csv', file, {
-        encoding: 'windows-1252',
-        mimeType: 'text/csv;charset=windows-1252;',
-    });
-    if (status === true) {
-        quasar.notify({
-            message: t('fileAllowed'),
-            color: 'positive',
-            icon: 'check',
-        });
-    } else {
-        quasar.notify({
-            message: t('fileDenied'),
-            color: 'negative',
-            icon: 'warning',
-        });
+const openPdf = () => {
+    try {
+        if (selectedCards.value.size === 0) return;
+        const selectedCardsArray = Array.from(selectedCards.value.values());
+
+        if (selectedCards.value.size === 1) {
+            const [invoiceOut] = selectedCardsArray;
+            const url = `api/InvoiceOuts/${invoiceOut.id}/download?access_token=${token}`;
+            window.open(url, '_blank');
+        } else {
+            const invoiceOutIdsArray = selectedCardsArray.map(
+                (invoiceOut) => invoiceOut.id
+            );
+            const invoiceOutIds = invoiceOutIdsArray.join(',');
+
+            const params = new URLSearchParams({
+                access_token: token,
+                ids: invoiceOutIds,
+            });
+
+            const url = `api/InvoiceOuts/downloadZip?${params}`;
+            window.open(url, '_blank');
+        }
+    } catch (err) {
+        console.error('Error opening PDF');
     }
 };
 </script>
@@ -129,13 +126,14 @@ const downloadCsv = () => {
                 <VnSubToolbar class="bg-vn-dark justify-end">
                     <template #st-actions>
                         <QBtn
-                            @click="downloadCsv()"
+                            @click="openPdf()"
                             class="q-mr-xl"
                             color="primary"
                             :disable="selectedCards.size === 0"
                             :label="t('globals.download')"
-                        />
-
+                        >
+                            <QTooltip>{{ t('downloadPdf') }}</QTooltip>
+                        </QBtn>
                         <QCheckbox
                             left-label
                             :label="t('globals.markAll')"
@@ -167,7 +165,6 @@ const downloadCsv = () => {
                                     :label="t('invoiceOut.list.amount')"
                                     :value="toCurrency(row.amount)"
                                 />
-
                                 <VnLv :label="t('invoiceOut.list.client')">
                                     <template #value>
                                         <span class="link" @click.stop>
@@ -216,9 +213,11 @@ en:
     fileDenied: Browser denied file download...
     fileAllowed: Successful download of CSV file
     youCanSearchByInvoiceReference: You can search by invoice reference
+    downloadPdf: Download PDF
 es:
     searchInvoice: Buscar factura emitida
     fileDenied: El navegador denegó la descarga de archivos...
     fileAllowed: Descarga exitosa de archivo CSV
     youCanSearchByInvoiceReference: Puedes buscar por referencia de la factura
+    downloadPdf: Download PDF
 </i18n>

From 6ace34cbb15cdae23cd5ffa6b35ccb84effce71b Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Mon, 19 Feb 2024 17:23:50 -0300
Subject: [PATCH 07/42] descriptor menu show invoice actions

---
 .../Card/InvoiceOutDescriptorMenu.vue           | 17 +++++++++++++++--
 1 file changed, 15 insertions(+), 2 deletions(-)

diff --git a/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue b/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue
index 3521ba0d7..ca2bf1304 100644
--- a/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue
+++ b/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue
@@ -4,6 +4,9 @@ import { useI18n } from 'vue-i18n';
 
 import TransferInvoiceForm from 'src/components/TransferInvoiceForm.vue';
 
+import { useSession } from 'src/composables/useSession';
+import { usePrintService } from 'composables/usePrintService';
+
 const $props = defineProps({
     invoiceOutData: {
         type: Object,
@@ -11,13 +14,23 @@ const $props = defineProps({
     },
 });
 
+const session = useSession();
+const token = session.getToken();
 const { t } = useI18n();
+const { openReport } = usePrintService();
 
 const transferInvoiceDialogRef = ref();
 
-const showInvoicePdf = () => {};
+const showInvoicePdf = () => {
+    const url = `api/InvoiceOuts/${$props.invoiceOutData.id}/download?access_token=${token}`;
+    window.open(url, '_blank');
+};
 
-const showInvoiceCsv = () => {};
+const showInvoiceCsv = () => {
+    openReport(`InvoiceOuts/${$props.invoiceOutData.ref}/invoice-csv`, {
+        recipientId: $props.invoiceOutData.client.id,
+    });
+};
 </script>
 
 <template>

From 587baa645e37cc98d9a655f500411b0f412e4b15 Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Tue, 20 Feb 2024 08:47:24 -0300
Subject: [PATCH 08/42] generic FormPopup creation and SendInvoiceForm creation

---
 src/components/FormPopup.vue                  | 93 +++++++++++++++++++
 .../Card/InvoiceOutDescriptorMenu.vue         | 15 ++-
 src/pages/InvoiceOut/Card/SendInvoiceForm.vue | 41 ++++++++
 3 files changed, 147 insertions(+), 2 deletions(-)
 create mode 100644 src/components/FormPopup.vue
 create mode 100644 src/pages/InvoiceOut/Card/SendInvoiceForm.vue

diff --git a/src/components/FormPopup.vue b/src/components/FormPopup.vue
new file mode 100644
index 000000000..951d697d5
--- /dev/null
+++ b/src/components/FormPopup.vue
@@ -0,0 +1,93 @@
+<script setup>
+import { ref } from 'vue';
+import { useI18n } from 'vue-i18n';
+
+const emit = defineEmits(['onDataSaved']);
+
+const $props = defineProps({
+    title: {
+        type: String,
+        default: '',
+    },
+    subtitle: {
+        type: String,
+        default: '',
+    },
+    defaultSaveButton: {
+        type: Boolean,
+        default: true,
+    },
+    defaultCancelButton: {
+        type: Boolean,
+        default: true,
+    },
+});
+
+const { t } = useI18n();
+
+const closeButton = ref(null);
+const isLoading = ref(false);
+
+const onDataSaved = (dataSaved) => {
+    emit('onDataSaved', dataSaved);
+    closeForm();
+};
+
+const closeForm = () => {
+    if (closeButton.value) closeButton.value.click();
+};
+</script>
+
+<template>
+    <QForm
+        @submit="onDataSaved($event)"
+        class="all-pointer-events full-width"
+        style="max-width: 800px"
+    >
+        <QCard class="q-pa-lg">
+            <span ref="closeButton" class="close-icon" v-close-popup>
+                <QIcon name="close" size="sm" />
+            </span>
+            <h1 class="title">{{ title }}</h1>
+            <p>{{ subtitle }}</p>
+            <slot name="form-inputs" />
+            <div class="q-mt-lg row justify-end">
+                <QBtn
+                    v-if="defaultSaveButton"
+                    :label="t('globals.save')"
+                    type="submit"
+                    color="primary"
+                    :disabled="isLoading"
+                    :loading="isLoading"
+                />
+                <QBtn
+                    v-if="defaultCancelButton"
+                    :label="t('globals.cancel')"
+                    type="reset"
+                    color="primary"
+                    flat
+                    class="q-ml-sm"
+                    :disabled="isLoading"
+                    :loading="isLoading"
+                    v-close-popup
+                />
+                <slot name="customButtons" />
+            </div>
+        </QCard>
+    </QForm>
+</template>
+
+<style lang="scss" scoped>
+.title {
+    font-size: 17px;
+    font-weight: bold;
+    line-height: 20px;
+}
+
+.close-icon {
+    position: absolute;
+    top: 20px;
+    right: 20px;
+    cursor: pointer;
+}
+</style>
diff --git a/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue b/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue
index ca2bf1304..76b9cefdc 100644
--- a/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue
+++ b/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue
@@ -3,6 +3,7 @@ import { ref } from 'vue';
 import { useI18n } from 'vue-i18n';
 
 import TransferInvoiceForm from 'src/components/TransferInvoiceForm.vue';
+import SendInvoiceForm from './SendInvoiceForm.vue';
 
 import { useSession } from 'src/composables/useSession';
 import { usePrintService } from 'composables/usePrintService';
@@ -20,6 +21,8 @@ const { t } = useI18n();
 const { openReport } = usePrintService();
 
 const transferInvoiceDialogRef = ref();
+const sendInvoiceFormRef = ref();
+const invoiceFormType = ref(null);
 
 const showInvoicePdf = () => {
     const url = `api/InvoiceOuts/${$props.invoiceOutData.id}/download?access_token=${token}`;
@@ -31,6 +34,11 @@ const showInvoiceCsv = () => {
         recipientId: $props.invoiceOutData.client.id,
     });
 };
+
+const showSendInvoiceForm = (type) => {
+    invoiceFormType.value = type;
+    sendInvoiceFormRef.value.show();
+};
 </script>
 
 <template>
@@ -60,10 +68,10 @@ const showInvoiceCsv = () => {
         </QItemSection>
         <QMenu anchor="top end" self="top start">
             <QList>
-                <QItem v-ripple clickable>
+                <QItem v-ripple clickable @click="showSendInvoiceForm()">
                     <QItemSection>{{ t('Send PDF') }}</QItemSection>
                 </QItem>
-                <QItem v-ripple clickable>
+                <QItem v-ripple clickable @click="showSendInvoiceForm()">
                     <QItemSection>{{ t('Send CSV') }}</QItemSection>
                 </QItem>
             </QList>
@@ -98,6 +106,9 @@ const showInvoiceCsv = () => {
     <QDialog ref="transferInvoiceDialogRef">
         <TransferInvoiceForm :invoice-out-data="invoiceOutData" />
     </QDialog>
+    <QDialog ref="sendInvoiceFormRef">
+        <SendInvoiceForm />
+    </QDialog>
 </template>
 
 <i18n>
diff --git a/src/pages/InvoiceOut/Card/SendInvoiceForm.vue b/src/pages/InvoiceOut/Card/SendInvoiceForm.vue
new file mode 100644
index 000000000..3956dab08
--- /dev/null
+++ b/src/pages/InvoiceOut/Card/SendInvoiceForm.vue
@@ -0,0 +1,41 @@
+<script setup>
+import { ref } from 'vue';
+import { useI18n } from 'vue-i18n';
+
+import VnRow from 'components/ui/VnRow.vue';
+import VnInput from 'src/components/common/VnInput.vue';
+import FormPopup from 'src/components/FormPopup.vue';
+
+const emit = defineEmits(['onDataSaved']);
+
+const { t } = useI18n();
+
+const email = ref(null);
+
+const onDataSaved = (dataSaved) => {
+    emit('onDataSaved', dataSaved);
+};
+</script>
+
+<template>
+    <FormPopup
+        :title="t('New thermograph')"
+        :subtitle="t('Are you sure you want to send it?')"
+        @on-data-saved="onDataSaved($event)"
+    >
+        <template #form-inputs>
+            <VnRow class="row q-gutter-md q-mb-md">
+                <div class="col">
+                    <VnInput :label="t('Email')" v-model="email" type="email" />
+                </div>
+            </VnRow>
+        </template>
+    </FormPopup>
+</template>
+
+<i18n>
+es:
+    Email: Email
+    Confirm: Confirmar
+    Are you sure you want to send it?: ¿Seguro que quieres enviarlo?
+</i18n>

From c407566d676d707154438d3a4f9dc9365358d4f0 Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Wed, 21 Feb 2024 11:27:32 -0300
Subject: [PATCH 09/42] descriptor menu actions

---
 src/components/FormPopup.vue                  |  19 +-
 src/components/TransferInvoiceForm.vue        |  44 ++---
 src/composables/useVnConfirm.js               |  23 +++
 .../Card/InvoiceOutDescriptorMenu.vue         | 163 ++++++++++++++++--
 4 files changed, 192 insertions(+), 57 deletions(-)
 create mode 100644 src/composables/useVnConfirm.js

diff --git a/src/components/FormPopup.vue b/src/components/FormPopup.vue
index 951d697d5..b6eff5d80 100644
--- a/src/components/FormPopup.vue
+++ b/src/components/FormPopup.vue
@@ -2,7 +2,7 @@
 import { ref } from 'vue';
 import { useI18n } from 'vue-i18n';
 
-const emit = defineEmits(['onDataSaved']);
+const emit = defineEmits(['onSubmit']);
 
 const $props = defineProps({
     title: {
@@ -13,7 +13,7 @@ const $props = defineProps({
         type: String,
         default: '',
     },
-    defaultSaveButton: {
+    defaultSubmitButton: {
         type: Boolean,
         default: true,
     },
@@ -21,6 +21,10 @@ const $props = defineProps({
         type: Boolean,
         default: true,
     },
+    customSubmitButtonLabel: {
+        type: String,
+        default: '',
+    },
 });
 
 const { t } = useI18n();
@@ -28,8 +32,8 @@ const { t } = useI18n();
 const closeButton = ref(null);
 const isLoading = ref(false);
 
-const onDataSaved = (dataSaved) => {
-    emit('onDataSaved', dataSaved);
+const onSubmit = () => {
+    emit('onSubmit');
     closeForm();
 };
 
@@ -40,7 +44,7 @@ const closeForm = () => {
 
 <template>
     <QForm
-        @submit="onDataSaved($event)"
+        @submit="onSubmit($event)"
         class="all-pointer-events full-width"
         style="max-width: 800px"
     >
@@ -53,8 +57,8 @@ const closeForm = () => {
             <slot name="form-inputs" />
             <div class="q-mt-lg row justify-end">
                 <QBtn
-                    v-if="defaultSaveButton"
-                    :label="t('globals.save')"
+                    v-if="defaultSubmitButton"
+                    :label="customSubmitButtonLabel || t('globals.save')"
                     type="submit"
                     color="primary"
                     :disabled="isLoading"
@@ -63,7 +67,6 @@ const closeForm = () => {
                 <QBtn
                     v-if="defaultCancelButton"
                     :label="t('globals.cancel')"
-                    type="reset"
                     color="primary"
                     flat
                     class="q-ml-sm"
diff --git a/src/components/TransferInvoiceForm.vue b/src/components/TransferInvoiceForm.vue
index e62c7638c..b7d8f3081 100644
--- a/src/components/TransferInvoiceForm.vue
+++ b/src/components/TransferInvoiceForm.vue
@@ -6,6 +6,8 @@ import { useRouter } from 'vue-router';
 import VnRow from 'components/ui/VnRow.vue';
 import FetchData from 'components/FetchData.vue';
 import VnSelectFilter from 'components/common/VnSelectFilter.vue';
+import FormPopup from './FormPopup.vue';
+
 import axios from 'axios';
 import useNotify from 'src/composables/useNotify.js';
 
@@ -25,7 +27,6 @@ const transferInvoiceParams = reactive({
     refFk: $props.invoiceOutData?.ref,
 });
 const closeButton = ref(null);
-const isLoading = ref(false);
 const clientsOptions = ref([]);
 const rectificativeTypeOptions = ref([]);
 const siiTypeInvoiceOutsOptions = ref([]);
@@ -74,12 +75,13 @@ const transferInvoice = async () => {
         @on-fetch="(data) => (invoiceCorrectionTypesOptions = data)"
         auto-load
     />
-    <QForm @submit="transferInvoice()" class="all-pointer-events">
-        <QCard class="column" style="padding: 32px; z-index: 100">
-            <span ref="closeButton" class="close-icon" v-close-popup>
-                <QIcon name="close" size="sm" />
-            </span>
-            <h1 class="title">{{ t('Transfer invoice') }}</h1>
+    <FormPopup
+        @on-submit="transferInvoice()"
+        :title="t('Transfer invoice')"
+        :custom-submit-button-label="t('Transfer client')"
+        :default-cancel-button="false"
+    >
+        <template #form-inputs>
             <VnRow class="row q-gutter-md q-mb-md">
                 <div class="col">
                     <VnSelectFilter
@@ -150,17 +152,8 @@ const transferInvoice = async () => {
                     />
                 </div>
             </VnRow>
-            <div class="q-mt-lg row justify-end">
-                <QBtn
-                    :label="t('Transfer client')"
-                    type="submit"
-                    color="primary"
-                    :disabled="isLoading"
-                    :loading="isLoading"
-                />
-            </div>
-        </QCard>
-    </QForm>
+        </template>
+    </FormPopup>
 </template>
 
 <i18n>
@@ -173,18 +166,3 @@ es:
     Type: Tipo
     Transferred invoice: Factura transferida
 </i18n>
-
-<style lang="scss" scoped>
-.title {
-    font-size: 17px;
-    font-weight: bold;
-    line-height: 20px;
-}
-
-.close-icon {
-    position: absolute;
-    top: 20px;
-    right: 20px;
-    cursor: pointer;
-}
-</style>
diff --git a/src/composables/useVnConfirm.js b/src/composables/useVnConfirm.js
new file mode 100644
index 000000000..430a290be
--- /dev/null
+++ b/src/composables/useVnConfirm.js
@@ -0,0 +1,23 @@
+import VnConfirm from 'components/ui/VnConfirm.vue';
+import { useQuasar } from 'quasar';
+
+export function useSummaryDialog() {
+    const quasar = useQuasar();
+
+    const openConfirmationModal = (title, message, promise, successFn) => {
+        quasar
+            .dialog({
+                component: VnConfirm,
+                componentProps: {
+                    title: title,
+                    message: message,
+                    promise: promise,
+                },
+            })
+            .onOk(async () => {
+                if (successFn) successFn();
+            });
+    };
+
+    return { openConfirmationModal };
+}
diff --git a/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue b/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue
index 76b9cefdc..fc84c042b 100644
--- a/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue
+++ b/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue
@@ -1,12 +1,17 @@
 <script setup>
 import { ref } from 'vue';
 import { useI18n } from 'vue-i18n';
+import { useRouter } from 'vue-router';
+import { useQuasar } from 'quasar';
 
 import TransferInvoiceForm from 'src/components/TransferInvoiceForm.vue';
-import SendInvoiceForm from './SendInvoiceForm.vue';
+import SendEmailDialog from 'components/common/SendEmailDialog.vue';
 
+import useNotify from 'src/composables/useNotify';
 import { useSession } from 'src/composables/useSession';
 import { usePrintService } from 'composables/usePrintService';
+import { useSummaryDialog } from 'composables/useVnConfirm';
+import axios from 'axios';
 
 const $props = defineProps({
     invoiceOutData: {
@@ -15,14 +20,18 @@ const $props = defineProps({
     },
 });
 
+const { notify } = useNotify();
+const router = useRouter();
 const session = useSession();
 const token = session.getToken();
 const { t } = useI18n();
-const { openReport } = usePrintService();
+const { openReport, sendEmail } = usePrintService();
+const { openConfirmationModal } = useSummaryDialog();
+const quasar = useQuasar();
 
 const transferInvoiceDialogRef = ref();
-const sendInvoiceFormRef = ref();
-const invoiceFormType = ref(null);
+const invoiceFormType = ref('pdf');
+const defaultEmailAddress = ref($props.invoiceOutData.client?.email);
 
 const showInvoicePdf = () => {
     const url = `api/InvoiceOuts/${$props.invoiceOutData.id}/download?access_token=${token}`;
@@ -35,9 +44,86 @@ const showInvoiceCsv = () => {
     });
 };
 
-const showSendInvoiceForm = (type) => {
+const showSendInvoiceDialog = (type) => {
     invoiceFormType.value = type;
-    sendInvoiceFormRef.value.show();
+    quasar.dialog({
+        component: SendEmailDialog,
+        componentProps: {
+            data: {
+                address: defaultEmailAddress.value,
+            },
+            promise: sendEmailInvoice,
+        },
+    });
+};
+
+const sendEmailInvoice = async ({ address }) => {
+    try {
+        if (!address) notify(`The email can't be empty`, 'negative');
+
+        if (invoiceFormType.value === 'pdf') {
+            return sendEmail(`InvoiceOuts/${$props.invoiceOutData.ref}/invoice-email`, {
+                recipientId: $props.invoiceOutData.client.id,
+                recipient: address,
+            });
+        } else {
+            return sendEmail(
+                `InvoiceOuts/${$props.invoiceOutData.ref}/invoice-csv-email`,
+                {
+                    recipientId: $props.invoiceOutData.client.id,
+                    recipient: address,
+                }
+            );
+        }
+    } catch (err) {
+        console.error('Error sending email', err);
+    }
+};
+
+const redirectToInvoiceOutList = () => {
+    router.push({ name: 'InvoiceOutList' });
+};
+
+const deleteInvoice = async () => {
+    try {
+        await axios.post(`InvoiceOuts/${$props.invoiceOutData.id}/delete`);
+        notify(t('InvoiceOut deleted'), 'positive');
+    } catch (err) {
+        console.error('Error deleting invoice out', err);
+    }
+};
+
+const bookInvoice = async () => {
+    try {
+        await axios.post(`InvoiceOuts/${$props.invoiceOutData.ref}/book`);
+        notify(t('InvoiceOut booked'), 'positive');
+    } catch (err) {
+        console.error('Error booking invoice out', err);
+    }
+};
+
+const generateInvoicePdf = async () => {
+    try {
+        await axios.post(`InvoiceOuts/${$props.invoiceOutData.id}/createPdf`);
+        notify(t('The invoice PDF document has been regenerated'), 'positive');
+    } catch (err) {
+        console.error('Error generating invoice out pdf', err);
+    }
+};
+
+const refundInvoice = async (withWarehouse) => {
+    try {
+        const params = { ref: $props.invoiceOutData.ref, withWarehouse: withWarehouse };
+        const { data } = await axios.post('InvoiceOuts/refund', params);
+        notify(
+            t('refundInvoiceSuccessMessage', {
+                refundTicket: data[0].id,
+            }),
+            'positive'
+        );
+    } catch (err) {
+        console.error('Error generating invoice out pdf', err);
+    }
 };
 </script>
 
@@ -68,22 +154,53 @@ const showSendInvoiceForm = (type) => {
         </QItemSection>
         <QMenu anchor="top end" self="top start">
             <QList>
-                <QItem v-ripple clickable @click="showSendInvoiceForm()">
+                <QItem v-ripple clickable @click="showSendInvoiceDialog('pdf')">
                     <QItemSection>{{ t('Send PDF') }}</QItemSection>
                 </QItem>
-                <QItem v-ripple clickable @click="showSendInvoiceForm()">
+                <QItem v-ripple clickable @click="showSendInvoiceDialog('csv')">
                     <QItemSection>{{ t('Send CSV') }}</QItemSection>
                 </QItem>
             </QList>
         </QMenu>
     </QItem>
-    <QItem v-ripple clickable>
+    <QItem
+        v-ripple
+        clickable
+        @click="
+            openConfirmationModal(
+                t('Confirm deletion'),
+                t('Are you sure you want to delete this invoice?'),
+                deleteInvoice,
+                redirectToInvoiceOutList
+            )
+        "
+    >
         <QItemSection>{{ t('Delete invoice') }}</QItemSection>
     </QItem>
-    <QItem v-ripple clickable>
+    <QItem
+        v-ripple
+        clickable
+        @click="
+            openConfirmationModal(
+                '',
+                t('Are you sure you want to book this invoice?'),
+                bookInvoice
+            )
+        "
+    >
         <QItemSection>{{ t('Book invoice') }}</QItemSection>
     </QItem>
-    <QItem v-ripple clickable>
+    <QItem
+        v-ripple
+        clickable
+        @click="
+            openConfirmationModal(
+                t('Generate PDF invoice document'),
+                t('Are you sure you want to generate/regenerate the PDF invoice?'),
+                generateInvoicePdf
+            )
+        "
+    >
         <QItemSection>{{ t('Generate PDF invoice') }}</QItemSection>
     </QItem>
     <QItem v-ripple clickable>
@@ -93,22 +210,22 @@ const showSendInvoiceForm = (type) => {
         </QItemSection>
         <QMenu anchor="top end" self="top start">
             <QList>
-                <QItem v-ripple clickable>
+                <QItem v-ripple clickable @click="refundInvoice(true)">
                     <QItemSection>{{ t('With warehouse') }}</QItemSection>
                 </QItem>
-                <QItem v-ripple clickable>
+                <QItem v-ripple clickable @click="refundInvoice(false)">
                     <QItemSection>{{ t('Without warehouse') }}</QItemSection>
                 </QItem>
             </QList>
         </QMenu>
+        <QTooltip>
+            {{ t('Create a single ticket with all the content of the current invoice') }}
+        </QTooltip>
     </QItem>
 
     <QDialog ref="transferInvoiceDialogRef">
         <TransferInvoiceForm :invoice-out-data="invoiceOutData" />
     </QDialog>
-    <QDialog ref="sendInvoiceFormRef">
-        <SendInvoiceForm />
-    </QDialog>
 </template>
 
 <i18n>
@@ -126,4 +243,18 @@ es:
     Send CSV: Enviar CSV
     With warehouse: Con almacén
     Without warehouse: Sin almacén
+    InvoiceOut deleted: Factura eliminada
+    Confirm deletion: Confirmar eliminación
+    Are you sure you want to delete this invoice?: Estas seguro de eliminar esta factura?
+    Are you sure you want to book this invoice?: Estas seguro de querer asentar esta factura?
+    InvoiceOut booked: Factura asentada
+    Generate PDF invoice document: Generar PDF de la factura
+    Are you sure you want to generate/regenerate the PDF invoice?: ¿Seguro que quieres generar/regenerar el PDF de la factura?
+    The invoice PDF document has been regenerated: El documento PDF de la factura ha sido regenerado
+    Create a single ticket with all the content of the current invoice: Crear un ticket único con todo el contenido de la factura actual
+    refundInvoiceSuccessMessage: Se ha creado el siguiente ticket de abono {refundTicket}
+    The email can't be empty: El email no puede estar vacío
+
+en:
+    refundInvoiceSuccessMessage: The following refund ticket have been created {refundTicket}
 </i18n>

From 4af2ef49a07befb22748fae0b934a501b6611ebf Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Mon, 26 Feb 2024 07:11:33 +0100
Subject: [PATCH 10/42] refs #6930: replace tokenUSer with tokenMultimedia

---
 src/components/NavBar.vue                     |  4 ++--
 src/components/UserPanel.vue                  |  2 +-
 src/components/ui/VnAvatar.vue                |  4 ++--
 src/composables/downloadFile.js               |  4 ++--
 src/composables/useSession.js                 | 16 ++++++++++++++--
 src/pages/Claim/Card/ClaimBasicData.vue       |  4 ++--
 src/pages/Claim/Card/ClaimPhoto.vue           |  4 ++--
 src/pages/Claim/Card/ClaimSummary.vue         |  4 ++--
 src/pages/Customer/Card/CustomerBasicData.vue |  4 ++--
 src/pages/Entry/EntryLatestBuys.vue           |  4 ++--
 src/pages/Item/Card/ItemDescriptor.vue        |  4 ++--
 src/pages/Order/Card/OrderCatalogItem.vue     |  4 ++--
 src/pages/Order/OrderLines.vue                |  4 ++--
 src/pages/Route/Cmr/CmrList.vue               |  4 ++--
 src/pages/Route/RouteList.vue                 | 16 ++++++++--------
 src/pages/Wagon/WagonCounter.vue              |  4 ++--
 src/pages/Worker/Card/WorkerDescriptor.vue    |  4 ++--
 17 files changed, 51 insertions(+), 39 deletions(-)

diff --git a/src/components/NavBar.vue b/src/components/NavBar.vue
index 12366e174..17b8c2b7e 100644
--- a/src/components/NavBar.vue
+++ b/src/components/NavBar.vue
@@ -10,12 +10,12 @@ import UserPanel from 'components/UserPanel.vue';
 import VnBreadcrumbs from './common/VnBreadcrumbs.vue';
 
 const { t } = useI18n();
-const session = useSession();
 const stateStore = useStateStore();
 const quasar = useQuasar();
 const state = useState();
 const user = state.getUser();
-const token = session.getToken();
+const { getTokenMultimedia } = useSession();
+const token = getTokenMultimedia();
 const appName = 'Lilium';
 
 onMounted(() => stateStore.setMounted());
diff --git a/src/components/UserPanel.vue b/src/components/UserPanel.vue
index e0b6b86ed..a18dad79a 100644
--- a/src/components/UserPanel.vue
+++ b/src/components/UserPanel.vue
@@ -44,7 +44,7 @@ const darkMode = computed({
 });
 
 const user = state.getUser();
-const token = session.getToken();
+const token = session.getTokenMultimedia();
 
 onMounted(async () => {
     updatePreferences();
diff --git a/src/components/ui/VnAvatar.vue b/src/components/ui/VnAvatar.vue
index 5a5483084..287f741e0 100644
--- a/src/components/ui/VnAvatar.vue
+++ b/src/components/ui/VnAvatar.vue
@@ -10,8 +10,8 @@ const $props = defineProps({
     size: { type: String, default: null },
     title: { type: String, default: null },
 });
-const session = useSession();
-const token = session.getToken();
+const { getTokenMultimedia } = useSession();
+const token = getTokenMultimedia();
 const { t } = useI18n();
 
 const title = computed(() => $props.title ?? t('globals.system'));
diff --git a/src/composables/downloadFile.js b/src/composables/downloadFile.js
index 877d2a254..b26dec731 100644
--- a/src/composables/downloadFile.js
+++ b/src/composables/downloadFile.js
@@ -1,8 +1,8 @@
 import { useSession } from 'src/composables/useSession';
 import { getUrl } from './getUrl';
 
-const session = useSession();
-const token = session.getToken();
+const {getTokenMultimedia} = useSession();
+const token =  getTokenMultimedia();
 
 export async function downloadFile(dmsId) {
     let appUrl = await getUrl('', 'lilium');
diff --git a/src/composables/useSession.js b/src/composables/useSession.js
index 008337b02..4d182a934 100644
--- a/src/composables/useSession.js
+++ b/src/composables/useSession.js
@@ -10,12 +10,20 @@ export function useSession() {
 
         return localToken || sessionToken || '';
     }
+    function getTokenMultimedia() {
+        const localTokenMultimedia = localStorage.getItem('tokenMultimedia');
+        const sessionTokenMultimedia = sessionStorage.getItem('tokenMultimedia');
+
+        return localTokenMultimedia || sessionTokenMultimedia || '';
+    }
 
     function setToken(data) {
         if (data.keepLogin) {
             localStorage.setItem('token', data.token);
+            localStorage.setItem('tokenMultimedia', data.tokenMultimedia);
         } else {
             sessionStorage.setItem('token', data.token);
+            sessionStorage.setItem('tokenMultimedia', data.tokenMultimedia);
         }
     }
 
@@ -23,6 +31,9 @@ export function useSession() {
         if (localStorage.getItem('token'))
             localStorage.removeItem('token')
 
+        if (localStorage.getItem('tokenMultimedia'))
+            localStorage.removeItem('tokenMultimedia')
+
         if (sessionStorage.getItem('token'))
             sessionStorage.removeItem('token');
 
@@ -37,8 +48,8 @@ export function useSession() {
         });
     }
 
-    async function login(token, keepLogin) {
-        setToken({ token, keepLogin });
+    async function login(token, tokenMultimedia, keepLogin) {
+        setToken({ token, tokenMultimedia, keepLogin });
 
         await useRole().fetch();
         await useUserConfig().fetch();
@@ -53,6 +64,7 @@ export function useSession() {
 
     return {
         getToken,
+        getTokenMultimedia,
         setToken,
         destroy,
         login,
diff --git a/src/pages/Claim/Card/ClaimBasicData.vue b/src/pages/Claim/Card/ClaimBasicData.vue
index 35f93c736..1c4f998d8 100644
--- a/src/pages/Claim/Card/ClaimBasicData.vue
+++ b/src/pages/Claim/Card/ClaimBasicData.vue
@@ -13,8 +13,8 @@ import { useSession } from 'src/composables/useSession';
 
 const route = useRoute();
 const { t } = useI18n();
-const session = useSession();
-const token = session.getToken();
+const { getTokenMultimedia } = useSession();
+const token = getTokenMultimedia();
 
 const claimFilter = {
     fields: [
diff --git a/src/pages/Claim/Card/ClaimPhoto.vue b/src/pages/Claim/Card/ClaimPhoto.vue
index 6ac116ce0..dd97b60df 100644
--- a/src/pages/Claim/Card/ClaimPhoto.vue
+++ b/src/pages/Claim/Card/ClaimPhoto.vue
@@ -11,8 +11,8 @@ import FetchData from 'components/FetchData.vue';
 const router = useRouter();
 const quasar = useQuasar();
 const { t } = useI18n();
-const session = useSession();
-const token = session.getToken();
+const { getTokenMultimedia } = useSession();
+const token = getTokenMultimedia();
 
 const claimId = computed(() => router.currentRoute.value.params.id);
 
diff --git a/src/pages/Claim/Card/ClaimSummary.vue b/src/pages/Claim/Card/ClaimSummary.vue
index cdc1f15be..9281cc08a 100644
--- a/src/pages/Claim/Card/ClaimSummary.vue
+++ b/src/pages/Claim/Card/ClaimSummary.vue
@@ -14,8 +14,8 @@ import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
 
 const route = useRoute();
 const { t } = useI18n();
-const session = useSession();
-const token = session.getToken();
+const { getTokenMultimedia } = useSession();
+const token = getTokenMultimedia();
 
 const $props = defineProps({
     id: {
diff --git a/src/pages/Customer/Card/CustomerBasicData.vue b/src/pages/Customer/Card/CustomerBasicData.vue
index 458d50082..d23b08f17 100644
--- a/src/pages/Customer/Card/CustomerBasicData.vue
+++ b/src/pages/Customer/Card/CustomerBasicData.vue
@@ -11,8 +11,8 @@ import VnInput from 'src/components/common/VnInput.vue';
 
 const route = useRoute();
 const { t } = useI18n();
-const session = useSession();
-const token = session.getToken();
+const { getTokenMultimedia } = useSession();
+const token = getTokenMultimedia();
 
 const workers = ref([]);
 const workersCopy = ref([]);
diff --git a/src/pages/Entry/EntryLatestBuys.vue b/src/pages/Entry/EntryLatestBuys.vue
index f4a423f3b..db66b8f0b 100644
--- a/src/pages/Entry/EntryLatestBuys.vue
+++ b/src/pages/Entry/EntryLatestBuys.vue
@@ -15,8 +15,8 @@ import { useSession } from 'composables/useSession';
 import { dashIfEmpty } from 'src/filters';
 
 const router = useRouter();
-const session = useSession();
-const token = session.getToken();
+const { getTokenMultimedia } = useSession();
+const token = getTokenMultimedia();
 const stateStore = useStateStore();
 const { t } = useI18n();
 
diff --git a/src/pages/Item/Card/ItemDescriptor.vue b/src/pages/Item/Card/ItemDescriptor.vue
index 362fcfc67..af6de04e8 100644
--- a/src/pages/Item/Card/ItemDescriptor.vue
+++ b/src/pages/Item/Card/ItemDescriptor.vue
@@ -36,7 +36,7 @@ const quasar = useQuasar();
 const route = useRoute();
 const router = useRouter();
 const { t } = useI18n();
-const { getToken } = useSession();
+const { getTokenMultimedia } = useSession();
 const state = useState();
 const user = state.getUser();
 
@@ -72,7 +72,7 @@ onMounted(async () => {
 });
 
 const getItemAvatar = async () => {
-    const token = getToken();
+    const token = getTokenMultimedia();
     const timeStamp = `timestamp=${Date.now()}`;
     image.value = `/api/Images/catalog/200x200/${entityId.value}/download?access_token=${token}&${timeStamp}`;
 };
diff --git a/src/pages/Order/Card/OrderCatalogItem.vue b/src/pages/Order/Card/OrderCatalogItem.vue
index ee73bcffb..a197ceafc 100644
--- a/src/pages/Order/Card/OrderCatalogItem.vue
+++ b/src/pages/Order/Card/OrderCatalogItem.vue
@@ -11,8 +11,8 @@ import toCurrency from '../../../filters/toCurrency';
 
 const DEFAULT_PRICE_KG = 0;
 
-const session = useSession();
-const token = session.getToken();
+const { getTokenMultimedia } = useSession();
+const token = getTokenMultimedia();
 const { t } = useI18n();
 
 defineProps({
diff --git a/src/pages/Order/OrderLines.vue b/src/pages/Order/OrderLines.vue
index a2ee42481..4b6c21c75 100644
--- a/src/pages/Order/OrderLines.vue
+++ b/src/pages/Order/OrderLines.vue
@@ -17,9 +17,9 @@ import axios from 'axios';
 
 const route = useRoute();
 const { t } = useI18n();
-const session = useSession();
+const { getTokenMultimedia } = useSession();
 const quasar = useQuasar();
-const token = session.getToken();
+const token = getTokenMultimedia();
 const orderSummary = ref({
     total: null,
     vat: null,
diff --git a/src/pages/Route/Cmr/CmrList.vue b/src/pages/Route/Cmr/CmrList.vue
index dc5602c1c..c0a6d263d 100644
--- a/src/pages/Route/Cmr/CmrList.vue
+++ b/src/pages/Route/Cmr/CmrList.vue
@@ -12,8 +12,8 @@ import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy
 
 const stateStore = useStateStore();
 const { t } = useI18n();
-const session = useSession();
-const token = session.getToken();
+const { getTokenMultimedia } = useSession();
+const token = getTokenMultimedia();
 const selected = ref([]);
 
 const columns = computed(() => [
diff --git a/src/pages/Route/RouteList.vue b/src/pages/Route/RouteList.vue
index 0b4aa6795..be8a29ceb 100644
--- a/src/pages/Route/RouteList.vue
+++ b/src/pages/Route/RouteList.vue
@@ -16,7 +16,7 @@ import RouteSearchbar from 'pages/Route/Card/RouteSearchbar.vue';
 import RouteFilter from 'pages/Route/Card/RouteFilter.vue';
 import RouteSummary from 'pages/Route/Card/RouteSummary.vue';
 import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
-import {useSession} from "composables/useSession";
+import { useSession } from 'composables/useSession';
 import { useSummaryDialog } from 'src/composables/useSummaryDialog';
 
 const stateStore = useStateStore();
@@ -144,21 +144,21 @@ const cloneRoutes = () => {
 };
 
 const showRouteReport = () => {
-    const ids = selectedRows.value.map(row => row?.id)
-    const idString = ids.join(',')
+    const ids = selectedRows.value.map((row) => row?.id);
+    const idString = ids.join(',');
     let url;
 
     if (selectedRows.value.length <= 1) {
-        url = `api/Routes/${idString}/driver-route-pdf?access_token=${session.getToken()}`;
+        url = `api/Routes/${idString}/driver-route-pdf?access_token=${session.getTokenMultimedia()}`;
     } else {
         const params = new URLSearchParams({
-            access_token: session.getToken(),
-            id: idString
-        })
+            access_token: session.getTokenMultimedia(),
+            id: idString,
+        });
         url = `api/Routes/downloadZip?${params.toString()}`;
     }
     window.open(url, '_blank');
-}
+};
 
 const markAsServed = () => {
     selectedRows.value.forEach((row) => {
diff --git a/src/pages/Wagon/WagonCounter.vue b/src/pages/Wagon/WagonCounter.vue
index bd5d2ca67..15a1ab7ba 100644
--- a/src/pages/Wagon/WagonCounter.vue
+++ b/src/pages/Wagon/WagonCounter.vue
@@ -7,8 +7,8 @@ import VnConfirm from 'components/ui/VnConfirm.vue';
 
 const quasar = useQuasar();
 const { t } = useI18n();
-const session = useSession();
-const token = session.getToken();
+const { getTokenMultimedia } = useSession();
+const token = getTokenMultimedia();
 
 const counters = ref({
     alquilerBandeja: { count: 0, id: 96001, title: 'CC Bandeja', isTray: true },
diff --git a/src/pages/Worker/Card/WorkerDescriptor.vue b/src/pages/Worker/Card/WorkerDescriptor.vue
index bec56bee7..5144b3bfa 100644
--- a/src/pages/Worker/Card/WorkerDescriptor.vue
+++ b/src/pages/Worker/Card/WorkerDescriptor.vue
@@ -22,7 +22,7 @@ const $props = defineProps({
 
 const route = useRoute();
 const { t } = useI18n();
-const { getToken } = useSession();
+const { getTokenMultimedia } = useSession();
 
 const entityId = computed(() => {
     return $props.id || route.params.id;
@@ -56,7 +56,7 @@ const filter = {
 const sip = computed(() => worker.value?.sip && worker.value.sip.extension);
 
 function getWorkerAvatar() {
-    const token = getToken();
+    const token = getTokenMultimedia();
     return `/api/Images/user/160x160/${entityId.value}/download?access_token=${token}`;
 }
 const data = ref(useCardDescription());

From d86675446827f092d816ff701d516aea1940bb26 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Mon, 26 Feb 2024 07:47:22 +0100
Subject: [PATCH 11/42] refs #6930:  handle tokenMultimedia

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

diff --git a/src/pages/Login/LoginMain.vue b/src/pages/Login/LoginMain.vue
index e2ccdac16..7a141e878 100644
--- a/src/pages/Login/LoginMain.vue
+++ b/src/pages/Login/LoginMain.vue
@@ -31,7 +31,7 @@ async function onSubmit() {
 
         if (!data) return;
 
-        await session.login(data.token, keepLogin.value);
+        await session.login(data.token, data.multimediaToken.id, keepLogin.value);
 
         quasar.notify({
             message: t('login.loginSuccess'),

From 564533604047c2185de21fbf2762453212ae0449 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Wed, 28 Feb 2024 07:55:11 +0100
Subject: [PATCH 12/42] refs #6795 hotFix(claimSummary) fix ItemDescriptorProxy

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

diff --git a/src/pages/Claim/Card/ClaimSummary.vue b/src/pages/Claim/Card/ClaimSummary.vue
index b93cbbd81..7d6def871 100644
--- a/src/pages/Claim/Card/ClaimSummary.vue
+++ b/src/pages/Claim/Card/ClaimSummary.vue
@@ -259,7 +259,7 @@ function openDialog(dmsId) {
                                 >
                                 <ItemDescriptorProxy
                                     v-if="col.name == 'description'"
-                                    :id="props.row.id"
+                                    :id="props.row.sale.itemFk"
                                     :sale-fk="props.row.saleFk"
                                 ></ItemDescriptorProxy>
                             </QTh>

From bddfe8f9d7ff94ca253b2e34acf57434712976c2 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Wed, 28 Feb 2024 08:33:26 +0100
Subject: [PATCH 13/42] refs #6930 feat: call shareToken endpoint

---
 src/pages/Login/LoginMain.vue | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/src/pages/Login/LoginMain.vue b/src/pages/Login/LoginMain.vue
index 7a141e878..0bdc02142 100644
--- a/src/pages/Login/LoginMain.vue
+++ b/src/pages/Login/LoginMain.vue
@@ -30,8 +30,15 @@ async function onSubmit() {
         const { data } = await axios.post('Accounts/login', params);
 
         if (!data) return;
+        const {
+            data: { multimediaToken },
+        } = await axios.get('VnUsers/ShareToken', {
+            headers: { Authorization: data.token },
+        });
 
-        await session.login(data.token, data.multimediaToken.id, keepLogin.value);
+        if (!multimediaToken) return;
+
+        await session.login(data.token, multimediaToken.id, keepLogin.value);
 
         quasar.notify({
             message: t('login.loginSuccess'),

From 0d80fd37a4127c7f395916f505cf6873bef53ba3 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Wed, 28 Feb 2024 11:28:57 +0100
Subject: [PATCH 14/42] refs #6930 feat: call loutMultimedia endpoint

---
 src/boot/axios.js             |  2 +-
 src/composables/useSession.js | 37 ++++++++++++++++++++++++++++-------
 2 files changed, 31 insertions(+), 8 deletions(-)

diff --git a/src/boot/axios.js b/src/boot/axios.js
index c58cc2d08..e3e7289af 100644
--- a/src/boot/axios.js
+++ b/src/boot/axios.js
@@ -11,7 +11,7 @@ axios.defaults.baseURL = '/api/';
 
 const onRequest = (config) => {
     const token = session.getToken();
-    if (token.length && config.headers) {
+    if (token.length && !config.headers.Authorization) {
         config.headers.Authorization = token;
     }
 
diff --git a/src/composables/useSession.js b/src/composables/useSession.js
index 4d182a934..580efbdf8 100644
--- a/src/composables/useSession.js
+++ b/src/composables/useSession.js
@@ -1,6 +1,7 @@
 import { useState } from './useState';
 import { useRole } from './useRole';
 import { useUserConfig } from './useUserConfig';
+import axios from 'axios';
 
 
 export function useSession() {
@@ -27,15 +28,37 @@ export function useSession() {
         }
     }
 
-    function destroy() {
-        if (localStorage.getItem('token'))
-            localStorage.removeItem('token')
-
-        if (localStorage.getItem('tokenMultimedia'))
+    async function destroy() {
+          if (localStorage.getItem('tokenMultimedia')){
+            await axios.post('VnUsers/logoutMultimedia',  null, {
+                headers: {Authorization: localStorage.getItem('tokenMultimedia') }
+            });
             localStorage.removeItem('tokenMultimedia')
 
-        if (sessionStorage.getItem('token'))
-            sessionStorage.removeItem('token');
+        }
+        if (localStorage.getItem('token')){
+            await axios.post('VnUsers/logout',  null, {
+                headers: {Authorization: localStorage.getItem('token') }
+            });
+            localStorage.removeItem('token')
+        }
+
+
+         if (sessionStorage.getItem('tokenMultimedia')){
+            await axios.post('VnUsers/logoutMultimedia',  null, {
+                headers: {Authorization: sessionStorage.getItem('tokenMultimedia') }
+            });
+            sessionStorage.removeItem('tokenMultimedia')
+
+        }
+        if (sessionStorage.getItem('token')){
+            await axios.post('VnUsers/logout',  null, {
+                headers: {Authorization: sessionStorage.getItem('token') }
+            });
+            sessionStorage.removeItem('token')
+        }
+
+
 
         const { setUser } = useState();
 

From 39381f29d739b2ccc865a91adf1dd5c55a460981 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Wed, 28 Feb 2024 13:15:59 +0100
Subject: [PATCH 15/42] refs #6980 test: fix failed tests

---
 .../vitest/__tests__/composables/useSession.spec.js | 13 ++++++++-----
 test/vitest/__tests__/pages/Login/Login.spec.js     |  6 +++---
 2 files changed, 11 insertions(+), 8 deletions(-)

diff --git a/test/vitest/__tests__/composables/useSession.spec.js b/test/vitest/__tests__/composables/useSession.spec.js
index 4f900aca6..f9f3dcb80 100644
--- a/test/vitest/__tests__/composables/useSession.spec.js
+++ b/test/vitest/__tests__/composables/useSession.spec.js
@@ -54,7 +54,8 @@ describe('session', () => {
             expect(localStorage.getItem('token')).toEqual('tokenToBeGone');
             expect(user.value).toEqual(previousUser);
 
-            session.destroy();
+            vi.spyOn(axios, 'post').mockResolvedValue({ data: true });
+            await session.destroy();
 
             user = state.getUser();
             expect(localStorage.getItem('token')).toBeNull();
@@ -92,9 +93,10 @@ describe('session', () => {
             });
 
             const expectedToken = 'mySessionToken';
+            const expectedTokenMultimedia = 'mySessionTokenMultimedia';
             const keepLogin = false;
 
-            await session.login(expectedToken, keepLogin);
+            await session.login(expectedToken,expectedTokenMultimedia, keepLogin);
 
             const roles = state.getRoles();
             const localToken = localStorage.getItem('token');
@@ -104,7 +106,7 @@ describe('session', () => {
             expect(localToken).toBeNull();
             expect(sessionToken).toEqual(expectedToken);
 
-            session.destroy(); // this clears token and user for any other test
+           await session.destroy(); // this clears token and user for any other test
         });
 
         it('should fetch the user roles and then set token in the localStorage', async () => {
@@ -114,9 +116,10 @@ describe('session', () => {
             });
 
             const expectedToken = 'myLocalToken';
+            const expectedTokenMultimedia = 'myLocalTokenMultimedia';
             const keepLogin = true;
 
-            await session.login(expectedToken, keepLogin);
+            await session.login(expectedToken, expectedTokenMultimedia, keepLogin);
 
             const roles = state.getRoles();
             const localToken = localStorage.getItem('token');
@@ -126,7 +129,7 @@ describe('session', () => {
             expect(localToken).toEqual(expectedToken);
             expect(sessionToken).toBeNull();
 
-            session.destroy(); // this clears token and user for any other test
+            await session.destroy(); // this clears token and user for any other test
         });
     });
 });
diff --git a/test/vitest/__tests__/pages/Login/Login.spec.js b/test/vitest/__tests__/pages/Login/Login.spec.js
index fadfc898f..6e2de9870 100644
--- a/test/vitest/__tests__/pages/Login/Login.spec.js
+++ b/test/vitest/__tests__/pages/Login/Login.spec.js
@@ -22,9 +22,9 @@ describe('Login', () => {
                 darkMode: false,
             },
         };
-        vi.spyOn(axios, 'post').mockResolvedValue({ data: { token: 'token' } });
+        vi.spyOn(axios, 'post').mockResolvedValueOnce({ data: { token: 'token' } });
         vi.spyOn(axios, 'get').mockResolvedValue({
-            data: { roles: [], user: expectedUser },
+            data: { roles: [], user: expectedUser , multimediaToken: {id:'multimediaToken' }},
         });
         vi.spyOn(vm.quasar, 'notify');
 
@@ -36,7 +36,7 @@ describe('Login', () => {
         expect(vm.quasar.notify).toHaveBeenCalledWith(
             expect.objectContaining({ type: 'positive' })
         );
-        vm.session.destroy();
+        await vm.session.destroy();
     });
 
     it('should not set the token into session if any error occurred', async () => {

From 4b860e05837c623f98f6725bfec90ab3993d6938 Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Tue, 5 Mar 2024 08:54:15 -0300
Subject: [PATCH 16/42] Change issued title

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

diff --git a/src/pages/InvoiceOut/InvoiceOutList.vue b/src/pages/InvoiceOut/InvoiceOutList.vue
index 7bdf2ffbd..ad54d9908 100644
--- a/src/pages/InvoiceOut/InvoiceOutList.vue
+++ b/src/pages/InvoiceOut/InvoiceOutList.vue
@@ -158,7 +158,7 @@ const openPdf = () => {
                         >
                             <template #list-items>
                                 <VnLv
-                                    :label="t('invoiceOut.list.amount')"
+                                    :label="t('invoiceOut.list.issued')"
                                     :value="toDate(row.issued)"
                                 />
                                 <VnLv

From 12a470b7d919e3dda0f8955a46fea6ea3a06d88b Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Tue, 5 Mar 2024 09:27:44 -0300
Subject: [PATCH 17/42] Add links to invoice out summary tickets table

---
 .../InvoiceOut/Card/InvoiceOutSummary.vue     | 30 ++++++++++---------
 1 file changed, 16 insertions(+), 14 deletions(-)

diff --git a/src/pages/InvoiceOut/Card/InvoiceOutSummary.vue b/src/pages/InvoiceOut/Card/InvoiceOutSummary.vue
index 5721818e4..d74969319 100644
--- a/src/pages/InvoiceOut/Card/InvoiceOutSummary.vue
+++ b/src/pages/InvoiceOut/Card/InvoiceOutSummary.vue
@@ -2,16 +2,14 @@
 import { onMounted, ref, computed } from 'vue';
 import { useRoute } from 'vue-router';
 import { useI18n } from 'vue-i18n';
-
+import axios from 'axios';
+import { toCurrency, toDate } from 'src/filters';
 import CardSummary from 'components/ui/CardSummary.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
+import { getUrl } from 'src/composables/getUrl';
 import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue';
 import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
 
-import { getUrl } from 'src/composables/getUrl';
-import { toCurrency, toDate } from 'src/filters';
-import axios from 'axios';
-
 onMounted(async () => {
     fetch();
     salixUrl.value = await getUrl('');
@@ -70,14 +68,14 @@ const taxColumns = ref([
 
 const ticketsColumns = ref([
     {
-        name: 'id',
+        name: 'item',
         label: t('invoiceOut.summary.ticketId'),
         field: (row) => row.id,
         sortable: true,
         align: 'left',
     },
     {
-        name: 'alias',
+        name: 'quantity',
         label: t('invoiceOut.summary.nickname'),
         field: (row) => row.nickname,
         sortable: true,
@@ -147,21 +145,25 @@ const ticketsColumns = ref([
                     </template>
                 </QTable>
             </QCard>
-            <QCard class="vn-max">
+            <QCard class="vn-three">
                 <div class="header">
                     {{ t('invoiceOut.summary.tickets') }}
                 </div>
                 <QTable v-if="tickets" :columns="ticketsColumns" :rows="tickets" flat>
-                    <template #body-cell-id="{ row }">
+                    <template #body-cell-item="{ value }">
                         <QTd>
-                            <QBtn flat color="primary"> {{ row.id }}</QBtn>
-                            <TicketDescriptorProxy :id="row.id" />
+                            <QBtn flat color="primary">
+                                {{ value }}
+                                <TicketDescriptorProxy :id="value" />
+                            </QBtn>
                         </QTd>
                     </template>
-                    <template #body-cell-alias="{ row }">
+                    <template #body-cell-quantity="{ value, row }">
                         <QTd>
-                            <QBtn flat dense color="primary"> {{ row.nickname }}</QBtn>
-                            <CustomerDescriptorProxy :id="row.clientFk" />
+                            <QBtn flat color="primary" dense>
+                                {{ value }}
+                                <CustomerDescriptorProxy :id="row.id" />
+                            </QBtn>
                         </QTd>
                     </template>
                 </QTable>

From 87ab080b38e5d2c32bde79684a38e16aaa93dd7b Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Tue, 5 Mar 2024 09:53:46 -0300
Subject: [PATCH 18/42] Change negative bases download button icon and tooltip

---
 src/pages/InvoiceOut/InvoiceOutNegativeBases.vue | 9 +++++++--
 1 file changed, 7 insertions(+), 2 deletions(-)

diff --git a/src/pages/InvoiceOut/InvoiceOutNegativeBases.vue b/src/pages/InvoiceOut/InvoiceOutNegativeBases.vue
index 5e82adf2a..e1740257a 100644
--- a/src/pages/InvoiceOut/InvoiceOutNegativeBases.vue
+++ b/src/pages/InvoiceOut/InvoiceOutNegativeBases.vue
@@ -140,7 +140,9 @@ const downloadCSV = async () => {
 <template>
     <template v-if="stateStore.isHeaderMounted()">
         <Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
-            <QBtn color="primary" icon-right="archive" no-caps @click="downloadCSV()" />
+            <QBtn color="primary" icon-right="download" no-caps @click="downloadCSV()">
+                <QTooltip>{{ t('Download as CSV') }}</QTooltip>
+            </QBtn>
         </Teleport>
     </template>
     <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above>
@@ -187,4 +189,7 @@ const downloadCSV = async () => {
 }
 </style>
 
-<i18n></i18n>
+<i18n>
+es:
+    Download as CSV: Descargar como CSV
+</i18n>

From 859c87940d92761381b53b53cb69b2f4cdea2815 Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Wed, 6 Mar 2024 08:15:50 -0300
Subject: [PATCH 19/42] Several changes

---
 src/pages/InvoiceOut/InvoiceOutGlobal.vue     | 11 ++++++-
 src/pages/InvoiceOut/InvoiceOutList.vue       |  2 +-
 .../InvoiceOut/InvoiceOutNegativeBases.vue    | 30 +++++++++++++++++--
 src/router/modules/invoiceOut.js              |  2 +-
 src/stores/invoiceOutGlobal.js                |  8 ++---
 5 files changed, 42 insertions(+), 11 deletions(-)

diff --git a/src/pages/InvoiceOut/InvoiceOutGlobal.vue b/src/pages/InvoiceOut/InvoiceOutGlobal.vue
index 7e2b43a76..6f7e4c7fd 100644
--- a/src/pages/InvoiceOut/InvoiceOutGlobal.vue
+++ b/src/pages/InvoiceOut/InvoiceOutGlobal.vue
@@ -21,6 +21,7 @@ const {
     nPdfs,
     totalPdfs,
     errors,
+    addresses,
 } = storeToRefs(invoiceOutGlobalStore);
 
 const selectedCustomerId = ref(null);
@@ -86,6 +87,14 @@ const selectCustomerId = (id) => {
     selectedCustomerId.value = id;
 };
 
+const statusText = computed(() => {
+    return status.value === 'invoicing'
+        ? `${t(`status.${status.value}`)} ${
+              addresses.value[getAddressNumber.value]?.clientId
+          }`
+        : t(`status.${status.value}`);
+});
+
 onMounted(() => (stateStore.rightDrawer = true));
 onUnmounted(() => {
     stateStore.rightDrawer = false;
@@ -103,7 +112,7 @@ onUnmounted(() => {
     <QPage class="column items-center q-pa-md">
         <QCard v-if="status" class="card">
             <QCardSection class="card-section">
-                <span class="text">{{ t(`status.${status}`) }}</span>
+                <span class="text">{{ statusText }}</span>
                 <span class="text">{{
                     t('invoiceOut.globalInvoices.statusCard.percentageText', {
                         getPercentage: getPercentage,
diff --git a/src/pages/InvoiceOut/InvoiceOutList.vue b/src/pages/InvoiceOut/InvoiceOutList.vue
index ad54d9908..ca68f0770 100644
--- a/src/pages/InvoiceOut/InvoiceOutList.vue
+++ b/src/pages/InvoiceOut/InvoiceOutList.vue
@@ -119,7 +119,7 @@ const openPdf = () => {
         <VnPaginate
             auto-load
             data-key="InvoiceOutList"
-            order="issued DESC, id DESC"
+            :order="['issued DESC', 'id DESC']"
             url="InvoiceOuts/filter"
         >
             <template #body="{ rows }">
diff --git a/src/pages/InvoiceOut/InvoiceOutNegativeBases.vue b/src/pages/InvoiceOut/InvoiceOutNegativeBases.vue
index e1740257a..fb3596499 100644
--- a/src/pages/InvoiceOut/InvoiceOutNegativeBases.vue
+++ b/src/pages/InvoiceOut/InvoiceOutNegativeBases.vue
@@ -1,5 +1,5 @@
 <script setup>
-import { ref, computed, onBeforeMount } from 'vue';
+import { ref, computed, onBeforeMount, onMounted, nextTick } from 'vue';
 import { useI18n } from 'vue-i18n';
 
 import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue';
@@ -45,6 +45,14 @@ onBeforeMount(async () => {
     stateStore.rightDrawer = true;
 });
 
+const componentIsRendered = ref(false);
+
+onMounted(() =>
+    nextTick(() => {
+        componentIsRendered.value = true;
+    })
+);
+
 const rows = computed(() => arrayData.value.store.data);
 
 const columns = computed(() => [
@@ -139,7 +147,10 @@ const downloadCSV = async () => {
 
 <template>
     <template v-if="stateStore.isHeaderMounted()">
-        <Teleport to="#st-actions" v-if="stateStore?.isSubToolbarShown()">
+        <Teleport
+            to="#st-actions"
+            v-if="stateStore?.isSubToolbarShown() && componentIsRendered"
+        >
             <QBtn color="primary" icon-right="download" no-caps @click="downloadCSV()">
                 <QTooltip>{{ t('Download as CSV') }}</QTooltip>
             </QBtn>
@@ -178,6 +189,21 @@ const downloadCSV = async () => {
                     <WorkerDescriptorProxy :id="row.comercialId" />
                 </QTd>
             </template>
+            <template #body-cell-active="{ row }">
+                <QTd>
+                    <QCheckbox :model-value="!!row.isActive" disable />
+                </QTd>
+            </template>
+            <template #body-cell-hasToInvoice="{ row }">
+                <QTd>
+                    <QCheckbox :model-value="!!row.hasToInvoice" disable />
+                </QTd>
+            </template>
+            <template #body-cell-verifiedData="{ row }">
+                <QTd>
+                    <QCheckbox :model-value="!!row.isTaxDataChecked" disable />
+                </QTd>
+            </template>
         </QTable>
     </QPage>
 </template>
diff --git a/src/router/modules/invoiceOut.js b/src/router/modules/invoiceOut.js
index 084cf6ac3..b9648e96a 100644
--- a/src/router/modules/invoiceOut.js
+++ b/src/router/modules/invoiceOut.js
@@ -43,7 +43,7 @@ export default {
                     name: 'InvoiceOutNegativeBases',
                     meta: {
                         title: 'negativeBases',
-                        icon: 'view_list',
+                        icon: 'vn:ticket',
                     },
                     component: () =>
                         import('src/pages/InvoiceOut/InvoiceOutNegativeBases.vue'),
diff --git a/src/stores/invoiceOutGlobal.js b/src/stores/invoiceOutGlobal.js
index 9929a0127..b1bcc0e7d 100644
--- a/src/stores/invoiceOutGlobal.js
+++ b/src/stores/invoiceOutGlobal.js
@@ -107,7 +107,6 @@ export const useInvoiceOutGlobalStore = defineStore({
                 if (clientsToInvoice == 'all') params.clientId = undefined;
 
                 const { data } = await axios.post('InvoiceOuts/clientsToInvoice', params);
-
                 this.addresses = data;
 
                 if (!this.addresses || !this.addresses.length > 0) {
@@ -188,10 +187,8 @@ export const useInvoiceOutGlobalStore = defineStore({
 
                 const { data } = await axios.post('InvoiceOuts/invoiceClient', params);
 
-                if (data) {
-                    await this.makePdfAndNotify(data, address);
-                }
-
+                if (data) await this.makePdfAndNotify(data, address);
+                this.addressIndex++;
                 this.isInvoicing = false;
             } catch (err) {
                 if (
@@ -201,7 +198,6 @@ export const useInvoiceOutGlobalStore = defineStore({
                     err.response.status < 500
                 ) {
                     this.invoiceClientError(address, err.response?.data?.error?.message);
-                    this.addressIndex++;
                     return;
                 } else {
                     this.invoicing = false;

From 0e79676f588a444b1e87a7e1e590da954267ab24 Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Fri, 8 Mar 2024 09:02:49 -0300
Subject: [PATCH 20/42] Create manual invoice WIP

---
 src/components/CreateManualInvoiceForm.vue | 155 +++++++++++++++++++++
 src/components/FormModelPopup.vue          |   6 +-
 src/pages/InvoiceOut/InvoiceOutList.vue    |  26 +++-
 3 files changed, 183 insertions(+), 4 deletions(-)
 create mode 100644 src/components/CreateManualInvoiceForm.vue

diff --git a/src/components/CreateManualInvoiceForm.vue b/src/components/CreateManualInvoiceForm.vue
new file mode 100644
index 000000000..de5decaad
--- /dev/null
+++ b/src/components/CreateManualInvoiceForm.vue
@@ -0,0 +1,155 @@
+<script setup>
+import { reactive, ref } from 'vue';
+import { useI18n } from 'vue-i18n';
+import { useRouter } from 'vue-router';
+
+import FetchData from 'components/FetchData.vue';
+import VnRow from 'components/ui/VnRow.vue';
+import VnSelectFilter from 'src/components/common/VnSelectFilter.vue';
+import VnInput from 'src/components/common/VnInput.vue';
+import FormModelPopup from './FormModelPopup.vue';
+import VnInputDate from './common/VnInputDate.vue';
+
+const emit = defineEmits(['onDataSaved']);
+
+const { t } = useI18n();
+const router = useRouter();
+
+const manualInvoiceFormData = reactive({
+    maxShipped: Date.vnNew(),
+});
+
+const isInvoicing = ref(false);
+const invoiceOutSerialsOptions = ref([]);
+const taxAreasOptions = ref([]);
+const ticketsOptions = ref([]);
+const clientsOptions = ref([]);
+
+const onDataSaved = (formData, requestResponse) => {
+    emit('onDataSaved', formData, requestResponse);
+    router.push({ name: 'InvoiceOutSummary', params: { id: requestResponse.id } });
+};
+</script>
+
+<template>
+    <FetchData
+        url="InvoiceOutSerials"
+        :filter="{ where: { code: { neq: 'R' } }, order: ['code'] }"
+        @on-fetch="(data) => (invoiceOutSerialsOptions = data)"
+        auto-load
+    />
+    <FetchData
+        url="TaxAreas"
+        :filter="{ order: ['code'] }"
+        @on-fetch="(data) => (taxAreasOptions = data)"
+        auto-load
+    />
+    <FetchData
+        url="Tickets"
+        :filter="{ fields: ['id', 'nickname'], order: 'shipped DESC', limit: 30 }"
+        @on-fetch="(data) => (ticketsOptions = data)"
+        auto-load
+    />
+    <FetchData
+        url="Clients"
+        :filter="{ fields: ['id', 'name'], order: 'name ASC', limit: 30 }"
+        @on-fetch="(data) => (clientsOptions = data)"
+        auto-load
+    />
+    <FormModelPopup
+        :title="t('Create manual invoice')"
+        url-create="InvoiceOuts/createManualInvoice"
+        model="invoiceOut"
+        :form-initial-data="manualInvoiceFormData"
+        @on-data-saved="onDataSaved"
+    >
+        <template #form-inputs="{ data }">
+            <VnRow class="row q-gutter-md q-mb-md">
+                <div class="col">
+                    <VnSelectFilter
+                        :label="t('Ticket')"
+                        :options="ticketsOptions"
+                        hide-selected
+                        option-label="id"
+                        option-value="id"
+                        v-model="data.ticketFk"
+                        @update:model-value="data.clientFk = null"
+                    >
+                        <template #option="scope">
+                            <QItem v-bind="scope.itemProps">
+                                <QItemSection>
+                                    <QItemLabel> #{{ scope.opt?.id }} </QItemLabel>
+                                    <QItemLabel caption>{{
+                                        scope.opt?.nickname
+                                    }}</QItemLabel>
+                                </QItemSection>
+                            </QItem>
+                        </template>
+                    </VnSelectFilter>
+                </div>
+                <span class="row items-center" style="max-width: max-content">{{
+                    t('Or')
+                }}</span>
+                <div class="col">
+                    <VnSelectFilter
+                        :label="t('Client')"
+                        :options="clientsOptions"
+                        hide-selected
+                        option-label="name"
+                        option-value="id"
+                        v-model="data.clientFk"
+                        @update:model-value="data.ticketFk = null"
+                    />
+                </div>
+                <div class="col">
+                    <VnInputDate :label="t('Max date')" v-model="data.maxShipped" />
+                </div>
+            </VnRow>
+            <VnRow class="row q-gutter-md q-mb-md">
+                <div class="col">
+                    <VnSelectFilter
+                        :label="t('Serial')"
+                        :options="invoiceOutSerialsOptions"
+                        hide-selected
+                        option-label="description"
+                        option-value="code"
+                        v-model="data.serial"
+                        :required="true"
+                    />
+                </div>
+                <div class="col">
+                    <VnSelectFilter
+                        :label="t('Area')"
+                        :options="taxAreasOptions"
+                        hide-selected
+                        option-label="code"
+                        option-value="code"
+                        v-model="data.taxArea"
+                        :required="true"
+                    />
+                </div>
+            </VnRow>
+            <VnRow class="row q-gutter-md q-mb-md">
+                <VnInput
+                    :label="t('Reference')"
+                    type="textarea"
+                    v-model="data.reference"
+                    fill-input
+                    autogrow
+                />
+            </VnRow>
+        </template>
+    </FormModelPopup>
+</template>
+
+<i18n>
+es:
+    Create manual invoice: Crear factura manual
+    Ticket: Ticket
+    Client: Cliente
+    Max date: Fecha límite
+    Serial: Serie
+    Area: Area
+    Reference: Referencia
+    Or: O
+</i18n>
diff --git a/src/components/FormModelPopup.vue b/src/components/FormModelPopup.vue
index 04322a3c8..cc22c77db 100644
--- a/src/components/FormModelPopup.vue
+++ b/src/components/FormModelPopup.vue
@@ -42,8 +42,8 @@ const { t } = useI18n();
 const closeButton = ref(null);
 const isLoading = ref(false);
 
-const onDataSaved = (dataSaved) => {
-    emit('onDataSaved', dataSaved);
+const onDataSaved = (formData, requestResponse) => {
+    emit('onDataSaved', formData, requestResponse);
     closeForm();
 };
 
@@ -59,7 +59,7 @@ const closeForm = () => {
         :default-actions="false"
         :url-create="urlCreate"
         :model="model"
-        @on-data-saved="onDataSaved($event)"
+        @on-data-saved="onDataSaved"
     >
         <template #form="{ data, validate }">
             <span ref="closeButton" class="close-icon" v-close-popup>
diff --git a/src/pages/InvoiceOut/InvoiceOutList.vue b/src/pages/InvoiceOut/InvoiceOutList.vue
index ca68f0770..17b37b962 100644
--- a/src/pages/InvoiceOut/InvoiceOutList.vue
+++ b/src/pages/InvoiceOut/InvoiceOutList.vue
@@ -10,6 +10,7 @@ import VnLv from 'src/components/ui/VnLv.vue';
 import CardList from 'src/components/ui/CardList.vue';
 import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue';
 import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue';
+import CreateManualInvoiceForm from 'src/components/CreateManualInvoiceForm.vue';
 
 import InvoiceOutFilter from './InvoiceOutFilter.vue';
 import { toDate, toCurrency } from 'src/filters/index';
@@ -18,13 +19,15 @@ import { useSummaryDialog } from 'src/composables/useSummaryDialog';
 import { useSession } from 'src/composables/useSession';
 
 const { t } = useI18n();
-const selectedCards = ref(new Map());
 const router = useRouter();
 const stateStore = useStateStore();
 const session = useSession();
 const token = session.getToken();
 const { viewSummary } = useSummaryDialog();
 
+const manualInvoiceDialogRef = ref(null);
+const selectedCards = ref(new Map());
+
 onMounted(() => (stateStore.rightDrawer = true));
 onUnmounted(() => (stateStore.rightDrawer = false));
 
@@ -83,6 +86,10 @@ const openPdf = () => {
         console.error('Error opening PDF');
     }
 };
+
+const openCreateInvoiceModal = () => {
+    manualInvoiceDialogRef.value.show();
+};
 </script>
 
 <template>
@@ -204,6 +211,20 @@ const openPdf = () => {
                 </div>
             </template>
         </VnPaginate>
+        <QPageSticky :offset="[20, 20]">
+            <QBtn fab icon="add" color="primary" @click="openCreateInvoiceModal()" />
+            <QTooltip>
+                {{ t('createInvoice') }}
+            </QTooltip>
+        </QPageSticky>
+
+        <QDialog
+            ref="manualInvoiceDialogRef"
+            transition-show="scale"
+            transition-hide="scale"
+        >
+            <CreateManualInvoiceForm />
+        </QDialog>
     </QPage>
 </template>
 
@@ -214,10 +235,13 @@ en:
     fileAllowed: Successful download of CSV file
     youCanSearchByInvoiceReference: You can search by invoice reference
     downloadPdf: Download PDF
+    createInvoice: Make invoice
 es:
     searchInvoice: Buscar factura emitida
     fileDenied: El navegador denegó la descarga de archivos...
     fileAllowed: Descarga exitosa de archivo CSV
     youCanSearchByInvoiceReference: Puedes buscar por referencia de la factura
     downloadPdf: Download PDF
+    createInvoice: Crear factura
+
 </i18n>

From ed99aea4a3cbecc4d23f576851c9fb97593be769 Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Fri, 8 Mar 2024 15:05:11 -0300
Subject: [PATCH 21/42] Finish create manual invoice popup form

---
 src/components/CreateManualInvoiceForm.vue | 27 ++++++++++++++++++----
 src/components/FormModel.vue               |  9 ++++----
 src/components/FormModelPopup.vue          | 13 ++++++++---
 src/css/app.scss                           |  4 ++++
 4 files changed, 42 insertions(+), 11 deletions(-)

diff --git a/src/components/CreateManualInvoiceForm.vue b/src/components/CreateManualInvoiceForm.vue
index de5decaad..f03afbf35 100644
--- a/src/components/CreateManualInvoiceForm.vue
+++ b/src/components/CreateManualInvoiceForm.vue
@@ -1,5 +1,5 @@
 <script setup>
-import { reactive, ref } from 'vue';
+import { reactive, ref, computed } from 'vue';
 import { useI18n } from 'vue-i18n';
 import { useRouter } from 'vue-router';
 
@@ -19,15 +19,17 @@ const manualInvoiceFormData = reactive({
     maxShipped: Date.vnNew(),
 });
 
-const isInvoicing = ref(false);
+const formModelPopupRef = ref();
 const invoiceOutSerialsOptions = ref([]);
 const taxAreasOptions = ref([]);
 const ticketsOptions = ref([]);
 const clientsOptions = ref([]);
+const isLoading = computed(() => formModelPopupRef.value?.isLoading);
 
-const onDataSaved = (formData, requestResponse) => {
+const onDataSaved = async (formData, requestResponse) => {
     emit('onDataSaved', formData, requestResponse);
-    router.push({ name: 'InvoiceOutSummary', params: { id: requestResponse.id } });
+    if (requestResponse && requestResponse.id)
+        router.push({ name: 'InvoiceOutSummary', params: { id: requestResponse.id } });
 };
 </script>
 
@@ -57,6 +59,7 @@ const onDataSaved = (formData, requestResponse) => {
         auto-load
     />
     <FormModelPopup
+        ref="formModelPopupRef"
         :title="t('Create manual invoice')"
         url-create="InvoiceOuts/createManualInvoice"
         model="invoiceOut"
@@ -64,6 +67,10 @@ const onDataSaved = (formData, requestResponse) => {
         @on-data-saved="onDataSaved"
     >
         <template #form-inputs="{ data }">
+            <span v-if="isLoading" class="text-primary invoicing-text">
+                <QIcon name="warning" class="fill-icon q-mr-sm" size="md" />
+                {{ t('Invoicing in progress...') }}
+            </span>
             <VnRow class="row q-gutter-md q-mb-md">
                 <div class="col">
                     <VnSelectFilter
@@ -142,6 +149,17 @@ const onDataSaved = (formData, requestResponse) => {
     </FormModelPopup>
 </template>
 
+<style lang="scss" scoped>
+.invoicing-text {
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    color: $primary;
+    font-size: 24px;
+    margin-bottom: 8px;
+}
+</style>
+
 <i18n>
 es:
     Create manual invoice: Crear factura manual
@@ -152,4 +170,5 @@ es:
     Area: Area
     Reference: Referencia
     Or: O
+    Invoicing in progress...: Facturación en progreso...
 </i18n>
diff --git a/src/components/FormModel.vue b/src/components/FormModel.vue
index 9f5ae319c..ca7bd30e5 100644
--- a/src/components/FormModel.vue
+++ b/src/components/FormModel.vue
@@ -69,10 +69,6 @@ const $props = defineProps({
 
 const emit = defineEmits(['onFetch', 'onDataSaved']);
 
-defineExpose({
-    save,
-});
-
 const componentIsRendered = ref(false);
 
 onMounted(async () => {
@@ -205,6 +201,11 @@ watch(formUrl, async () => {
     reset();
     fetch();
 });
+
+defineExpose({
+    save,
+    isLoading,
+});
 </script>
 <template>
     <div class="column items-center full-width">
diff --git a/src/components/FormModelPopup.vue b/src/components/FormModelPopup.vue
index cc22c77db..2d8886610 100644
--- a/src/components/FormModelPopup.vue
+++ b/src/components/FormModelPopup.vue
@@ -1,5 +1,5 @@
 <script setup>
-import { ref } from 'vue';
+import { ref, computed } from 'vue';
 import { useI18n } from 'vue-i18n';
 
 import FormModel from 'components/FormModel.vue';
@@ -39,21 +39,28 @@ const $props = defineProps({
 
 const { t } = useI18n();
 
+const formModelRef = ref(null);
 const closeButton = ref(null);
-const isLoading = ref(false);
 
 const onDataSaved = (formData, requestResponse) => {
     emit('onDataSaved', formData, requestResponse);
     closeForm();
 };
 
-const closeForm = () => {
+const isLoading = computed(() => formModelRef.value?.isLoading);
+
+const closeForm = async () => {
     if (closeButton.value) closeButton.value.click();
 };
+
+defineExpose({
+    isLoading,
+});
 </script>
 
 <template>
     <FormModel
+        ref="formModelRef"
         :form-initial-data="formInitialData"
         :observe-form-changes="false"
         :default-actions="false"
diff --git a/src/css/app.scss b/src/css/app.scss
index 38dd642a3..388492432 100644
--- a/src/css/app.scss
+++ b/src/css/app.scss
@@ -97,6 +97,10 @@ select:-webkit-autofill {
     background-color: var(--vn-light-gray);
 }
 
+.fill-icon {
+    font-variation-settings: 'FILL' 1;
+}
+
 /* Estilo para el asterisco en campos requeridos */
 .q-field.required .q-field__label:after {
     content: ' *';

From 049e3c722a897e48bb8dbc118b14eaaf3e0962f7 Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Fri, 8 Mar 2024 15:09:28 -0300
Subject: [PATCH 22/42] Fix translation

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

diff --git a/src/pages/InvoiceOut/InvoiceOutList.vue b/src/pages/InvoiceOut/InvoiceOutList.vue
index 17b37b962..f193a6e8c 100644
--- a/src/pages/InvoiceOut/InvoiceOutList.vue
+++ b/src/pages/InvoiceOut/InvoiceOutList.vue
@@ -241,7 +241,6 @@ es:
     fileDenied: El navegador denegó la descarga de archivos...
     fileAllowed: Descarga exitosa de archivo CSV
     youCanSearchByInvoiceReference: Puedes buscar por referencia de la factura
-    downloadPdf: Download PDF
+    downloadPdf: Descargar PDF
     createInvoice: Crear factura
-
 </i18n>

From 0c3dc30b8c329273fdcc79969cea991ec9c22e06 Mon Sep 17 00:00:00 2001
From: pablone <pablone@verdnatura.es>
Date: Tue, 12 Mar 2024 09:49:58 +0100
Subject: [PATCH 23/42] fix(styles): refs #7001 create VnTitle and correct
 background-page color

---
 src/components/common/VnTitle.vue     | 25 +++++++++++
 src/css/app.scss                      |  2 +-
 src/pages/Claim/Card/ClaimSummary.vue | 10 +++--
 src/pages/Entry/Card/EntrySummary.vue | 63 +--------------------------
 4 files changed, 34 insertions(+), 66 deletions(-)
 create mode 100644 src/components/common/VnTitle.vue

diff --git a/src/components/common/VnTitle.vue b/src/components/common/VnTitle.vue
new file mode 100644
index 000000000..1d53e47a1
--- /dev/null
+++ b/src/components/common/VnTitle.vue
@@ -0,0 +1,25 @@
+<script setup>
+const $props = defineProps({
+    url: { type: String, default: null },
+    text: { type: String, default: null },
+    icon: { type: String, default: null },
+});
+</script>
+<template>
+    <div class="titleBox">
+        <div class="header-link">
+            <a :href="$props.url" class="link">
+                {{ text }}
+                <QIcon :name="$props.icon" />
+            </a>
+        </div>
+    </div>
+</template>
+<style scoped>
+a {
+    font-size: large;
+}
+.titleBox {
+    padding-bottom: 2%;
+}
+</style>
diff --git a/src/css/app.scss b/src/css/app.scss
index 38dd642a3..459a7f2dc 100644
--- a/src/css/app.scss
+++ b/src/css/app.scss
@@ -18,7 +18,7 @@ body.body--light {
 
 body.body--dark {
     --vn-pageColor: #222;
-    --vn-SectionColor: #3c3b3b;
+    --vn-SectionColor: #403c3c;
     background-color: var(--vn-pageColor);
     --vn-text: white;
     --vn-gray: var(--vn-SectionColor);
diff --git a/src/pages/Claim/Card/ClaimSummary.vue b/src/pages/Claim/Card/ClaimSummary.vue
index bf6b57973..2d8592b6d 100644
--- a/src/pages/Claim/Card/ClaimSummary.vue
+++ b/src/pages/Claim/Card/ClaimSummary.vue
@@ -11,6 +11,7 @@ import VnLv from 'src/components/ui/VnLv.vue';
 import ClaimNotes from 'src/pages/Claim/Card/ClaimNotes.vue';
 import VnUserLink from 'src/components/ui/VnUserLink.vue';
 import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
+import VnTitle from 'src/components/common/VnTitle.vue';
 
 const route = useRoute();
 const { t } = useI18n();
@@ -179,10 +180,11 @@ function openDialog(dmsId) {
         </template>
         <template #body="{ entity: { claim, salesClaimed, developments } }">
             <QCard class="vn-one">
-                <a class="header header-link" :href="`#/claim/${entityId}/basic-data`">
-                    {{ t('claim.pageTitles.basicData') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle
+                    :url="`#/claim/${entityId}/basic-data`"
+                    :text="t('claim.pageTitles.basicData')"
+                    icon="open_in_new"
+                />
                 <VnLv
                     :label="t('claim.summary.created')"
                     :value="toDate(claim.created)"
diff --git a/src/pages/Entry/Card/EntrySummary.vue b/src/pages/Entry/Card/EntrySummary.vue
index b6863f8f7..be30468d5 100644
--- a/src/pages/Entry/Card/EntrySummary.vue
+++ b/src/pages/Entry/Card/EntrySummary.vue
@@ -175,8 +175,8 @@ const fetchEntryBuys = async () => {
 
         <template #body>
             <QCard class="vn-one">
-                <a class="header header-link" :href="`#/entry/${entityId}/basic-data`">
-                    {{ t('globals.summary.basicData') }}
+                <a class="header link" :href="`#/entry/${entityId}/basic-data`">
+                    textos
                     <QIcon name="open_in_new" />
                 </a>
 
@@ -274,65 +274,6 @@ const fetchEntryBuys = async () => {
                     {{ t('entry.summary.buys') }}
                     <QIcon name="open_in_new" />
                 </a>
-                <QTable
-                    :rows="entryBuys"
-                    :columns="entriesTableColumns"
-                    hide-bottom
-                    row-key="index"
-                    class="full-width q-mt-md"
-                >
-                    <template #body="{ cols, row, rowIndex }">
-                        <QTr no-hover>
-                            <QTd v-for="col in cols" :key="col.name">
-                                <component
-                                    :is="tableColumnComponents[col.name].component(props)"
-                                    v-bind="tableColumnComponents[col.name].props(props)"
-                                    @click="tableColumnComponents[col.name].event(props)"
-                                    class="col-content"
-                                >
-                                    <template
-                                        v-if="
-                                            col.name !== 'observation' &&
-                                            col.name !== 'isConfirmed'
-                                        "
-                                        >{{ col.value }}</template
-                                    >
-                                    <QTooltip v-if="col.toolTip">{{
-                                        col.toolTip
-                                    }}</QTooltip>
-                                </component>
-                            </QTd>
-                        </QTr>
-                        <QTr no-hover>
-                            <QTd>
-                                <span>{{ row.item.itemType.code }}</span>
-                            </QTd>
-                            <QTd>
-                                <span>{{ row.item.id }}</span>
-                            </QTd>
-                            <QTd>
-                                <span>{{ row.item.size }}</span>
-                            </QTd>
-                            <QTd>
-                                <span>{{ toCurrency(row.item.minPrice) }}</span>
-                            </QTd>
-                            <QTd colspan="6">
-                                <span>{{ row.item.concept }}</span>
-                                <span v-if="row.item.subName" class="subName">
-                                    {{ row.item.subName }}
-                                </span>
-                                <fetched-tags :item="row.item" :max-length="5" />
-                            </QTd>
-                        </QTr>
-                        <!-- Esta última row es utilizada para agregar un espaciado y así marcar una diferencia visual entre los diferentes buys -->
-                        <QTr
-                            v-if="rowIndex !== entryBuys.length - 1"
-                            class="separation-row"
-                        >
-                            <QTd colspan="10" style="height: 24px" />
-                        </QTr>
-                    </template>
-                </QTable>
             </QCard>
         </template>
     </CardSummary>

From fff5e1849eaea53bc72a174b3eb6f4c618f7a7c3 Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Tue, 12 Mar 2024 09:59:27 -0300
Subject: [PATCH 24/42] Delete unused form

---
 src/pages/InvoiceOut/Card/SendInvoiceForm.vue | 41 -------------------
 1 file changed, 41 deletions(-)
 delete mode 100644 src/pages/InvoiceOut/Card/SendInvoiceForm.vue

diff --git a/src/pages/InvoiceOut/Card/SendInvoiceForm.vue b/src/pages/InvoiceOut/Card/SendInvoiceForm.vue
deleted file mode 100644
index 3956dab08..000000000
--- a/src/pages/InvoiceOut/Card/SendInvoiceForm.vue
+++ /dev/null
@@ -1,41 +0,0 @@
-<script setup>
-import { ref } from 'vue';
-import { useI18n } from 'vue-i18n';
-
-import VnRow from 'components/ui/VnRow.vue';
-import VnInput from 'src/components/common/VnInput.vue';
-import FormPopup from 'src/components/FormPopup.vue';
-
-const emit = defineEmits(['onDataSaved']);
-
-const { t } = useI18n();
-
-const email = ref(null);
-
-const onDataSaved = (dataSaved) => {
-    emit('onDataSaved', dataSaved);
-};
-</script>
-
-<template>
-    <FormPopup
-        :title="t('New thermograph')"
-        :subtitle="t('Are you sure you want to send it?')"
-        @on-data-saved="onDataSaved($event)"
-    >
-        <template #form-inputs>
-            <VnRow class="row q-gutter-md q-mb-md">
-                <div class="col">
-                    <VnInput :label="t('Email')" v-model="email" type="email" />
-                </div>
-            </VnRow>
-        </template>
-    </FormPopup>
-</template>
-
-<i18n>
-es:
-    Email: Email
-    Confirm: Confirmar
-    Are you sure you want to send it?: ¿Seguro que quieres enviarlo?
-</i18n>

From 06fa4aebc5b095e3b7f78736bf9b73350a5dc567 Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Tue, 12 Mar 2024 11:40:06 -0300
Subject: [PATCH 25/42] add invoice out list download icon

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

diff --git a/src/pages/InvoiceOut/InvoiceOutList.vue b/src/pages/InvoiceOut/InvoiceOutList.vue
index f193a6e8c..cf33b2cec 100644
--- a/src/pages/InvoiceOut/InvoiceOutList.vue
+++ b/src/pages/InvoiceOut/InvoiceOutList.vue
@@ -134,10 +134,10 @@ const openCreateInvoiceModal = () => {
                     <template #st-actions>
                         <QBtn
                             @click="openPdf()"
-                            class="q-mr-xl"
+                            class="q-mr-md"
                             color="primary"
+                            icon="cloud_download"
                             :disable="selectedCards.size === 0"
-                            :label="t('globals.download')"
                         >
                             <QTooltip>{{ t('downloadPdf') }}</QTooltip>
                         </QBtn>

From 09e18dfc017e2210f18e994e5cea31b3bd88bbb7 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Thu, 14 Mar 2024 09:07:34 +0100
Subject: [PATCH 26/42] refs #7033 deploy(2412): dev to test

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

diff --git a/test/cypress/integration/worker/workerList.spec.js b/test/cypress/integration/worker/workerList.spec.js
index b5c57f920..bc4b2383e 100644
--- a/test/cypress/integration/worker/workerList.spec.js
+++ b/test/cypress/integration/worker/workerList.spec.js
@@ -16,7 +16,7 @@ describe('WorkerList', () => {
     it('should open the worker summary', () => {
         cy.openListSummary(0);
         cy.get('.summaryHeader div').should('have.text', '1110 - Jessica Jones');
-        cy.get('.summary .header').eq(0).invoke('text').should('include', 'Basic data');
-        cy.get('.summary .header').eq(1).should('have.text', 'User data');
+        cy.get('.summary .header-link').eq(0).invoke('text').should('include', 'Basic data');
+        cy.get('.summary .header-link').eq(1).should('have.text', 'User data');
     });
 });

From 78e3cff397201b110203925299b669f34ad146ae Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Thu, 14 Mar 2024 09:08:27 +0100
Subject: [PATCH 27/42] refs #7033 deploy: init version 2414

---
 CHANGELOG.md | 8 ++++++++
 package.json | 2 +-
 2 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1a679cdfc..dbf6bdcc3 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,6 +5,14 @@ All notable changes to this project will be documented in this file.
 The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
 and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
 
+## [2414.01] - 2024-04-04
+
+### Added
+
+### Changed
+
+### Fixed
+
 ## [2400.01] - 2024-01-04
 
 ### Added
diff --git a/package.json b/package.json
index beae0b7b9..a35020b66 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
     "name": "salix-front",
-    "version": "24.12.0",
+    "version": "24.14.0",
     "description": "Salix frontend",
     "productName": "Salix",
     "author": "Verdnatura",

From f1d21915eb55bc76b3bc21f821418a111f0f22e8 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Thu, 14 Mar 2024 09:28:28 +0000
Subject: [PATCH 28/42] fix vnlocation.spec

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

diff --git a/test/cypress/integration/VnLocation.spec.js b/test/cypress/integration/VnLocation.spec.js
index 02b924e4d..5892e3ad5 100644
--- a/test/cypress/integration/VnLocation.spec.js
+++ b/test/cypress/integration/VnLocation.spec.js
@@ -40,9 +40,9 @@ describe('VnLocation', () => {
             cy.waitForElement('.q-card');
         });
 
-        it('Show all options', function() {
+        it('Show locations options', function() {
             cy.get(inputLocation).click();
-            cy.get(locationOptions).should('have.length', 1);
+            cy.get(locationOptions).should('have.length', 5);
         });
     });
 })

From 00a47cd5b254ea73750a58176b7d275bf1e1b235 Mon Sep 17 00:00:00 2001
From: pablone <pablone@verdnatura.es>
Date: Thu, 14 Mar 2024 15:06:06 +0100
Subject: [PATCH 29/42] refactor(style): refs #7001 add style font vnTitle
 vnChip

---
 src/components/NavBar.vue                     |  2 +-
 src/components/common/VnBreadcrumbs.vue       |  2 +-
 src/components/common/VnChip.vue              | 13 +++
 src/components/common/VnDmsList.vue           |  2 +-
 src/components/common/VnLog.vue               | 21 ++---
 src/components/common/VnSelectDialog.vue      |  2 +-
 src/components/common/VnSmsDialog.vue         |  5 +-
 src/components/common/VnTitle.vue             | 14 ++--
 src/components/ui/CardDescriptor.vue          | 10 +--
 src/components/ui/CardList.vue                | 16 ++--
 src/components/ui/CardSummary.vue             | 12 +--
 src/components/ui/VnSearchbar.vue             |  2 +-
 src/components/ui/VnSms.vue                   |  8 +-
 src/css/app.scss                              | 51 ++++++------
 src/css/quasar.variables.scss                 |  8 +-
 src/pages/Claim/Card/ClaimDescriptor.vue      |  6 +-
 src/pages/Claim/Card/ClaimLines.vue           | 13 +--
 src/pages/Claim/Card/ClaimSummary.vue         | 48 ++++++-----
 src/pages/Claim/ClaimList.vue                 |  6 +-
 .../Customer/Card/CustomerConsignees.vue      |  4 +-
 src/pages/Customer/Card/CustomerGreuges.vue   |  4 +-
 src/pages/Customer/Card/CustomerNotes.vue     |  4 +-
 .../Customer/Card/CustomerRecoveries.vue      |  4 +-
 src/pages/Customer/Card/CustomerSummary.vue   | 81 +++++++------------
 .../Defaulter/CustomerBalanceDueTotal.vue     |  6 +-
 .../Customer/Payments/CustomerPayments.vue    |  3 +-
 .../Department/Card/DepartmentSummary.vue     | 12 ++-
 src/pages/Entry/Card/EntryBuys.vue            |  2 +-
 src/pages/Entry/Card/EntrySummary.vue         | 25 +++---
 src/pages/Entry/EntryLatestBuysFilter.vue     |  2 +-
 src/pages/InvoiceIn/Card/InvoiceInSummary.vue | 63 +++++++--------
 .../InvoiceOut/Card/InvoiceOutSummary.vue     | 16 +---
 src/pages/InvoiceOut/InvoiceOutGlobal.vue     |  4 +-
 src/pages/Login/VerifyEmail.vue               |  7 +-
 src/pages/Order/Card/OrderCatalogFilter.vue   |  2 +-
 src/pages/Order/Card/OrderCatalogItem.vue     |  6 +-
 src/pages/Order/Card/OrderSummary.vue         |  2 +-
 src/pages/Order/OrderCatalog.vue              |  2 +-
 src/pages/Order/OrderLines.vue                | 16 ++--
 src/pages/Order/OrderList.vue                 |  2 +-
 src/pages/Order/OrderVolume.vue               |  4 +-
 src/pages/Route/Cmr/CmrList.vue               |  1 +
 .../Supplier/Card/SupplierConsumption.vue     |  2 +-
 src/pages/Supplier/Card/SupplierContacts.vue  |  2 +-
 src/pages/Supplier/Card/SupplierSummary.vue   |  3 +-
 src/pages/Ticket/Card/TicketDescriptor.vue    |  5 +-
 src/pages/Ticket/Card/TicketSummary.vue       | 62 +++++++-------
 src/pages/Ticket/TicketList.vue               |  1 +
 src/pages/Travel/Card/TravelSummary.vue       |  5 +-
 src/pages/Travel/TravelList.vue               |  2 +
 src/pages/Worker/Card/WorkerSummary.vue       | 13 ++-
 51 files changed, 304 insertions(+), 304 deletions(-)
 create mode 100644 src/components/common/VnChip.vue

diff --git a/src/components/NavBar.vue b/src/components/NavBar.vue
index 12366e174..c51ed3024 100644
--- a/src/components/NavBar.vue
+++ b/src/components/NavBar.vue
@@ -106,7 +106,7 @@ const pinnedModulesRef = ref();
     width: max-content;
 }
 .q-header {
-    background-color: var(--vn-dark);
+    background-color: var(--vn-section-color);
 }
 </style>
 <i18n>
diff --git a/src/components/common/VnBreadcrumbs.vue b/src/components/common/VnBreadcrumbs.vue
index 666f14c72..09bc198f7 100644
--- a/src/components/common/VnBreadcrumbs.vue
+++ b/src/components/common/VnBreadcrumbs.vue
@@ -71,7 +71,7 @@ function getBreadcrumb(param) {
     }
     &--last,
     &__separator {
-        color: var(--vn-label);
+        color: var(--vn-label-color);
     }
 }
 @media (max-width: $breakpoint-md) {
diff --git a/src/components/common/VnChip.vue b/src/components/common/VnChip.vue
new file mode 100644
index 000000000..dd2de2c64
--- /dev/null
+++ b/src/components/common/VnChip.vue
@@ -0,0 +1,13 @@
+<script setup>
+const $props = defineProps({
+    color: { type: String, default: null },
+    text: { type: String, default: null },
+    dense: { type: String, default: null },
+    textColor: { type: String, default: 'black' },
+});
+</script>
+<template>
+    <QChip :color="$props.color" :text-color="textColor" :dense="$props.dense">{{
+        text
+    }}</QChip>
+</template>
diff --git a/src/components/common/VnDmsList.vue b/src/components/common/VnDmsList.vue
index 5057c0790..07940274e 100644
--- a/src/components/common/VnDmsList.vue
+++ b/src/components/common/VnDmsList.vue
@@ -304,7 +304,7 @@ function parseDms(data) {
     row-gap: 20px;
 }
 .labelColor {
-    color: var(--vn-label);
+    color: var(--vn-label-color);
 }
 </style>
 <i18n>
diff --git a/src/components/common/VnLog.vue b/src/components/common/VnLog.vue
index eae391cc4..732f0e964 100644
--- a/src/components/common/VnLog.vue
+++ b/src/components/common/VnLog.vue
@@ -14,6 +14,7 @@ import VnJsonValue from '../common/VnJsonValue.vue';
 import FetchData from '../FetchData.vue';
 import VnSelectFilter from './VnSelectFilter.vue';
 import VnUserLink from '../ui/VnUserLink.vue';
+import VnChip from '../common/VnChip.vue';
 
 const stateStore = useStateStore();
 const validationsStore = useValidator();
@@ -443,10 +444,10 @@ setLogTree();
                 >
                     <QItemSection>
                         <QItemLabel class="model-info q-mb-xs" v-if="!byRecord">
-                            <QChip
-                                dense
+                            <VnChip
                                 size="md"
-                                class="model-name q-mr-xs text-white"
+                                text-color="white"
+                                class="model-name q-mr-xs"
                                 v-if="
                                     !(modelLog.changedModel && modelLog.changedModelId) &&
                                     modelLog.model
@@ -455,9 +456,9 @@ setLogTree();
                                     backgroundColor: useColor(modelLog.model),
                                 }"
                                 :title="modelLog.model"
-                            >
-                                {{ t(modelLog.modelI18n) }}
-                            </QChip>
+                                :text="t(modelLog.modelI18n)"
+                                dense="dense"
+                            />
                             <span class="model-id" v-if="modelLog.id"
                                 >#{{ modelLog.id }}</span
                             >
@@ -819,7 +820,7 @@ setLogTree();
 </template>
 <style lang="scss" scoped>
 .q-card {
-    background-color: var(--vn-gray);
+    background-color: var(--vn-section-color);
 }
 .q-item {
     min-height: 0px;
@@ -916,7 +917,7 @@ setLogTree();
             font-style: italic;
         }
         .model-id {
-            color: var(--vn-label);
+            color: var(--vn-label-color);
             font-size: 0.9rem;
         }
         .q-btn {
@@ -942,7 +943,7 @@ setLogTree();
     }
     .change-info {
         overflow: hidden;
-        background-color: var(--vn-dark);
+        background-color: var(--vn-section-color);
         & > .date {
             overflow: hidden;
             white-space: nowrap;
@@ -981,7 +982,7 @@ setLogTree();
         position: relative;
         overflow: hidden;
         text-overflow: ellipsis;
-        background-color: var(--vn-gray);
+        background-color: var(--vn-section-color);
         white-space: nowrap;
         box-sizing: border-box;
         & > .q-icon {
diff --git a/src/components/common/VnSelectDialog.vue b/src/components/common/VnSelectDialog.vue
index 355a25109..770173728 100644
--- a/src/components/common/VnSelectDialog.vue
+++ b/src/components/common/VnSelectDialog.vue
@@ -79,7 +79,7 @@ const toggleForm = () => {
     border-radius: 50px;
 
     &.--add-icon {
-        color: var(--vn-text);
+        color: var(--vn-text-color);
         background-color: $primary;
     }
 }
diff --git a/src/components/common/VnSmsDialog.vue b/src/components/common/VnSmsDialog.vue
index 59021cef4..3027b14c7 100644
--- a/src/components/common/VnSmsDialog.vue
+++ b/src/components/common/VnSmsDialog.vue
@@ -4,6 +4,7 @@ import { useI18n } from 'vue-i18n';
 import { useDialogPluginComponent } from 'quasar';
 
 import VnInput from 'src/components/common/VnInput.vue';
+import VnChip from 'src/components/common/VnChip.vue';
 
 const { dialogRef, onDialogOK } = useDialogPluginComponent();
 const { t, availableLocales } = useI18n();
@@ -137,9 +138,7 @@ async function send() {
                         />
                     </template>
                     <template #counter>
-                        <QChip :color="color" dense>
-                            {{ totalLength }}/{{ maxLength }}
-                        </QChip>
+                        <VnChip :color="color" :text="totalLength + '/' + maxLength" />
                     </template>
                 </QInput>
             </QCardSection>
diff --git a/src/components/common/VnTitle.vue b/src/components/common/VnTitle.vue
index 1d53e47a1..8e994eb92 100644
--- a/src/components/common/VnTitle.vue
+++ b/src/components/common/VnTitle.vue
@@ -2,20 +2,24 @@
 const $props = defineProps({
     url: { type: String, default: null },
     text: { type: String, default: null },
-    icon: { type: String, default: null },
+    icon: { type: String, default: 'open_in_new' },
 });
 </script>
 <template>
     <div class="titleBox">
         <div class="header-link">
-            <a :href="$props.url" class="link">
-                {{ text }}
-                <QIcon :name="$props.icon" />
+            <a :href="$props.url" :class="$props.url ? 'link' : 'normalTitle'">
+                {{ $props.text }}
+                <QIcon v-if="url" :name="$props.icon" />
             </a>
         </div>
     </div>
 </template>
-<style scoped>
+<style scoped lang="scss">
+.normalTitle {
+    color: whitesmoke;
+    cursor: auto;
+}
 a {
     font-size: large;
 }
diff --git a/src/components/ui/CardDescriptor.vue b/src/components/ui/CardDescriptor.vue
index c53193e6f..e5e4baae4 100644
--- a/src/components/ui/CardDescriptor.vue
+++ b/src/components/ui/CardDescriptor.vue
@@ -171,7 +171,7 @@ const emit = defineEmits(['onFetch']);
 
 <style lang="scss">
 .body {
-    background-color: var(--vn-gray);
+    background-color: var(--vn-section-color);
     .text-h5 {
         padding-top: 5px;
         padding-bottom: 5px;
@@ -187,7 +187,7 @@ const emit = defineEmits(['onFetch']);
         display: flex;
         padding: 2px 16px;
         .label {
-            color: var(--vn-label);
+            color: var(--vn-label-color);
             font-size: 12px;
 
             &:not(:has(a))::after {
@@ -195,7 +195,7 @@ const emit = defineEmits(['onFetch']);
             }
         }
         .value {
-            color: var(--vn-text);
+            color: var(--vn-text-color);
             font-size: 14px;
             margin-left: 12px;
             overflow: hidden;
@@ -220,13 +220,13 @@ const emit = defineEmits(['onFetch']);
     }
 }
 .subtitle {
-    color: var(--vn-text);
+    color: var(--vn-text-color);
     font-size: 16px;
     margin-bottom: 15px;
 }
 .list-box {
     .q-item__label {
-        color: var(--vn-label);
+        color: var(--vn-label-color);
     }
 }
 .descriptor {
diff --git a/src/components/ui/CardList.vue b/src/components/ui/CardList.vue
index eba69a952..9779c58eb 100644
--- a/src/components/ui/CardList.vue
+++ b/src/components/ui/CardList.vue
@@ -1,5 +1,6 @@
 <script setup>
 import { useI18n } from 'vue-i18n';
+import VnChip from 'src/components/common/VnChip.vue';
 
 const { t } = useI18n();
 
@@ -28,9 +29,12 @@ const toggleCardCheck = (item) => {
                         <div class="title text-primary text-weight-bold text-h5">
                             {{ $props.title }}
                         </div>
-                        <QChip class="q-chip-color" outline size="sm">
-                            {{ t('ID') }}: {{ $props.id }}
-                        </QChip>
+                        <VnChip
+                            :text="'ID:' + $props.id"
+                            class="q-chip-color"
+                            outline
+                            size="sm"
+                        />
                     </div>
                     <QCheckbox
                         v-if="showCheckbox"
@@ -61,7 +65,7 @@ const toggleCardCheck = (item) => {
 }
 
 .q-chip-color {
-    color: var(--vn-label);
+    color: var(--vn-label-color) !important;
 }
 
 .card-list-body {
@@ -75,7 +79,7 @@ const toggleCardCheck = (item) => {
         width: 50%;
         .label {
             width: 35%;
-            color: var(--vn-label);
+            color: var(--vn-label-color);
             overflow: hidden;
             text-overflow: ellipsis;
             white-space: nowrap;
@@ -117,7 +121,7 @@ const toggleCardCheck = (item) => {
     transition: background-color 0.2s;
 }
 .card:hover {
-    background-color: var(--vn-gray);
+    background-color: var(--vn-section-color);
 }
 .list-items {
     width: 75%;
diff --git a/src/components/ui/CardSummary.vue b/src/components/ui/CardSummary.vue
index aeca84c80..226981408 100644
--- a/src/components/ui/CardSummary.vue
+++ b/src/components/ui/CardSummary.vue
@@ -87,7 +87,7 @@ watch(props, async () => {
         justify-content: space-evenly;
         gap: 10px;
         padding: 10px;
-        background-color: var(--vn-gray);
+        background-color: var(--vn-section-color);
 
         > .q-card.vn-one {
             flex: 1;
@@ -104,7 +104,7 @@ watch(props, async () => {
 
         > .q-card {
             width: 100%;
-            background-color: var(--vn-gray);
+            background-color: var(--vn-section-color);
             padding: 7px;
             font-size: 16px;
             min-width: 275px;
@@ -114,7 +114,7 @@ watch(props, async () => {
                 flex-direction: row;
                 margin-top: 2px;
                 .label {
-                    color: var(--vn-label);
+                    color: var(--vn-label-color);
                     width: 8em;
                     overflow: hidden;
                     white-space: nowrap;
@@ -124,7 +124,7 @@ watch(props, async () => {
                     flex-shrink: 0;
                 }
                 .value {
-                    color: var(--vn-text);
+                    color: var(--vn-text-color);
                     overflow: hidden;
                 }
             }
@@ -143,12 +143,12 @@ watch(props, async () => {
                 margin-bottom: 9px;
                 & .q-checkbox__label {
                     margin-left: 31px;
-                    color: var(--vn-text);
+                    color: var(--vn-text-color);
                 }
                 & .q-checkbox__inner {
                     position: absolute;
                     left: 0;
-                    color: var(--vn-label);
+                    color: var(--vn-label-color);
                 }
             }
         }
diff --git a/src/components/ui/VnSearchbar.vue b/src/components/ui/VnSearchbar.vue
index f06b44334..ee0313143 100644
--- a/src/components/ui/VnSearchbar.vue
+++ b/src/components/ui/VnSearchbar.vue
@@ -158,7 +158,7 @@ async function search() {
 }
 #searchbar {
     .q-field--standout.q-field--highlighted .q-field__control {
-        background-color: var(--vn-text);
+        background-color: var(--vn-text-color);
     }
 }
 </style>
diff --git a/src/components/ui/VnSms.vue b/src/components/ui/VnSms.vue
index ceb24b2bb..d45eef103 100644
--- a/src/components/ui/VnSms.vue
+++ b/src/components/ui/VnSms.vue
@@ -4,6 +4,7 @@ import { date } from 'quasar';
 import VnPaginate from 'src/components/ui/VnPaginate.vue';
 import VnAvatar from '../ui/VnAvatar.vue';
 import VnUserLink from 'src/components/ui/VnUserLink.vue';
+import VnChip from '../common/VnChip.vue';
 
 const $props = defineProps({
     url: { type: String, default: null },
@@ -89,15 +90,14 @@ function formatNumber(number) {
                                     )
                                 }}</QItemLabel>
                                 <QItemLabel class="row center">
-                                    <QChip
+                                    <VnChip
                                         :color="
                                             row.sms.status == 'OK'
                                                 ? 'positive'
                                                 : 'negative'
                                         "
-                                    >
-                                        {{ row.sms.status }}
-                                    </QChip>
+                                        :text="row.sms.status"
+                                    />
                                 </QItemLabel>
                             </QItemSection>
                         </QItem>
diff --git a/src/css/app.scss b/src/css/app.scss
index 459a7f2dc..7d8a29920 100644
--- a/src/css/app.scss
+++ b/src/css/app.scss
@@ -2,29 +2,24 @@
 @import './icons.scss';
 
 body.body--light {
-    --fount-color: black;
-    --vn-sectionColor: #ffffff;
-    --vn-pageColor: #e0e0e0;
-    background-color: var(--vn-pageColor);
+    --font-color: black;
+    --vn-section-color: #e0e0e0;
+    --vn-page-color: #ffffff;
+    background-color: #ffffff;
     .q-header .q-toolbar {
-        color: var(--fount-color);
+        color: var(--font-color);
     }
-    --vn-text: var(--fount-color);
-    --vn-gray: var(--vn-sectionColor);
-    --vn-label: #5f5f5f;
-    --vn-dark: var(--vn-sectionColor);
-    --vn-light-gray: #e7e3e3;
+    --vn-text-color: var(--font-color);
+    --vn-label-color: #5f5f5f;
+    --vn-accent-color: #e7e3e3;
 }
 
 body.body--dark {
-    --vn-pageColor: #222;
-    --vn-SectionColor: #403c3c;
-    background-color: var(--vn-pageColor);
-    --vn-text: white;
-    --vn-gray: var(--vn-SectionColor);
-    --vn-label: #a8a8a8;
-    --vn-dark: var(--vn-SectionColor);
-    --vn-light-gray: #424242;
+    --vn-section-color: #403c3c;
+    background-color: #222;
+    --vn-text-color: white;
+    --vn-label-color: #a8a8a8;
+    --vn-accent-color: #424242;
 }
 
 a {
@@ -59,19 +54,23 @@ a {
 // Removes chrome autofill background
 input:-webkit-autofill,
 select:-webkit-autofill {
-    color: var(--vn-text);
+    color: var(--vn-text-color);
     font-family: $typography-font-family;
-    -webkit-text-fill-color: var(--vn-text);
+    -webkit-text-fill-color: var(--vn-text-color);
     -webkit-background-clip: text !important;
     background-clip: text !important;
 }
 
 .bg-vn-dark {
-    background-color: var(--vn-dark);
+    background-color: var(--vn-section-color);
+}
+
+.bg-vn-dark {
+    background-color: var(--vn-section-color);
 }
 
 .color-vn-text {
-    color: var(--vn-text);
+    color: var(--vn-text-color);
 }
 
 .color-vn-white {
@@ -79,8 +78,8 @@ select:-webkit-autofill {
 }
 
 .vn-card {
-    background-color: var(--vn-gray);
-    color: var(--vn-text);
+    background-color: var(--vn-section-color);
+    color: var(--vn-text-color);
     border-radius: 8px;
 }
 
@@ -90,11 +89,11 @@ select:-webkit-autofill {
 }
 
 .bg-vn-primary-row {
-    background-color: var(--vn-dark);
+    background-color: var(--vn-section-color);
 }
 
 .bg-vn-secondary-row {
-    background-color: var(--vn-light-gray);
+    background-color: var(--vn-accent-color);
 }
 
 /* Estilo para el asterisco en campos requeridos */
diff --git a/src/css/quasar.variables.scss b/src/css/quasar.variables.scss
index b70e3e8d0..8423a9a35 100644
--- a/src/css/quasar.variables.scss
+++ b/src/css/quasar.variables.scss
@@ -14,10 +14,10 @@
 // Tip: to add new colors https://quasar.dev/style/color-palette/#adding-your-own-colors
 $primary: #ec8916;
 $secondary: $primary;
-$positive: #21ba45;
-$negative: #c10015;
-$info: #31ccec;
-$warning: #f2c037;
+$positive: #c8e484;
+$negative: #fb5252;
+$info: #84d0e2;
+$warning: #f4b974;
 // Pendiente de cuadrar con la base de datos
 $success: $positive;
 $alert: $negative;
diff --git a/src/pages/Claim/Card/ClaimDescriptor.vue b/src/pages/Claim/Card/ClaimDescriptor.vue
index b5b9aae99..968f1e294 100644
--- a/src/pages/Claim/Card/ClaimDescriptor.vue
+++ b/src/pages/Claim/Card/ClaimDescriptor.vue
@@ -107,7 +107,11 @@ onMounted(async () => {
         <template #body="{ entity }">
             <VnLv v-if="entity.claimState" :label="t('claim.card.state')">
                 <template #value>
-                    <QBadge :color="stateColor(entity.claimState.code)" dense>
+                    <QBadge
+                        :color="stateColor(entity.claimState.code)"
+                        text-color="black"
+                        dense
+                    >
                         {{ entity.claimState.description }}
                     </QBadge>
                 </template>
diff --git a/src/pages/Claim/Card/ClaimLines.vue b/src/pages/Claim/Card/ClaimLines.vue
index 90dd31199..248b5a69a 100644
--- a/src/pages/Claim/Card/ClaimLines.vue
+++ b/src/pages/Claim/Card/ClaimLines.vue
@@ -12,6 +12,7 @@ import FetchData from 'components/FetchData.vue';
 import VnDiscount from 'components/common/vnDiscount.vue';
 import ClaimLinesImport from './ClaimLinesImport.vue';
 import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
+import VnChip from 'src/components/common/VnChip.vue';
 
 const quasar = useQuasar();
 const route = useRoute();
@@ -161,16 +162,16 @@ function showImportDialog() {
         <div class="row q-gutter-md">
             <div>
                 {{ t('Amount') }}
-                <QChip :dense="$q.screen.lt.sm">
-                    {{ toCurrency(amount) }}
-                </QChip>
+                <VnChip :dense="$q.screen.lt.sm" :text="toCurrency(amount)" />
             </div>
             <QSeparator dark vertical />
             <div>
                 {{ t('Amount Claimed') }}
-                <QChip color="positive" :dense="$q.screen.lt.sm">
-                    {{ toCurrency(amountClaimed) }}
-                </QChip>
+                <VnChip
+                    color="positive"
+                    :dense="$q.screen.lt.sm"
+                    :text="toCurrency(amountClaimed)"
+                />
             </div>
         </div>
     </Teleport>
diff --git a/src/pages/Claim/Card/ClaimSummary.vue b/src/pages/Claim/Card/ClaimSummary.vue
index 2d8592b6d..4e690526b 100644
--- a/src/pages/Claim/Card/ClaimSummary.vue
+++ b/src/pages/Claim/Card/ClaimSummary.vue
@@ -12,6 +12,7 @@ import ClaimNotes from 'src/pages/Claim/Card/ClaimNotes.vue';
 import VnUserLink from 'src/components/ui/VnUserLink.vue';
 import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
 import VnTitle from 'src/components/common/VnTitle.vue';
+import VnChip from 'src/components/common/VnChip.vue';
 
 const route = useRoute();
 const { t } = useI18n();
@@ -183,7 +184,6 @@ function openDialog(dmsId) {
                 <VnTitle
                     :url="`#/claim/${entityId}/basic-data`"
                     :text="t('claim.pageTitles.basicData')"
-                    icon="open_in_new"
                 />
                 <VnLv
                     :label="t('claim.summary.created')"
@@ -191,9 +191,11 @@ function openDialog(dmsId) {
                 />
                 <VnLv :label="t('claim.summary.state')">
                     <template #value>
-                        <QChip :color="stateColor(claim.claimState.code)" dense>
-                            {{ claim.claimState.description }}
-                        </QChip>
+                        <VnChip
+                            :color="stateColor(claim.claimState.code)"
+                            :text="claim.claimState.description"
+                            dense="dense"
+                        />
                     </template>
                 </VnLv>
                 <VnLv :label="t('globals.salesPerson')">
@@ -227,10 +229,10 @@ function openDialog(dmsId) {
                 />
             </QCard>
             <QCard class="vn-three">
-                <a class="header header-link" :href="`#/claim/${entityId}/notes`">
-                    {{ t('claim.summary.notes') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle
+                    :url="`#/claim/${entityId}/notes`"
+                    :text="t('claim.summary.notes')"
+                />
                 <ClaimNotes
                     :id="entityId"
                     :add-note="false"
@@ -239,10 +241,10 @@ function openDialog(dmsId) {
                 />
             </QCard>
             <QCard class="vn-two" v-if="salesClaimed.length > 0">
-                <a class="header header-link" :href="`#/claim/${entityId}/lines`">
-                    {{ t('claim.summary.details') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle
+                    :url="`#/claim/${entityId}/lines`"
+                    :text="t('claim.summary.details')"
+                />
                 <QTable
                     :columns="detailsColumns"
                     :rows="salesClaimed"
@@ -281,11 +283,10 @@ function openDialog(dmsId) {
                 </QTable>
             </QCard>
             <QCard class="vn-two" v-if="developments.length > 0">
-                <a class="header header-link" :href="claimUrl + 'development'">
-                    {{ t('claim.summary.development') }}
-                    <QIcon name="open_in_new" />
-                </a>
-
+                <VnTitle
+                    :url="claimUrl + 'development'"
+                    :text="t('claim.summary.development')"
+                />
                 <QTable
                     :columns="developmentColumns"
                     :rows="developments"
@@ -304,10 +305,10 @@ function openDialog(dmsId) {
                 </QTable>
             </QCard>
             <QCard class="vn-max" v-if="claimDms.length > 0">
-                <a class="header header-link" :href="`#/claim/${entityId}/photos`">
-                    {{ t('claim.summary.photos') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle
+                    :url="`#/claim/${entityId}/photos`"
+                    :text="t('claim.summary.photos')"
+                />
                 <div class="container">
                     <div
                         class="multimedia-container"
@@ -347,10 +348,7 @@ function openDialog(dmsId) {
             </QCard>
 
             <QCard class="vn-max">
-                <a class="header header-link" :href="claimUrl + 'action'">
-                    {{ t('claim.summary.actions') }}
-                    <QIcon name="open_in_new" class="link" />
-                </a>
+                <VnTitle :url="claimUrl + 'action'" :text="t('claim.summary.actions')" />
                 <div id="slider-container" class="q-px-xl q-py-md">
                     <QSlider
                         v-model="claim.responsibility"
diff --git a/src/pages/Claim/ClaimList.vue b/src/pages/Claim/ClaimList.vue
index 6a793314f..f8ba8d541 100644
--- a/src/pages/Claim/ClaimList.vue
+++ b/src/pages/Claim/ClaimList.vue
@@ -108,7 +108,11 @@ function navigate(event, id) {
                             />
                             <VnLv :label="t('claim.list.state')">
                                 <template #value>
-                                    <QBadge :color="stateColor(row.stateCode)" dense>
+                                    <QBadge
+                                        text-color="black"
+                                        :color="stateColor(row.stateCode)"
+                                        dense
+                                    >
                                         {{ row.stateDescription }}
                                     </QBadge>
                                 </template>
diff --git a/src/pages/Customer/Card/CustomerConsignees.vue b/src/pages/Customer/Card/CustomerConsignees.vue
index ad666ff32..6713d53a3 100644
--- a/src/pages/Customer/Card/CustomerConsignees.vue
+++ b/src/pages/Customer/Card/CustomerConsignees.vue
@@ -150,14 +150,14 @@ const toCustomerConsigneeEdit = (consigneeId) => {
 
 <style lang="scss" scoped>
 .consignees-card {
-    border: 2px solid var(--vn-light-gray);
+    border: 2px solid var(--vn-accent-color);
     border-radius: 10px;
     padding: 10px;
     display: flex;
     cursor: pointer;
 
     &:hover {
-        background-color: var(--vn-light-gray);
+        background-color: var(--vn-accent-color);
     }
 }
 </style>
diff --git a/src/pages/Customer/Card/CustomerGreuges.vue b/src/pages/Customer/Card/CustomerGreuges.vue
index d92d7b4ee..158f418ba 100644
--- a/src/pages/Customer/Card/CustomerGreuges.vue
+++ b/src/pages/Customer/Card/CustomerGreuges.vue
@@ -180,13 +180,13 @@ const toCustomerGreugeCreate = () => {
 
 <style lang="scss">
 .consignees-card {
-    border: 2px solid var(--vn-light-gray);
+    border: 2px solid var(--vn-accent-color);
     border-radius: 10px;
     padding: 10px;
 }
 
 .label-color {
-    color: var(--vn-label);
+    color: var(--vn-label-color);
 }
 </style>
 
diff --git a/src/pages/Customer/Card/CustomerNotes.vue b/src/pages/Customer/Card/CustomerNotes.vue
index 781c57e50..9c1a07ca3 100644
--- a/src/pages/Customer/Card/CustomerNotes.vue
+++ b/src/pages/Customer/Card/CustomerNotes.vue
@@ -85,12 +85,12 @@ const toCustomerNoteCreate = () => {
 
 <style lang="scss">
 .custom-border {
-    border: 2px solid var(--vn-light-gray);
+    border: 2px solid var(--vn-accent-color);
     border-radius: 10px;
     padding: 10px;
 }
 
 .label-color {
-    color: var(--vn-label);
+    color: var(--vn-label-color);
 }
 </style>
diff --git a/src/pages/Customer/Card/CustomerRecoveries.vue b/src/pages/Customer/Card/CustomerRecoveries.vue
index 7247145f2..886968290 100644
--- a/src/pages/Customer/Card/CustomerRecoveries.vue
+++ b/src/pages/Customer/Card/CustomerRecoveries.vue
@@ -137,13 +137,13 @@ const toCustomerRecoverieCreate = () => {
 
 <style lang="scss">
 .consignees-card {
-    border: 2px solid var(--vn-light-gray);
+    border: 2px solid var(--vn-accent-color);
     border-radius: 10px;
     padding: 10px;
 }
 
 .label-color {
-    color: var(--vn-label);
+    color: var(--vn-label-color);
 }
 </style>
 
diff --git a/src/pages/Customer/Card/CustomerSummary.vue b/src/pages/Customer/Card/CustomerSummary.vue
index 184b990b6..504404f5a 100644
--- a/src/pages/Customer/Card/CustomerSummary.vue
+++ b/src/pages/Customer/Card/CustomerSummary.vue
@@ -7,6 +7,7 @@ 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 VnTitle from 'src/components/common/VnTitle.vue';
 
 const route = useRoute();
 const { t } = useI18n();
@@ -62,10 +63,10 @@ const creditWarning = computed(() => {
     <CardSummary ref="summary" :url="`Clients/${entityId}/summary`">
         <template #body="{ entity }">
             <QCard class="vn-one">
-                <a class="header header-link" :href="`#/customer/${entityId}/basic-data`">
-                    {{ t('customer.summary.basicData') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle
+                    :url="`#/customer/${entityId}/basic-data`"
+                    :text="t('customer.summary.basicData')"
+                />
                 <VnLv :label="t('customer.summary.customerId')" :value="entity.id" />
                 <VnLv :label="t('customer.summary.name')" :value="entity.name" />
                 <VnLv :label="t('customer.summary.contact')" :value="entity.contact" />
@@ -96,13 +97,10 @@ const creditWarning = computed(() => {
                 />
             </QCard>
             <QCard class="vn-one">
-                <a
-                    class="header header-link"
-                    :href="`#/customer/${entityId}/fiscal-data`"
-                >
-                    {{ t('customer.summary.fiscalAddress') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle
+                    :url="`#/customer/${entityId}/fiscal-data`"
+                    :text="t('customer.summary.fiscalAddress')"
+                />
                 <VnLv
                     :label="t('customer.summary.socialName')"
                     :value="entity.socialName"
@@ -124,14 +122,10 @@ const creditWarning = computed(() => {
                 <VnLv :label="t('customer.summary.street')" :value="entity.street" />
             </QCard>
             <QCard class="vn-one">
-                <a
-                    class="header header-link"
-                    :href="`#/customer/${entityId}/fiscal-data`"
-                    link
-                >
-                    {{ t('customer.summary.fiscalData') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle
+                    :url="`#/customer/${entityId}/fiscal-data`"
+                    :text="t('customer.summary.fiscalData')"
+                />
                 <QCheckbox
                     :label="t('customer.summary.isEqualizated')"
                     v-model="entity.isEqualizated"
@@ -169,14 +163,10 @@ const creditWarning = computed(() => {
                 />
             </QCard>
             <QCard class="vn-one">
-                <a
-                    class="header header-link"
-                    :href="`#/customer/${entityId}/billing-data`"
-                    link
-                >
-                    {{ t('customer.summary.billingData') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle
+                    :url="`#/customer/${entityId}/billing-data`"
+                    :text="t('customer.summary.billingData')"
+                />
                 <VnLv
                     :label="t('customer.summary.payMethod')"
                     :value="entity.payMethod.name"
@@ -202,14 +192,10 @@ const creditWarning = computed(() => {
                 />
             </QCard>
             <QCard class="vn-one" v-if="entity.defaultAddress">
-                <a
-                    class="header header-link"
-                    :href="`#/customer/${entityId}/consignees`"
-                    link
-                >
-                    {{ t('customer.summary.consignee') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle
+                    :url="`#/customer/${entityId}/consignees`"
+                    :text="t('customer.summary.consignee')"
+                />
                 <VnLv
                     :label="t('customer.summary.addressName')"
                     :value="entity.defaultAddress.nickname"
@@ -224,10 +210,10 @@ const creditWarning = computed(() => {
                 />
             </QCard>
             <QCard class="vn-one" v-if="entity.account">
-                <a class="header header-link" :href="`#/customer/${entityId}/web-access`">
-                    {{ t('customer.summary.webAccess') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle
+                    :url="`#/customer/${entityId}/web-access`"
+                    :text="t('customer.summary.webAccess')"
+                />
                 <VnLv
                     :label="t('customer.summary.username')"
                     :value="entity.account.name"
@@ -239,9 +225,7 @@ const creditWarning = computed(() => {
                 />
             </QCard>
             <QCard class="vn-one" v-if="entity.account">
-                <div class="header header-link">
-                    {{ t('customer.summary.businessData') }}
-                </div>
+                <VnTitle :text="t('customer.summary.businessData')" />
                 <VnLv
                     :label="t('customer.summary.totalGreuge')"
                     :value="toCurrency(entity.totalGreuge)"
@@ -266,14 +250,11 @@ const creditWarning = computed(() => {
                 />
             </QCard>
             <QCard class="vn-one" v-if="entity.account">
-                <a
-                    class="header header-link"
-                    :href="`https://grafana.verdnatura.es/d/40buzE4Vk/comportamiento-pagos-clientes?orgId=1&var-clientFk=${entityId}`"
-                    link
-                >
-                    {{ t('customer.summary.financialData') }}
-                    <QIcon name="vn:grafana" />
-                </a>
+                <VnTitle
+                    :url="`https://grafana.verdnatura.es/d/40buzE4Vk/comportamiento-pagos-clientes?orgId=1&var-clientFk=${entityId}`"
+                    :text="t('customer.summary.financialData')"
+                    icon="vn:grafana"
+                />
                 <VnLv
                     :label="t('customer.summary.risk')"
                     :value="toCurrency(entity?.debt?.debt)"
diff --git a/src/pages/Customer/Defaulter/CustomerBalanceDueTotal.vue b/src/pages/Customer/Defaulter/CustomerBalanceDueTotal.vue
index c4a50a10f..c49ed5e32 100644
--- a/src/pages/Customer/Defaulter/CustomerBalanceDueTotal.vue
+++ b/src/pages/Customer/Defaulter/CustomerBalanceDueTotal.vue
@@ -30,16 +30,16 @@ const { t } = useI18n();
     border: 1px solid black;
 }
 .title_balance {
-    color: var(--vn-text);
+    color: var(--vn-text-color);
     margin-top: 0;
     margin-bottom: 0;
 }
 .key_balance {
-    color: var(--vn-label);
+    color: var(--vn-label-color);
     margin-bottom: 0;
 }
 .value_balance {
-    color: var(--vn-text);
+    color: var(--vn-text-color);
     margin-bottom: 0;
 }
 </style>
diff --git a/src/pages/Customer/Payments/CustomerPayments.vue b/src/pages/Customer/Payments/CustomerPayments.vue
index eedaaf137..5a0404eef 100644
--- a/src/pages/Customer/Payments/CustomerPayments.vue
+++ b/src/pages/Customer/Payments/CustomerPayments.vue
@@ -196,7 +196,7 @@ function stateColor(row) {
                         </template>
                         <template #body-cell-state="{ row }">
                             <QTd auto-width class="text-center">
-                                <QBadge :color="stateColor(row)">
+                                <QBadge text-color="black" :color="stateColor(row)">
                                     {{
                                         row.isConfirmed
                                             ? t('Confirmed')
@@ -227,6 +227,7 @@ function stateColor(row) {
                                                                 v-if="col.name == 'state'"
                                                             >
                                                                 <QBadge
+                                                                    text-color="black"
                                                                     :color="
                                                                         stateColor(row)
                                                                     "
diff --git a/src/pages/Department/Card/DepartmentSummary.vue b/src/pages/Department/Card/DepartmentSummary.vue
index d6fac56c6..1d42183c3 100644
--- a/src/pages/Department/Card/DepartmentSummary.vue
+++ b/src/pages/Department/Card/DepartmentSummary.vue
@@ -5,6 +5,7 @@ import { useI18n } from 'vue-i18n';
 import CardSummary from 'components/ui/CardSummary.vue';
 import { getUrl } from 'src/composables/getUrl';
 import VnLv from 'src/components/ui/VnLv.vue';
+import VnTitle from 'src/components/common/VnTitle.vue';
 
 const route = useRoute();
 const { t } = useI18n();
@@ -36,13 +37,10 @@ onMounted(async () => {
         </template>
         <template #body="{ entity: department }">
             <QCard class="column">
-                <a
-                    class="header header-link"
-                    :href="`#/department/department/${entityId}/basic-data`"
-                >
-                    {{ t('Basic data') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle
+                    :url="`#/department/department/${entityId}/basic-data`"
+                    :text="t('Basic data')"
+                />
                 <div class="full-width row wrap justify-between content-between">
                     <div class="column" style="min-width: 50%">
                         <VnLv
diff --git a/src/pages/Entry/Card/EntryBuys.vue b/src/pages/Entry/Card/EntryBuys.vue
index 679afff1b..bd3bab78b 100644
--- a/src/pages/Entry/Card/EntryBuys.vue
+++ b/src/pages/Entry/Card/EntryBuys.vue
@@ -449,7 +449,7 @@ const showLockIcon = (groupingMode, mode) => {
 
 <style lang="scss" scoped>
 .separation-row {
-    background-color: var(--vn-gray) !important;
+    background-color: var(--vn-section-color) !important;
 }
 .grid-style-transition {
     transition: transform 0.28s, background-color 0.28s;
diff --git a/src/pages/Entry/Card/EntrySummary.vue b/src/pages/Entry/Card/EntrySummary.vue
index be30468d5..d89d7cf87 100644
--- a/src/pages/Entry/Card/EntrySummary.vue
+++ b/src/pages/Entry/Card/EntrySummary.vue
@@ -5,9 +5,8 @@ import { useI18n } from 'vue-i18n';
 
 import CardSummary from 'components/ui/CardSummary.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
-import VnRow from 'components/ui/VnRow.vue';
 import TravelDescriptorProxy from 'src/pages/Travel/Card/TravelDescriptorProxy.vue';
-import FetchedTags from 'components/ui/FetchedTags.vue';
+import VnTitle from 'src/components/common/VnTitle.vue';
 
 import { toDate, toCurrency } from 'src/filters';
 import { getUrl } from 'src/composables/getUrl';
@@ -164,7 +163,7 @@ const fetchEntryBuys = async () => {
         :url="`Entries/${entityId}/getEntry`"
         @on-fetch="(data) => setEntryData(data)"
     >
-        <template #header-left>
+        <template>
             <a class="header-link" :href="entryUrl">
                 <QIcon name="open_in_new" color="white" size="sm" />
             </a>
@@ -175,10 +174,10 @@ const fetchEntryBuys = async () => {
 
         <template #body>
             <QCard class="vn-one">
-                <a class="header link" :href="`#/entry/${entityId}/basic-data`">
-                    textos
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle
+                    :url="`#/entry/${entityId}/basic-data`"
+                    :text="t('basic data')"
+                />
 
                 <VnLv :label="t('entry.summary.commission')" :value="entry.commission" />
 
@@ -219,10 +218,7 @@ const fetchEntryBuys = async () => {
                 />
             </QCard>
             <QCard class="vn-one">
-                <a class="header header-link" :href="entryUrl">
-                    {{ t('Travel data') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle :url="entryUrl" :text="t('Travel data')" />
 
                 <VnLv :label="t('entry.summary.travelReference')">
                     <template #value>
@@ -270,10 +266,7 @@ const fetchEntryBuys = async () => {
                 />
             </QCard>
             <QCard class="vn-two" style="min-width: 100%">
-                <a class="header header-link">
-                    {{ t('entry.summary.buys') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle :text="t('entry.summary.buys')" />
             </QCard>
         </template>
     </CardSummary>
@@ -281,7 +274,7 @@ const fetchEntryBuys = async () => {
 
 <style lang="scss" scoped>
 .separation-row {
-    background-color: var(--vn-gray) !important;
+    background-color: var(--vn-section-color) !important;
 }
 </style>
 
diff --git a/src/pages/Entry/EntryLatestBuysFilter.vue b/src/pages/Entry/EntryLatestBuysFilter.vue
index 23361ffb3..5eaf001d8 100644
--- a/src/pages/Entry/EntryLatestBuysFilter.vue
+++ b/src/pages/Entry/EntryLatestBuysFilter.vue
@@ -412,7 +412,7 @@ const removeTag = (index, params, search) => {
         width: 60px;
         height: 60px;
         font-size: 1.4rem;
-        background-color: var(--vn-light-gray);
+        background-color: var(--vn-accent-color);
 
         &.active {
             background-color: $primary;
diff --git a/src/pages/InvoiceIn/Card/InvoiceInSummary.vue b/src/pages/InvoiceIn/Card/InvoiceInSummary.vue
index 6d2d11509..e30d9151c 100644
--- a/src/pages/InvoiceIn/Card/InvoiceInSummary.vue
+++ b/src/pages/InvoiceIn/Card/InvoiceInSummary.vue
@@ -6,6 +6,8 @@ import { toCurrency, toDate } from 'src/filters';
 import { getUrl } from 'src/composables/getUrl';
 import CardSummary from 'components/ui/CardSummary.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
+import VnChip from 'src/components/common/VnChip.vue';
+import VnTitle from 'src/components/common/VnTitle.vue';
 
 onMounted(async () => {
     salixUrl.value = await getUrl('');
@@ -209,10 +211,10 @@ function getLink(param) {
             <!--Basic Data-->
             <QCard class="vn-one">
                 <QCardSection class="q-pa-none">
-                    <a class="header header-link" :href="getLink('basic-data')">
-                        {{ t('invoiceIn.pageTitles.basicData') }}
-                        <QIcon name="open_in_new" />
-                    </a>
+                    <VnTitle
+                        :url="getLink('basic-data')"
+                        :text="t('invoiceIn.pageTitles.basicData')"
+                    />
                 </QCardSection>
                 <VnLv
                     :label="t('invoiceIn.summary.supplier')"
@@ -233,10 +235,10 @@ function getLink(param) {
             </QCard>
             <QCard class="vn-one">
                 <QCardSection class="q-pa-none">
-                    <a class="header header-link" :href="getLink('basic-data')">
-                        {{ t('invoiceIn.pageTitles.basicData') }}
-                        <QIcon name="open_in_new" />
-                    </a>
+                    <VnTitle
+                        :url="getLink('basic-data')"
+                        :text="t('invoiceIn.pageTitles.basicData')"
+                    />
                 </QCardSection>
                 <VnLv
                     :ellipsis-value="false"
@@ -258,10 +260,10 @@ function getLink(param) {
             </QCard>
             <QCard class="vn-one">
                 <QCardSection class="q-pa-none">
-                    <a class="header header-link" :href="getLink('basic-data')">
-                        {{ t('invoiceIn.pageTitles.basicData') }}
-                        <QIcon name="open_in_new" />
-                    </a>
+                    <VnTitle
+                        :url="getLink('basic-data')"
+                        :text="t('invoiceIn.pageTitles.basicData')"
+                    />
                 </QCardSection>
                 <VnLv
                     :label="t('invoiceIn.summary.sage')"
@@ -283,10 +285,10 @@ function getLink(param) {
             </QCard>
             <QCard class="vn-one">
                 <QCardSection class="q-pa-none">
-                    <a class="header header-link" :href="getLink('basic-data')">
-                        {{ t('invoiceIn.pageTitles.basicData') }}
-                        <QIcon name="open_in_new" />
-                    </a>
+                    <VnTitle
+                        :url="getLink('basic-data')"
+                        :text="t('invoiceIn.pageTitles.basicData')"
+                    />
                 </QCardSection>
                 <QCardSection class="q-pa-none">
                     <div class="bordered q-px-sm q-mx-auto">
@@ -300,7 +302,7 @@ function getLink(param) {
                         />
                         <VnLv :label="t('invoiceIn.summary.dueTotal')">
                             <template #value>
-                                <QChip
+                                <VnChip
                                     dense
                                     class="q-pa-xs"
                                     :color="amountsNotMatch ? 'negative' : 'transparent'"
@@ -309,9 +311,8 @@ function getLink(param) {
                                             ? t('invoiceIn.summary.noMatch')
                                             : t('invoiceIn.summary.dueTotal')
                                     "
-                                >
-                                    {{ toCurrency(invoiceIn.totals.totalDueDay) }}
-                                </QChip>
+                                    :text="toCurrency(invoiceIn.totals.totalDueDay)"
+                                />
                             </template>
                         </VnLv>
                     </div>
@@ -319,10 +320,7 @@ function getLink(param) {
             </QCard>
             <!--Vat-->
             <QCard v-if="invoiceIn.invoiceInTax.length">
-                <a class="header header-link" :href="getLink('vat')">
-                    {{ t('invoiceIn.card.vat') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle :url="getLink('vat')" :text="t('invoiceIn.card.vat')" />
                 <QTable
                     :columns="vatColumns"
                     :rows="invoiceIn.invoiceInTax"
@@ -352,10 +350,7 @@ function getLink(param) {
             </QCard>
             <!--Due Day-->
             <QCard v-if="invoiceIn.invoiceInDueDay.length">
-                <a class="header header-link" :href="getLink('due-day')">
-                    {{ t('invoiceIn.card.dueDay') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle :url="getLink('due-day')" :text="t('invoiceIn.card.dueDay')" />
                 <QTable
                     class="full-width"
                     :columns="dueDayColumns"
@@ -382,10 +377,10 @@ function getLink(param) {
             </QCard>
             <!--Intrastat-->
             <QCard v-if="invoiceIn.invoiceInIntrastat.length">
-                <a class="header header-link" :href="getLink('intrastat')">
-                    {{ t('invoiceIn.card.intrastat') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle
+                    :url="getLink('intrastat')"
+                    :text="t('invoiceIn.card.intrastat')"
+                />
                 <QTable
                     :columns="intrastatColumns"
                     :rows="invoiceIn.invoiceInIntrastat"
@@ -415,10 +410,10 @@ function getLink(param) {
 </template>
 <style lang="scss" scoped>
 .bg {
-    background-color: var(--vn-light-gray);
+    background-color: var(--vn-accent-color);
 }
 .bordered {
-    border: 1px solid var(--vn-text);
+    border: 1px solid var(--vn-text-color);
     max-width: 18em;
 }
 </style>
diff --git a/src/pages/InvoiceOut/Card/InvoiceOutSummary.vue b/src/pages/InvoiceOut/Card/InvoiceOutSummary.vue
index 73acc0ad1..311b029ad 100644
--- a/src/pages/InvoiceOut/Card/InvoiceOutSummary.vue
+++ b/src/pages/InvoiceOut/Card/InvoiceOutSummary.vue
@@ -7,6 +7,7 @@ import { toCurrency, toDate } from 'src/filters';
 import CardSummary from 'components/ui/CardSummary.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
 import { getUrl } from 'src/composables/getUrl';
+import VnTitle from 'src/components/common/VnTitle.vue';
 
 onMounted(async () => {
     fetch();
@@ -101,10 +102,7 @@ const ticketsColumns = ref([
         </template>
         <template #body="{ entity: { invoiceOut } }">
             <QCard class="vn-one">
-                <a class="header header-link">
-                    {{ t('invoiceOut.pageTitles.basicData') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle :text="t('invoiceOut.pageTitles.basicData')" />
                 <VnLv
                     :label="t('invoiceOut.summary.issued')"
                     :value="toDate(invoiceOut.issued)"
@@ -127,10 +125,7 @@ const ticketsColumns = ref([
                 />
             </QCard>
             <QCard class="vn-three">
-                <a class="header header-link">
-                    {{ t('invoiceOut.summary.taxBreakdown') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle :text="t('invoiceOut.summary.taxBreakdown')" />
                 <QTable :columns="taxColumns" :rows="invoiceOut.taxesBreakdown" flat>
                     <template #header="props">
                         <QTr :props="props">
@@ -142,10 +137,7 @@ const ticketsColumns = ref([
                 </QTable>
             </QCard>
             <QCard class="vn-three">
-                <a class="header header-link">
-                    {{ t('invoiceOut.summary.tickets') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle :text="t('invoiceOut.summary.tickets')" />
                 <QTable v-if="tickets" :columns="ticketsColumns" :rows="tickets" flat>
                     <template #header="props">
                         <QTr :props="props">
diff --git a/src/pages/InvoiceOut/InvoiceOutGlobal.vue b/src/pages/InvoiceOut/InvoiceOutGlobal.vue
index 7e2b43a76..6378ff8e5 100644
--- a/src/pages/InvoiceOut/InvoiceOutGlobal.vue
+++ b/src/pages/InvoiceOut/InvoiceOutGlobal.vue
@@ -156,7 +156,7 @@ onUnmounted(() => {
     display: flex;
     justify-content: center;
     width: 100%;
-    background-color: var(--vn-dark);
+    background-color: var(--vn-section-color);
     padding: 16px;
 
     .card-section {
@@ -167,7 +167,7 @@ onUnmounted(() => {
 
     .text {
         font-size: 14px;
-        color: var(--vn-text);
+        color: var(--vn-text-color);
     }
 }
 
diff --git a/src/pages/Login/VerifyEmail.vue b/src/pages/Login/VerifyEmail.vue
index 4c02e5869..1febd0eaa 100644
--- a/src/pages/Login/VerifyEmail.vue
+++ b/src/pages/Login/VerifyEmail.vue
@@ -57,10 +57,13 @@ onMounted(async () => {
                 :href="button.url"
             >
                 <div class="row items-center no-wrap q-gutter-md">
-                    <div class="circle q-pa-sm" style="background-color: var(--vn-gray)">
+                    <div
+                        class="circle q-pa-sm"
+                        style="background-color: var(--vn-section-color)"
+                    >
                         <QImg :src="button.icon" class="q-pa-md" />
                     </div>
-                    <div class="text-h5" style="color: var(--vn-gray)">
+                    <div class="text-h5" style="color: var(--vn-section-color)">
                         {{ t(button.text) }}
                     </div>
                 </div>
diff --git a/src/pages/Order/Card/OrderCatalogFilter.vue b/src/pages/Order/Card/OrderCatalogFilter.vue
index 760c48726..402df1173 100644
--- a/src/pages/Order/Card/OrderCatalogFilter.vue
+++ b/src/pages/Order/Card/OrderCatalogFilter.vue
@@ -420,7 +420,7 @@ const getCategoryClass = (category, params) => {
 
     .category-icon {
         border-radius: 50%;
-        background-color: var(--vn-light-gray);
+        background-color: var(--vn-accent-color);
         font-size: 2.6rem;
         padding: 8px;
         cursor: pointer;
diff --git a/src/pages/Order/Card/OrderCatalogItem.vue b/src/pages/Order/Card/OrderCatalogItem.vue
index ee73bcffb..8a949adad 100644
--- a/src/pages/Order/Card/OrderCatalogItem.vue
+++ b/src/pages/Order/Card/OrderCatalogItem.vue
@@ -88,11 +88,11 @@ const dialog = ref(null);
         font-size: 11px;
 
         .label {
-            color: var(--vn-label);
+            color: var(--vn-label-color);
         }
 
         .value {
-            color: var(--vn-text);
+            color: var(--vn-text-color);
         }
     }
 }
@@ -125,7 +125,7 @@ const dialog = ref(null);
     gap: 4px;
 
     .subName {
-        color: var(--vn-label);
+        color: var(--vn-label-color);
         text-transform: uppercase;
     }
 
diff --git a/src/pages/Order/Card/OrderSummary.vue b/src/pages/Order/Card/OrderSummary.vue
index f9704a480..cba4723e8 100644
--- a/src/pages/Order/Card/OrderSummary.vue
+++ b/src/pages/Order/Card/OrderSummary.vue
@@ -248,7 +248,7 @@ const detailsColumns = ref([
 
         .subName {
             text-transform: uppercase;
-            color: var(--vn-label);
+            color: var(--vn-label-color);
         }
     }
 }
diff --git a/src/pages/Order/OrderCatalog.vue b/src/pages/Order/OrderCatalog.vue
index 21442d10d..1ed03c47d 100644
--- a/src/pages/Order/OrderCatalog.vue
+++ b/src/pages/Order/OrderCatalog.vue
@@ -104,7 +104,7 @@ function extractTags(items) {
 .no-result {
     font-size: 24px;
     font-weight: bold;
-    color: var(--vn-label);
+    color: var(--vn-label-color);
     text-align: center;
 }
 </style>
diff --git a/src/pages/Order/OrderLines.vue b/src/pages/Order/OrderLines.vue
index a2ee42481..930230c7b 100644
--- a/src/pages/Order/OrderLines.vue
+++ b/src/pages/Order/OrderLines.vue
@@ -14,6 +14,7 @@ import VnConfirm from 'components/ui/VnConfirm.vue';
 import { toCurrency, toDate } from 'src/filters';
 import { useSession } from 'composables/useSession';
 import axios from 'axios';
+import VnChip from 'src/components/common/VnChip.vue';
 
 const route = useRoute();
 const { t } = useI18n();
@@ -150,9 +151,12 @@ async function confirmOrder() {
                                     >
                                         {{ row?.item?.name }}
                                     </div>
-                                    <QChip class="q-chip-color" outline size="sm">
-                                        {{ t('ID') }}: {{ row.id }}
-                                    </QChip>
+                                    <VnChip
+                                        class="q-chip-color"
+                                        outline
+                                        size="sm"
+                                        :text="t('ID') + ':' + row.id"
+                                    />
                                 </div>
                             </template>
                             <template #list-items>
@@ -213,7 +217,7 @@ async function confirmOrder() {
         gap: 2%;
 
         .label {
-            color: var(--vn-label);
+            color: var(--vn-label-color);
             overflow: hidden;
             text-overflow: ellipsis;
             white-space: nowrap;
@@ -246,13 +250,13 @@ async function confirmOrder() {
 }
 
 .subname {
-    color: var(--vn-label);
+    color: var(--vn-label-color);
 }
 
 .no-result {
     font-size: 24px;
     font-weight: bold;
-    color: var(--vn-label);
+    color: var(--vn-label-color);
     text-align: center;
 }
 </style>
diff --git a/src/pages/Order/OrderList.vue b/src/pages/Order/OrderList.vue
index b7b233a64..203eaccd1 100644
--- a/src/pages/Order/OrderList.vue
+++ b/src/pages/Order/OrderList.vue
@@ -104,7 +104,7 @@ function navigate(id) {
                             />
                             <VnLv :label="t('order.field.landed')">
                                 <template #value>
-                                    <QBadge color="positive" dense>
+                                    <QBadge text-color="black" color="positive" dense>
                                         {{ toDate(row?.landed) }}
                                     </QBadge>
                                 </template>
diff --git a/src/pages/Order/OrderVolume.vue b/src/pages/Order/OrderVolume.vue
index 5bb106edc..67f409b45 100644
--- a/src/pages/Order/OrderVolume.vue
+++ b/src/pages/Order/OrderVolume.vue
@@ -106,7 +106,7 @@ const loadVolumes = async (rows) => {
         gap: 2%;
 
         .label {
-            color: var(--vn-label);
+            color: var(--vn-label-color);
             overflow: hidden;
             text-overflow: ellipsis;
             white-space: nowrap;
@@ -132,7 +132,7 @@ const loadVolumes = async (rows) => {
 .no-result {
     font-size: 24px;
     font-weight: bold;
-    color: var(--vn-label);
+    color: var(--vn-label-color);
     text-align: center;
 }
 </style>
diff --git a/src/pages/Route/Cmr/CmrList.vue b/src/pages/Route/Cmr/CmrList.vue
index dc5602c1c..1baf22c17 100644
--- a/src/pages/Route/Cmr/CmrList.vue
+++ b/src/pages/Route/Cmr/CmrList.vue
@@ -123,6 +123,7 @@ function downloadPdfs() {
                         <template #body-cell-hasCmrDms="{ value }">
                             <QTd align="center">
                                 <QBadge
+                                    text-color="black"
                                     :id="value ? 'true' : 'false'"
                                     :label="
                                         value
diff --git a/src/pages/Supplier/Card/SupplierConsumption.vue b/src/pages/Supplier/Card/SupplierConsumption.vue
index 59dd2281c..86c5e586b 100644
--- a/src/pages/Supplier/Card/SupplierConsumption.vue
+++ b/src/pages/Supplier/Card/SupplierConsumption.vue
@@ -195,7 +195,7 @@ onMounted(async () => {
 
 <style scoped lang="scss">
 .label {
-    color: var(--vn-label);
+    color: var(--vn-label-color);
 }
 </style>
 
diff --git a/src/pages/Supplier/Card/SupplierContacts.vue b/src/pages/Supplier/Card/SupplierContacts.vue
index d69b74a4c..ce65ecac6 100644
--- a/src/pages/Supplier/Card/SupplierContacts.vue
+++ b/src/pages/Supplier/Card/SupplierContacts.vue
@@ -107,7 +107,7 @@ onMounted(() => {
 <style lang="scss" scoped>
 .border {
     border-radius: 0px !important;
-    border: 1px solid var(--vn-text) !important;
+    border: 1px solid var(--vn-text-color) !important;
 }
 </style>
 
diff --git a/src/pages/Supplier/Card/SupplierSummary.vue b/src/pages/Supplier/Card/SupplierSummary.vue
index 2da9bcfdc..2b1131e5e 100644
--- a/src/pages/Supplier/Card/SupplierSummary.vue
+++ b/src/pages/Supplier/Card/SupplierSummary.vue
@@ -8,6 +8,7 @@ import { getUrl } from 'src/composables/getUrl';
 import { useRole } from 'src/composables/useRole';
 import { dashIfEmpty } from 'src/filters';
 import VnUserLink from 'src/components/ui/VnUserLink.vue';
+import VnTitle from 'src/components/common/VnTitle.vue';
 
 onUpdated(() => summaryRef.value.fetch());
 
@@ -51,7 +52,7 @@ const isAdministrative = computed(() => {
         :url="`Suppliers/${entityId}/getSummary`"
         @on-fetch="(data) => setData(data)"
     >
-        <template #header-left>
+        <template>
             <a v-if="isAdministrative" class="header header-link" :href="supplierUrl">
                 <QIcon name="open_in_new" color="white" size="sm" />
             </a>
diff --git a/src/pages/Ticket/Card/TicketDescriptor.vue b/src/pages/Ticket/Card/TicketDescriptor.vue
index f0ded019b..dfbcfc106 100644
--- a/src/pages/Ticket/Card/TicketDescriptor.vue
+++ b/src/pages/Ticket/Card/TicketDescriptor.vue
@@ -98,7 +98,10 @@ const setData = (entity) =>
             </VnLv>
             <VnLv v-if="entity.ticketState" :label="t('ticket.card.state')">
                 <template #value>
-                    <QBadge :color="entity.ticketState.state.classColor">
+                    <QBadge
+                        text-color="black"
+                        :color="entity.ticketState.state.classColor"
+                    >
                         {{ entity.ticketState.state.name }}
                     </QBadge>
                 </template>
diff --git a/src/pages/Ticket/Card/TicketSummary.vue b/src/pages/Ticket/Card/TicketSummary.vue
index fbca21086..a7608cae8 100644
--- a/src/pages/Ticket/Card/TicketSummary.vue
+++ b/src/pages/Ticket/Card/TicketSummary.vue
@@ -12,6 +12,8 @@ import VnLv from 'src/components/ui/VnLv.vue';
 import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue';
 import { getUrl } from 'src/composables/getUrl';
 import VnUserLink from 'src/components/ui/VnUserLink.vue';
+import VnChip from 'src/components/common/VnChip.vue';
+import VnTitle from 'src/components/common/VnTitle.vue';
 
 onUpdated(() => summaryRef.value.fetch());
 
@@ -74,7 +76,7 @@ async function changeState(value) {
         code: value,
     };
 
-    await axios.post(`TicketTrackings/changeState`, formData);
+    await axios.post(`Tickets/state`, formData);
     router.go(route.fullPath);
 }
 </script>
@@ -102,8 +104,8 @@ async function changeState(value) {
             <QBtnDropdown
                 side
                 top
-                color="orange-11"
-                text-color="black"
+                color="black"
+                text-color="white"
                 :label="t('ticket.summary.changeState')"
                 :disable="!isEditable()"
             >
@@ -147,15 +149,17 @@ async function changeState(value) {
                 </div>
             </QCard>
             <QCard class="vn-one">
-                <a class="header header-link" :href="ticketUrl + 'basic-data/step-one'">
-                    {{ t('globals.summary.basicData') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle
+                    :url="ticketUrl + 'basic-data/step-one'"
+                    :text="t('globals.summary.basicData')"
+                />
                 <VnLv :label="t('ticket.summary.state')">
                     <template #value>
-                        <QChip :color="ticket.ticketState?.state?.classColor ?? 'dark'">
-                            {{ ticket.ticketState?.state?.name }}
-                        </QChip>
+                        <VnChip
+                            :color="ticket.ticketState?.state?.classColor ?? 'dark'"
+                            dense="dense"
+                            :text="ticket.ticketState?.state?.name"
+                        />
                     </template>
                 </VnLv>
                 <VnLv :label="t('ticket.summary.salesPerson')">
@@ -193,10 +197,10 @@ async function changeState(value) {
                 />
             </QCard>
             <QCard class="vn-one">
-                <a class="header header-link" :href="ticketUrl + 'basic-data/step-one'">
-                    {{ t('globals.summary.basicData') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle
+                    :url="ticketUrl + 'basic-data/step-one'"
+                    :text="t('globals.summary.basicData')"
+                />
                 <VnLv
                     :label="t('ticket.summary.shipped')"
                     :value="toDate(ticket.shipped)"
@@ -236,10 +240,10 @@ async function changeState(value) {
                 />
             </QCard>
             <QCard class="vn-one">
-                <a class="header header-link" :href="ticketUrl + 'observation'">
-                    {{ t('ticket.pageTitles.notes') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle
+                    :url="ticketUrl + 'observation'"
+                    :text="t('ticket.pageTitles.notes')"
+                />
                 <VnLv
                     v-for="note in ticket.notes"
                     :key="note.id"
@@ -258,10 +262,10 @@ async function changeState(value) {
                 </VnLv>
             </QCard>
             <QCard class="vn-max">
-                <a class="header header-link" :href="ticketUrl + 'sale'">
-                    {{ t('ticket.summary.saleLines') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle
+                    :url="ticketUrl + 'sale'"
+                    :text="t('ticket.summary.saleLines')"
+                />
                 <QTable :rows="ticket.sales">
                     <template #header="props">
                         <QTr :props="props">
@@ -396,10 +400,7 @@ async function changeState(value) {
                 class="vn-max"
                 v-if="ticket.packagings.length > 0 || ticket.services.length > 0"
             >
-                <a class="header header-link" :href="ticketUrl + 'package'">
-                    {{ t('globals.packages') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle :url="ticketUrl + 'package'" :text="t('globals.packages')" />
                 <QTable :rows="ticket.packagings" flat>
                     <template #header="props">
                         <QTr :props="props">
@@ -416,11 +417,10 @@ async function changeState(value) {
                         </QTr>
                     </template>
                 </QTable>
-
-                <a class="header header-link q-mt-xl" :href="ticketUrl + 'service'">
-                    {{ t('ticket.summary.service') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle
+                    :url="ticketUrl + 'service'"
+                    :text="t('ticket.summary.service')"
+                />
                 <QTable :rows="ticket.services" flat>
                     <template #header="props">
                         <QTr :props="props">
diff --git a/src/pages/Ticket/TicketList.vue b/src/pages/Ticket/TicketList.vue
index 72c2da57c..c5d25fed2 100644
--- a/src/pages/Ticket/TicketList.vue
+++ b/src/pages/Ticket/TicketList.vue
@@ -89,6 +89,7 @@ function navigate(id) {
                             <VnLv :label="t('ticket.list.state')">
                                 <template #value>
                                     <QBadge
+                                        text-color="black"
                                         :color="row.classColor ?? 'orange'"
                                         class="q-ma-none"
                                         dense
diff --git a/src/pages/Travel/Card/TravelSummary.vue b/src/pages/Travel/Card/TravelSummary.vue
index 5a2c6bdb2..65050dfcd 100644
--- a/src/pages/Travel/Card/TravelSummary.vue
+++ b/src/pages/Travel/Card/TravelSummary.vue
@@ -5,6 +5,7 @@ import { useI18n } from 'vue-i18n';
 
 import CardSummary from 'components/ui/CardSummary.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
+import VnTitle from 'src/components/common/VnTitle.vue';
 import EntryDescriptorProxy from 'src/pages/Entry/Card/EntryDescriptorProxy.vue';
 import FetchData from 'src/components/FetchData.vue';
 import TravelDescriptorMenuItems from './TravelDescriptorMenuItems.vue';
@@ -294,9 +295,7 @@ async function setTravelData(travelData) {
                 <VnLv :label="t('globals.totalEntries')" :value="travel.totalEntries" />
             </QCard>
             <QCard class="full-width" v-if="entriesTableRows.length > 0">
-                <span class="header header-link">
-                    {{ t('travel.summary.entries') }}
-                </span>
+                <VnTitle :text="t('travel.summary.entries')" />
                 <QTable
                     :rows="entriesTableRows"
                     :columns="entriesTableColumns"
diff --git a/src/pages/Travel/TravelList.vue b/src/pages/Travel/TravelList.vue
index edcfbac52..257586367 100644
--- a/src/pages/Travel/TravelList.vue
+++ b/src/pages/Travel/TravelList.vue
@@ -100,6 +100,7 @@ onMounted(async () => {
                             <VnLv :label="t('globals.shipped')">
                                 <template #value>
                                     <QBadge
+                                        text-color="black"
                                         v-if="getDateQBadgeColor(row.shipped)"
                                         :color="getDateQBadgeColor(row.shipped)"
                                         class="q-ma-none"
@@ -114,6 +115,7 @@ onMounted(async () => {
                             <VnLv :label="t('globals.landed')">
                                 <template #value>
                                     <QBadge
+                                        text-color="black"
                                         v-if="getDateQBadgeColor(row.landed)"
                                         :color="getDateQBadgeColor(row.landed)"
                                         class="q-ma-none"
diff --git a/src/pages/Worker/Card/WorkerSummary.vue b/src/pages/Worker/Card/WorkerSummary.vue
index 9d0e18b15..dad21cda5 100644
--- a/src/pages/Worker/Card/WorkerSummary.vue
+++ b/src/pages/Worker/Card/WorkerSummary.vue
@@ -8,6 +8,7 @@ import VnLv from 'src/components/ui/VnLv.vue';
 import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue';
 import CardSummary from 'components/ui/CardSummary.vue';
 import VnUserLink from 'src/components/ui/VnUserLink.vue';
+import VnTitle from 'src/components/common/VnTitle.vue';
 
 const route = useRoute();
 const { t } = useI18n();
@@ -71,10 +72,10 @@ const filter = {
         </template>
         <template #body="{ entity: worker }">
             <QCard class="vn-one">
-                <a class="header header-link" :href="workerUrl + `basic-data`">
-                    {{ t('worker.summary.basicData') }}
-                    <QIcon name="open_in_new" />
-                </a>
+                <VnTitle
+                    :url="workerUrl + `basic-data`"
+                    :text="t('worker.summary.basicData')"
+                />
                 <VnLv :label="t('worker.card.name')" :value="worker.user.nickname" />
                 <VnLv
                     :label="t('worker.list.department')"
@@ -111,9 +112,7 @@ const filter = {
                 <VnLv :label="t('worker.summary.locker')" :value="worker.locker" />
             </QCard>
             <QCard class="vn-one">
-                <div class="header header-link">
-                    {{ t('worker.summary.userData') }}
-                </div>
+                <VnTitle :text="t('worker.summary.userData')" />
                 <VnLv :label="t('worker.summary.userId')" :value="worker.user.id" />
                 <VnLv :label="t('worker.card.name')" :value="worker.user.nickname" />
                 <VnLv :label="t('worker.summary.role')" :value="worker.user.role.name" />

From 0fe0a6385bc26465a104b6264bda80759563aaf6 Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Fri, 15 Mar 2024 10:43:25 +0100
Subject: [PATCH 30/42] hotFix(Readme): change bun to pnpm

---
 README.md | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/README.md b/README.md
index 86fe11ca7..e87a84d60 100644
--- a/README.md
+++ b/README.md
@@ -5,13 +5,13 @@ Lilium frontend
 ## Install the dependencies
 
 ```bash
-bun install
+pnpm install
 ```
 
 ### Install quasar cli
 
 ```bash
-sudo bun install -g @quasar/cli
+sudo npm install -g @quasar/cli
 ```
 
 ### Start the app in development mode (hot-code reloading, error reporting, etc.)
@@ -23,13 +23,13 @@ quasar dev
 ### Run unit tests
 
 ```bash
-bun run test:unit
+pnpm run test:unit
 ```
 
 ### Run e2e tests
 
 ```bash
-npm run test:e2e
+pnpm run test:e2e
 ```
 
 ### Build the app for production

From 445d95057a7395dacbea495dd0daa0810df11bcd Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Fri, 15 Mar 2024 12:42:08 +0100
Subject: [PATCH 31/42] refs #6930 feat: trycatchfinally logout

---
 src/composables/useSession.js | 69 ++++++++++++++++++++++-------------
 1 file changed, 43 insertions(+), 26 deletions(-)

diff --git a/src/composables/useSession.js b/src/composables/useSession.js
index 580efbdf8..507e63c0f 100644
--- a/src/composables/useSession.js
+++ b/src/composables/useSession.js
@@ -2,9 +2,11 @@ import { useState } from './useState';
 import { useRole } from './useRole';
 import { useUserConfig } from './useUserConfig';
 import axios from 'axios';
-
+import useNotify from './useNotify';
 
 export function useSession() {
+    const { notify } = useNotify();
+
     function getToken() {
         const localToken = localStorage.getItem('token');
         const sessionToken = sessionStorage.getItem('token');
@@ -29,37 +31,52 @@ export function useSession() {
     }
 
     async function destroy() {
-          if (localStorage.getItem('tokenMultimedia')){
-            await axios.post('VnUsers/logoutMultimedia',  null, {
-                headers: {Authorization: localStorage.getItem('tokenMultimedia') }
-            });
-            localStorage.removeItem('tokenMultimedia')
-
+        if (localStorage.getItem('tokenMultimedia')) {
+            try {
+                await axios.post('Accounts/logout', null, {
+                    headers: { Authorization: localStorage.getItem('tokenMultimedia') },
+                });
+            } catch (error) {
+                notify('errors.userConfig', 'negative');
+            } finally {
+                localStorage.removeItem('tokenMultimedia');
+            }
         }
-        if (localStorage.getItem('token')){
-            await axios.post('VnUsers/logout',  null, {
-                headers: {Authorization: localStorage.getItem('token') }
-            });
-            localStorage.removeItem('token')
+        if (localStorage.getItem('token')) {
+            try {
+                await axios.post('VnUsers/logout', null, {
+                    headers: { Authorization: localStorage.getItem('token') },
+                });
+            } catch (error) {
+                notify('errors.userConfig', 'negative');
+            } finally {
+                localStorage.removeItem('token');
+            }
         }
 
-
-         if (sessionStorage.getItem('tokenMultimedia')){
-            await axios.post('VnUsers/logoutMultimedia',  null, {
-                headers: {Authorization: sessionStorage.getItem('tokenMultimedia') }
-            });
-            sessionStorage.removeItem('tokenMultimedia')
-
+        if (sessionStorage.getItem('tokenMultimedia')) {
+            try {
+                await axios.post('Accounts/logout', null, {
+                    headers: { Authorization: sessionStorage.getItem('tokenMultimedia') },
+                });
+            } catch (error) {
+                notify('errors.userConfig', 'negative');
+            } finally {
+                sessionStorage.removeItem('tokenMultimedia');
+            }
         }
-        if (sessionStorage.getItem('token')){
-            await axios.post('VnUsers/logout',  null, {
-                headers: {Authorization: sessionStorage.getItem('token') }
-            });
-            sessionStorage.removeItem('token')
+        if (sessionStorage.getItem('token')) {
+            try {
+                await axios.post('VnUsers/logout', null, {
+                    headers: { Authorization: sessionStorage.getItem('token') },
+                });
+            } catch (error) {
+                notify('errors.userConfig', 'negative');
+            } finally {
+                sessionStorage.removeItem('token');
+            }
         }
 
-
-
         const { setUser } = useState();
 
         setUser({

From 0d3d002baf5265acbb226270ea5ac2d25e76e282 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Fri, 15 Mar 2024 12:43:56 +0100
Subject: [PATCH 32/42] refs #6930 perf: replace label

---
 src/composables/useSession.js | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/composables/useSession.js b/src/composables/useSession.js
index 507e63c0f..aa20dde20 100644
--- a/src/composables/useSession.js
+++ b/src/composables/useSession.js
@@ -37,7 +37,7 @@ export function useSession() {
                     headers: { Authorization: localStorage.getItem('tokenMultimedia') },
                 });
             } catch (error) {
-                notify('errors.userConfig', 'negative');
+                notify('errors.statusUnauthorized', 'negative');
             } finally {
                 localStorage.removeItem('tokenMultimedia');
             }
@@ -48,7 +48,7 @@ export function useSession() {
                     headers: { Authorization: localStorage.getItem('token') },
                 });
             } catch (error) {
-                notify('errors.userConfig', 'negative');
+                notify('errors.statusUnauthorized', 'negative');
             } finally {
                 localStorage.removeItem('token');
             }
@@ -60,7 +60,7 @@ export function useSession() {
                     headers: { Authorization: sessionStorage.getItem('tokenMultimedia') },
                 });
             } catch (error) {
-                notify('errors.userConfig', 'negative');
+                notify('errors.statusUnauthorized', 'negative');
             } finally {
                 sessionStorage.removeItem('tokenMultimedia');
             }
@@ -71,7 +71,7 @@ export function useSession() {
                     headers: { Authorization: sessionStorage.getItem('token') },
                 });
             } catch (error) {
-                notify('errors.userConfig', 'negative');
+                notify('errors.statusUnauthorized', 'negative');
             } finally {
                 sessionStorage.removeItem('token');
             }

From 63d195cbbf0de82a9bc1236bc0671b5dd296e914 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Fri, 15 Mar 2024 13:21:26 +0100
Subject: [PATCH 33/42] refs #6930 perf: refactor destroy method

---
 src/composables/useSession.js | 65 +++++++++++------------------------
 1 file changed, 20 insertions(+), 45 deletions(-)

diff --git a/src/composables/useSession.js b/src/composables/useSession.js
index aa20dde20..45d439b46 100644
--- a/src/composables/useSession.js
+++ b/src/composables/useSession.js
@@ -29,52 +29,27 @@ export function useSession() {
             sessionStorage.setItem('tokenMultimedia', data.tokenMultimedia);
         }
     }
-
+    async function destroyToken(url, storage, key) {
+        if (storage.getItem(key)) {
+            try {
+                await axios.post(url, null, {
+                    headers: { Authorization: storage.getItem(key) },
+                });
+            } catch (error) {
+                notify('errors.statusUnauthorized', 'negative');
+            } finally {
+                storage.removeItem(key);
+            }
+        }
+    }
     async function destroy() {
-        if (localStorage.getItem('tokenMultimedia')) {
-            try {
-                await axios.post('Accounts/logout', null, {
-                    headers: { Authorization: localStorage.getItem('tokenMultimedia') },
-                });
-            } catch (error) {
-                notify('errors.statusUnauthorized', 'negative');
-            } finally {
-                localStorage.removeItem('tokenMultimedia');
-            }
-        }
-        if (localStorage.getItem('token')) {
-            try {
-                await axios.post('VnUsers/logout', null, {
-                    headers: { Authorization: localStorage.getItem('token') },
-                });
-            } catch (error) {
-                notify('errors.statusUnauthorized', 'negative');
-            } finally {
-                localStorage.removeItem('token');
-            }
-        }
-
-        if (sessionStorage.getItem('tokenMultimedia')) {
-            try {
-                await axios.post('Accounts/logout', null, {
-                    headers: { Authorization: sessionStorage.getItem('tokenMultimedia') },
-                });
-            } catch (error) {
-                notify('errors.statusUnauthorized', 'negative');
-            } finally {
-                sessionStorage.removeItem('tokenMultimedia');
-            }
-        }
-        if (sessionStorage.getItem('token')) {
-            try {
-                await axios.post('VnUsers/logout', null, {
-                    headers: { Authorization: sessionStorage.getItem('token') },
-                });
-            } catch (error) {
-                notify('errors.statusUnauthorized', 'negative');
-            } finally {
-                sessionStorage.removeItem('token');
-            }
+        const tokens = {
+            tokenMultimedia: 'Accounts/logout',
+            token: 'VnUsers/logout',
+        };
+        for (const [key, url] of Object.entries(tokens)) {
+            await destroyToken(url, localStorage, key);
+            await destroyToken(url, sessionStorage, key);
         }
 
         const { setUser } = useState();

From 55ef37584a9f3b87a461048c4326bee385123a52 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Fri, 15 Mar 2024 14:07:28 +0100
Subject: [PATCH 34/42] refs #6993 fix test front

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

diff --git a/test/vitest/__tests__/components/Paginate.spec.js b/test/vitest/__tests__/components/Paginate.spec.js
index 2a22ce438..f3bf01489 100644
--- a/test/vitest/__tests__/components/Paginate.spec.js
+++ b/test/vitest/__tests__/components/Paginate.spec.js
@@ -57,13 +57,13 @@ describe('VnPaginate', () => {
 
             await vm.paginate();
 
-            expect(vm.store.skip).toEqual(3);
-            expect(vm.store.data.length).toEqual(6);
+            expect(vm.store.skip).toEqual(6);
+            expect(vm.store.data.length).toEqual(9);
 
             await vm.paginate();
 
-            expect(vm.store.skip).toEqual(6);
-            expect(vm.store.data.length).toEqual(9);
+            expect(vm.store.skip).toEqual(9);
+            expect(vm.store.data.length).toEqual(12);
         });
     });
 

From 62253e228270744980194554a47e278d9e86f1ae Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Mon, 18 Mar 2024 13:33:02 +0100
Subject: [PATCH 35/42] refs #6546 test(VnPaginate) skip

---
 test/vitest/__tests__/components/Paginate.spec.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/test/vitest/__tests__/components/Paginate.spec.js b/test/vitest/__tests__/components/Paginate.spec.js
index 2a22ce438..4b87a6ea3 100644
--- a/test/vitest/__tests__/components/Paginate.spec.js
+++ b/test/vitest/__tests__/components/Paginate.spec.js
@@ -2,7 +2,7 @@ import { vi, describe, expect, it, beforeAll, afterEach } from 'vitest';
 import { createWrapper, axios } from 'app/test/vitest/helper';
 import VnPaginate from 'src/components/ui/VnPaginate.vue';
 
-describe('VnPaginate', () => {
+describe.skip('VnPaginate', () => {
     const expectedUrl = '/api/customers';
 
     let vm;

From 9094ae8c883e9cd89602c181c3a6cc53d9a7607f Mon Sep 17 00:00:00 2001
From: alexm <alexm@verdnatura.es>
Date: Mon, 18 Mar 2024 15:02:50 +0100
Subject: [PATCH 36/42] test(VnPaginate) fix

---
 test/vitest/__tests__/components/Paginate.spec.js | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/test/vitest/__tests__/components/Paginate.spec.js b/test/vitest/__tests__/components/Paginate.spec.js
index dcd74c72b..cf5e5d2ea 100644
--- a/test/vitest/__tests__/components/Paginate.spec.js
+++ b/test/vitest/__tests__/components/Paginate.spec.js
@@ -2,7 +2,7 @@ import { vi, describe, expect, it, beforeAll, afterEach } from 'vitest';
 import { createWrapper, axios } from 'app/test/vitest/helper';
 import VnPaginate from 'src/components/ui/VnPaginate.vue';
 
-describe.skip('VnPaginate', () => {
+describe('VnPaginate', () => {
     const expectedUrl = '/api/customers';
 
     let vm;
@@ -49,6 +49,8 @@ describe.skip('VnPaginate', () => {
                 ],
             });
             vm.arrayData.hasMoreData.value = true;
+            await vm.$nextTick();
+
             vm.store.data = [
                 { id: 1, name: 'Tony Stark' },
                 { id: 2, name: 'Jessica Jones' },
@@ -57,13 +59,13 @@ describe.skip('VnPaginate', () => {
 
             await vm.paginate();
 
-            expect(vm.store.skip).toEqual(6);
-            expect(vm.store.data.length).toEqual(9);
+            expect(vm.store.skip).toEqual(3);
+            expect(vm.store.data.length).toEqual(6);
 
             await vm.paginate();
 
-            expect(vm.store.skip).toEqual(9);
-            expect(vm.store.data.length).toEqual(12);
+            expect(vm.store.skip).toEqual(6);
+            expect(vm.store.data.length).toEqual(9);
         });
     });
 

From 22eef14bb3fb4a976e3a76524ecbcb4979acda85 Mon Sep 17 00:00:00 2001
From: pablone <pablone@verdnatura.es>
Date: Tue, 19 Mar 2024 11:15:54 +0100
Subject: [PATCH 37/42] feat(light theme): refs #7001 add light theme

---
 src/components/LeftMenu.vue                   |  2 +-
 src/components/UserPanel.vue                  |  2 +-
 src/components/common/VnLog.vue               |  2 +-
 src/components/common/VnTitle.vue             |  6 +---
 src/components/ui/VnSearchbar.vue             |  7 +++-
 src/components/ui/VnSubToolbar.vue            |  2 +-
 src/css/app.scss                              | 30 ++++++++++------
 src/layouts/MainLayout.vue                    |  2 --
 src/layouts/OutLayout.vue                     |  2 +-
 src/pages/Claim/ClaimList.vue                 |  1 -
 src/pages/Customer/CustomerList.vue           |  1 -
 .../Customer/Defaulter/CustomerDefaulter.vue  |  2 +-
 src/pages/Dashboard/DashboardMain.vue         |  6 +++-
 src/pages/Entry/EntryLatestBuys.vue           |  2 +-
 src/pages/InvoiceIn/InvoiceInList.vue         |  1 -
 src/pages/InvoiceOut/InvoiceOutList.vue       | 36 +------------------
 src/pages/Order/Card/OrderCatalogItem.vue     |  2 +-
 src/pages/Route/RouteList.vue                 |  2 +-
 src/pages/Route/RouteRoadmap.vue              | 17 +++++----
 src/pages/Route/RouteTickets.vue              |  6 ++--
 .../Shelving/Card/ShelvingDescriptorMenu.vue  | 17 +++++----
 src/pages/Travel/ExtraCommunity.vue           |  2 +-
 src/pages/Travel/TravelList.vue               |  2 --
 src/pages/Wagon/Type/WagonTypeList.vue        |  1 -
 src/pages/Wagon/WagonList.vue                 |  1 -
 src/pages/Worker/WorkerList.vue               |  1 -
 26 files changed, 64 insertions(+), 91 deletions(-)

diff --git a/src/components/LeftMenu.vue b/src/components/LeftMenu.vue
index 1721adfec..278a67129 100644
--- a/src/components/LeftMenu.vue
+++ b/src/components/LeftMenu.vue
@@ -234,6 +234,6 @@ async function togglePinned(item, event) {
     max-width: 256px;
 }
 .header {
-    color: #999999;
+    color: var(--vn-label-color);
 }
 </style>
diff --git a/src/components/UserPanel.vue b/src/components/UserPanel.vue
index a18dad79a..007d595cc 100644
--- a/src/components/UserPanel.vue
+++ b/src/components/UserPanel.vue
@@ -87,7 +87,7 @@ function copyUserToken() {
 </script>
 
 <template>
-    <QMenu anchor="bottom left">
+    <QMenu anchor="bottom left" class="bg-vn-section-color">
         <div class="row no-wrap q-pa-md">
             <div class="column panel">
                 <div class="text-h6 q-mb-md">
diff --git a/src/components/common/VnLog.vue b/src/components/common/VnLog.vue
index 732f0e964..0c8a70092 100644
--- a/src/components/common/VnLog.vue
+++ b/src/components/common/VnLog.vue
@@ -837,7 +837,7 @@ setLogTree();
         max-width: 400px;
 
         & > .header {
-            color: $dark;
+            color: var(--vn-section-color);
             overflow: hidden;
             white-space: nowrap;
             text-overflow: ellipsis;
diff --git a/src/components/common/VnTitle.vue b/src/components/common/VnTitle.vue
index 8e994eb92..e772f3e37 100644
--- a/src/components/common/VnTitle.vue
+++ b/src/components/common/VnTitle.vue
@@ -8,7 +8,7 @@ const $props = defineProps({
 <template>
     <div class="titleBox">
         <div class="header-link">
-            <a :href="$props.url" :class="$props.url ? 'link' : 'normalTitle'">
+            <a :href="$props.url" :class="$props.url ? 'link' : 'color-vn-text'">
                 {{ $props.text }}
                 <QIcon v-if="url" :name="$props.icon" />
             </a>
@@ -16,10 +16,6 @@ const $props = defineProps({
     </div>
 </template>
 <style scoped lang="scss">
-.normalTitle {
-    color: whitesmoke;
-    cursor: auto;
-}
 a {
     font-size: large;
 }
diff --git a/src/components/ui/VnSearchbar.vue b/src/components/ui/VnSearchbar.vue
index 371c12397..143efcd0f 100644
--- a/src/components/ui/VnSearchbar.vue
+++ b/src/components/ui/VnSearchbar.vue
@@ -163,7 +163,12 @@ async function search() {
 }
 #searchbar {
     .q-field--standout.q-field--highlighted .q-field__control {
-        background-color: var(--vn-text-color);
+        background-color: white;
+        color: black;
+        .q-field__native,
+        .q-icon {
+            color: black !important;
+        }
     }
 }
 </style>
diff --git a/src/components/ui/VnSubToolbar.vue b/src/components/ui/VnSubToolbar.vue
index 1a6549856..018447057 100644
--- a/src/components/ui/VnSubToolbar.vue
+++ b/src/components/ui/VnSubToolbar.vue
@@ -14,7 +14,7 @@ onUnmounted(() => {
 </script>
 
 <template>
-    <QToolbar class="bg-vn-dark justify-end sticky">
+    <QToolbar class="justify-end sticky">
         <slot name="st-data">
             <div id="st-data"></div>
         </slot>
diff --git a/src/css/app.scss b/src/css/app.scss
index 5692cacaf..34d7b0a42 100644
--- a/src/css/app.scss
+++ b/src/css/app.scss
@@ -5,21 +5,30 @@ body.body--light {
     --font-color: black;
     --vn-section-color: #e0e0e0;
     --vn-page-color: #ffffff;
-    background-color: #ffffff;
-    .q-header .q-toolbar {
-        color: var(--font-color);
-    }
     --vn-text-color: var(--font-color);
     --vn-label-color: #5f5f5f;
     --vn-accent-color: #e7e3e3;
+
+    background-color: var(--vn-page-color);
+
+    .q-header .q-toolbar {
+        color: var(--font-color);
+    }
+    .q-card,
+    .q-table,
+    .q-table__bottom,
+    .q-drawer {
+        background-color: var(--vn-section-color);
+    }
 }
 
 body.body--dark {
     --vn-section-color: #403c3c;
-    background-color: #222;
     --vn-text-color: white;
     --vn-label-color: #a8a8a8;
     --vn-accent-color: #424242;
+
+    background-color: #222;
 }
 
 a {
@@ -34,6 +43,9 @@ a {
 .tx-color-link {
     color: $color-link !important;
 }
+.tx-color-font {
+    color: $color-link !important;
+}
 
 .header-link {
     color: $color-link !important;
@@ -61,11 +73,7 @@ select:-webkit-autofill {
     background-clip: text !important;
 }
 
-.bg-vn-dark {
-    background-color: var(--vn-section-color);
-}
-
-.bg-vn-dark {
+.bg-vn-section-color {
     background-color: var(--vn-section-color);
 }
 
@@ -98,7 +106,7 @@ select:-webkit-autofill {
 
 .vn-table-separation-row {
     height: 16px !important;
-    background-color: var(--vn-gray) !important;
+    background-color: var(--vn-section-color) !important;
 }
 
 /* Estilo para el asterisco en campos requeridos */
diff --git a/src/layouts/MainLayout.vue b/src/layouts/MainLayout.vue
index 021ee685a..88c5ee293 100644
--- a/src/layouts/MainLayout.vue
+++ b/src/layouts/MainLayout.vue
@@ -11,5 +11,3 @@ const quasar = useQuasar();
         <QFooter v-if="quasar.platform.is.mobile"></QFooter>
     </QLayout>
 </template>
-
-<style lang="scss" scoped></style>
diff --git a/src/layouts/OutLayout.vue b/src/layouts/OutLayout.vue
index f66fcff1f..0eb1329a4 100644
--- a/src/layouts/OutLayout.vue
+++ b/src/layouts/OutLayout.vue
@@ -40,7 +40,7 @@ const langs = ['en', 'es'];
 
 <template>
     <QLayout view="hHh LpR fFf">
-        <QHeader reveal class="bg-vn-dark">
+        <QHeader reveal class="bg-vn-section-color">
             <QToolbar class="justify-end">
                 <QBtn
                     id="switchLanguage"
diff --git a/src/pages/Claim/ClaimList.vue b/src/pages/Claim/ClaimList.vue
index f8ba8d541..6e0a08f2c 100644
--- a/src/pages/Claim/ClaimList.vue
+++ b/src/pages/Claim/ClaimList.vue
@@ -122,7 +122,6 @@ function navigate(event, id) {
                             <QBtn
                                 :label="t('globals.description')"
                                 @click.stop
-                                class="bg-vn-dark"
                                 outline
                                 style="margin-top: 15px"
                             >
diff --git a/src/pages/Customer/CustomerList.vue b/src/pages/Customer/CustomerList.vue
index 56c3749d4..f4b4223fc 100644
--- a/src/pages/Customer/CustomerList.vue
+++ b/src/pages/Customer/CustomerList.vue
@@ -84,7 +84,6 @@ const redirectToCreateView = () => {
                             <QBtn
                                 :label="t('components.smartCard.openCard')"
                                 @click.stop="navigate(row.id)"
-                                class="bg-vn-dark"
                                 outline
                             />
                             <QBtn
diff --git a/src/pages/Customer/Defaulter/CustomerDefaulter.vue b/src/pages/Customer/Defaulter/CustomerDefaulter.vue
index 1dfd331e2..c685615c6 100644
--- a/src/pages/Customer/Defaulter/CustomerDefaulter.vue
+++ b/src/pages/Customer/Defaulter/CustomerDefaulter.vue
@@ -207,7 +207,7 @@ const refreshData = () => {
         </QScrollArea>
     </QDrawer>
 
-    <VnSubToolbar class="bg-vn-dark">
+    <VnSubToolbar>
         <template #st-data>
             <CustomerBalanceDueTotal :amount="balanceDueTotal" />
             <div class="flex items-center q-ml-lg">
diff --git a/src/pages/Dashboard/DashboardMain.vue b/src/pages/Dashboard/DashboardMain.vue
index da6a2cb7d..6da39ce25 100644
--- a/src/pages/Dashboard/DashboardMain.vue
+++ b/src/pages/Dashboard/DashboardMain.vue
@@ -21,7 +21,7 @@ const pinnedModules = computed(() => navigation.getPinnedModules());
         :width="256"
         :breakpoint="1000"
     >
-        <QScrollArea class="fit text-grey-8">
+        <QScrollArea class="fit">
             <LeftMenu />
         </QScrollArea>
     </QDrawer>
@@ -67,6 +67,10 @@ const pinnedModules = computed(() => navigation.getPinnedModules());
 </template>
 
 <style lang="scss" scoped>
+.left-menu {
+    color: var(--vn-font-color);
+}
+
 .flex-container {
     display: flex;
     flex-wrap: wrap;
diff --git a/src/pages/Entry/EntryLatestBuys.vue b/src/pages/Entry/EntryLatestBuys.vue
index bbc4a16cf..876fe6a9e 100644
--- a/src/pages/Entry/EntryLatestBuys.vue
+++ b/src/pages/Entry/EntryLatestBuys.vue
@@ -636,7 +636,7 @@ onUnmounted(() => (stateStore.rightDrawer = false));
         auto-load
         @on-fetch="(data) => (intrastatOptions = data)"
     />
-    <QToolbar class="bg-vn-dark justify-end">
+    <QToolbar class="justify-end">
         <div id="st-data">
             <TableVisibleColumns
                 :all-columns="allColumnNames"
diff --git a/src/pages/InvoiceIn/InvoiceInList.vue b/src/pages/InvoiceIn/InvoiceInList.vue
index 9508c669c..46c95c734 100644
--- a/src/pages/InvoiceIn/InvoiceInList.vue
+++ b/src/pages/InvoiceIn/InvoiceInList.vue
@@ -112,7 +112,6 @@ function navigate(id) {
                             <QBtn
                                 :label="t('components.smartCard.openCard')"
                                 @click.stop="navigate(row.id)"
-                                class="bg-vn-dark"
                                 outline
                                 type="reset"
                             />
diff --git a/src/pages/InvoiceOut/InvoiceOutList.vue b/src/pages/InvoiceOut/InvoiceOutList.vue
index 08f893db2..d2954d77d 100644
--- a/src/pages/InvoiceOut/InvoiceOutList.vue
+++ b/src/pages/InvoiceOut/InvoiceOutList.vue
@@ -123,7 +123,7 @@ const downloadCsv = () => {
             url="InvoiceOuts/filter"
         >
             <template #body="{ rows }">
-                <VnSubToolbar class="bg-vn-dark justify-end">
+                <VnSubToolbar class="justify-end">
                     <template #st-actions>
                         <QBtn
                             @click="downloadCsv()"
@@ -132,39 +132,6 @@ const downloadCsv = () => {
                             :disable="selectedCards.size === 0"
                             :label="t('globals.download')"
                         />
-                        <!-- <QBtnDropdown
-                            class="q-mr-xl"
-                            color="primary"
-                            :disable="!manageCheckboxes && arrayElements.length < 1"
-                            :label="t('globals.download')"
-                            v-else
-                        >
-                            <QList>
-                                <QItem clickable v-close-popup @click="downloadCsv(rows)">
-                                    <QItemSection>
-                                        <QItemLabel>
-                                            {{
-                                                t('globals.allRows', {
-                                                    numberRows: rows.length,
-                                                })
-                                            }}
-                                        </QItemLabel>
-                                    </QItemSection>
-                                </QItem>
-
-                                <QItem clickable v-close-popup @click="downloadCsv(rows)">
-                                    <QItemSection>
-                                        <QItemLabel>
-                                            {{
-                                                t('globals.selectRows', {
-                                                    numberRows: rows.length,
-                                                })
-                                            }}
-                                        </QItemLabel>
-                                    </QItemSection>
-                                </QItem>
-                            </QList>
-                        </QBtnDropdown> -->
                         <QCheckbox
                             left-label
                             :label="t('globals.markAll')"
@@ -220,7 +187,6 @@ const downloadCsv = () => {
                                 <QBtn
                                     :label="t('components.smartCard.openCard')"
                                     @click.stop="navigate(row.id)"
-                                    class="bg-vn-dark"
                                     outline
                                     type="reset"
                                 />
diff --git a/src/pages/Order/Card/OrderCatalogItem.vue b/src/pages/Order/Card/OrderCatalogItem.vue
index 294a9fbc9..0e1005493 100644
--- a/src/pages/Order/Card/OrderCatalogItem.vue
+++ b/src/pages/Order/Card/OrderCatalogItem.vue
@@ -163,7 +163,7 @@ const dialog = ref(null);
     position: absolute;
     bottom: 12px;
     right: 12px;
-    background: linear-gradient($dark, $primary);
+    background: linear-gradient(var(--vn-section-color), $primary);
     border-radius: 50%;
     width: 40px;
     height: 40px;
diff --git a/src/pages/Route/RouteList.vue b/src/pages/Route/RouteList.vue
index d308696e1..94c05ad54 100644
--- a/src/pages/Route/RouteList.vue
+++ b/src/pages/Route/RouteList.vue
@@ -224,7 +224,7 @@ const openTicketsDialog = (id) => {
     <FetchData url="AgencyModes" @on-fetch="(data) => (agencyList = data)" auto-load />
     <FetchData url="Vehicles" @on-fetch="(data) => (vehicleList = data)" auto-load />
     <QPage class="column items-center">
-        <VnSubToolbar class="bg-vn-dark justify-end">
+        <VnSubToolbar class="justify-end">
             <template #st-actions>
                 <QBtn
                     icon="vn:clone"
diff --git a/src/pages/Route/RouteRoadmap.vue b/src/pages/Route/RouteRoadmap.vue
index 3dca18d66..b1d5d4159 100644
--- a/src/pages/Route/RouteRoadmap.vue
+++ b/src/pages/Route/RouteRoadmap.vue
@@ -13,9 +13,9 @@ import RoadmapFilter from 'pages/Route/Roadmap/RoadmapFilter.vue';
 import VnConfirm from 'components/ui/VnConfirm.vue';
 import axios from 'axios';
 import VnInputDate from 'components/common/VnInputDate.vue';
-import {useSummaryDialog} from "composables/useSummaryDialog";
-import RoadmapSummary from "pages/Route/Roadmap/RoadmapSummary.vue";
-import {useRouter} from "vue-router";
+import { useSummaryDialog } from 'composables/useSummaryDialog';
+import RoadmapSummary from 'pages/Route/Roadmap/RoadmapSummary.vue';
+import { useRouter } from 'vue-router';
 
 const stateStore = useStateStore();
 const { t } = useI18n();
@@ -128,7 +128,7 @@ function confirmRemove() {
 }
 
 function navigateToRoadmapSummary(event, row) {
-    router.push({ name: 'RoadmapSummary', params: { id: row.id } })
+    router.push({ name: 'RoadmapSummary', params: { id: row.id } });
 }
 </script>
 
@@ -182,7 +182,7 @@ function navigateToRoadmapSummary(event, row) {
         </QCard>
     </QDialog>
     <QPage class="column items-center">
-        <VnSubToolbar class="bg-vn-dark justify-end">
+        <VnSubToolbar class="justify-end">
             <template #st-actions>
                 <QBtn
                     icon="vn:clone"
@@ -244,7 +244,12 @@ function navigateToRoadmapSummary(event, row) {
                                             name="preview"
                                             size="xs"
                                             color="primary"
-                                            @click.stop="viewSummary(props?.row?.id, RoadmapSummary)"
+                                            @click.stop="
+                                                viewSummary(
+                                                    props?.row?.id,
+                                                    RoadmapSummary
+                                                )
+                                            "
                                             class="cursor-pointer"
                                         >
                                             <QTooltip>{{ t('Preview') }}</QTooltip>
diff --git a/src/pages/Route/RouteTickets.vue b/src/pages/Route/RouteTickets.vue
index 8410232c5..c1ca5507e 100644
--- a/src/pages/Route/RouteTickets.vue
+++ b/src/pages/Route/RouteTickets.vue
@@ -15,8 +15,8 @@ import VnConfirm from 'components/ui/VnConfirm.vue';
 import FetchData from 'components/FetchData.vue';
 import { openBuscaman } from 'src/utils/buscaman';
 import SendSmsDialog from 'components/common/SendSmsDialog.vue';
-import RouteSearchbar from "pages/Route/Card/RouteSearchbar.vue";
-import {useStateStore} from "stores/useStateStore";
+import RouteSearchbar from 'pages/Route/Card/RouteSearchbar.vue';
+import { useStateStore } from 'stores/useStateStore';
 
 const { t } = useI18n();
 const quasar = useQuasar();
@@ -257,7 +257,7 @@ const openSmsDialog = async () => {
         </QCard>
     </QDialog>
     <QPage class="column items-center">
-        <QToolbar class="bg-vn-dark justify-end">
+        <QToolbar class="justify-end">
             <div id="st-actions" class="q-pa-sm">
                 <QBtn icon="vn:wand" color="primary" class="q-mr-sm" @click="sortRoutes">
                     <QTooltip>{{ t('Sort routes') }}</QTooltip>
diff --git a/src/pages/Shelving/Card/ShelvingDescriptorMenu.vue b/src/pages/Shelving/Card/ShelvingDescriptorMenu.vue
index 684a46807..16351fdd4 100644
--- a/src/pages/Shelving/Card/ShelvingDescriptorMenu.vue
+++ b/src/pages/Shelving/Card/ShelvingDescriptorMenu.vue
@@ -17,15 +17,14 @@ const quasar = useQuasar();
 const { t } = useI18n();
 
 function confirmRemove() {
-    quasar
-        .dialog({
-            component: VnConfirm,
-            componentProps: {
-                title: t('Confirm deletion'),
-                message: t('Are you sure you want to delete this shelving?'),
-                promise: remove
-            },
-        })
+    quasar.dialog({
+        component: VnConfirm,
+        componentProps: {
+            title: t('Confirm deletion'),
+            message: t('Are you sure you want to delete this shelving?'),
+            promise: remove,
+        },
+    });
 }
 
 async function remove() {
diff --git a/src/pages/Travel/ExtraCommunity.vue b/src/pages/Travel/ExtraCommunity.vue
index e7805a7ed..443a10451 100644
--- a/src/pages/Travel/ExtraCommunity.vue
+++ b/src/pages/Travel/ExtraCommunity.vue
@@ -430,7 +430,7 @@ const handleDragScroll = (event) => {
             />
         </Teleport>
     </template>
-    <VnSubToolbar class="bg-vn-dark justify-end">
+    <VnSubToolbar class="justify-end">
         <template #st-actions>
             <QBtn
                 color="primary"
diff --git a/src/pages/Travel/TravelList.vue b/src/pages/Travel/TravelList.vue
index 257586367..77fb34697 100644
--- a/src/pages/Travel/TravelList.vue
+++ b/src/pages/Travel/TravelList.vue
@@ -141,13 +141,11 @@ onMounted(async () => {
                             <QBtn
                                 :label="t('components.smartCard.clone')"
                                 @click.stop="cloneTravel(row)"
-                                class="bg-vn-dark"
                                 outline
                             />
                             <QBtn
                                 :label="t('addEntry')"
                                 @click.stop="redirectCreateEntryView(row)"
-                                class="bg-vn-dark"
                                 outline
                                 style="margin-top: 15px"
                             />
diff --git a/src/pages/Wagon/Type/WagonTypeList.vue b/src/pages/Wagon/Type/WagonTypeList.vue
index a7c713039..23f56e4c3 100644
--- a/src/pages/Wagon/Type/WagonTypeList.vue
+++ b/src/pages/Wagon/Type/WagonTypeList.vue
@@ -61,7 +61,6 @@ async function remove(row) {
                             <QBtn
                                 :label="t('components.smartCard.openCard')"
                                 @click.stop="navigate(row.id)"
-                                class="bg-vn-dark"
                                 outline
                             />
                             <QBtn
diff --git a/src/pages/Wagon/WagonList.vue b/src/pages/Wagon/WagonList.vue
index 18417f433..a8b6728c3 100644
--- a/src/pages/Wagon/WagonList.vue
+++ b/src/pages/Wagon/WagonList.vue
@@ -80,7 +80,6 @@ async function remove(row) {
                             <QBtn
                                 :label="t('components.smartCard.openCard')"
                                 @click.stop="navigate(row.id)"
-                                class="bg-vn-dark"
                                 outline
                             />
                             <QBtn
diff --git a/src/pages/Worker/WorkerList.vue b/src/pages/Worker/WorkerList.vue
index 5214a9066..b11d531dd 100644
--- a/src/pages/Worker/WorkerList.vue
+++ b/src/pages/Worker/WorkerList.vue
@@ -82,7 +82,6 @@ const redirectToCreateView = () => {
                             <QBtn
                                 :label="t('components.smartCard.openCard')"
                                 @click.stop="navigate(row.id)"
-                                class="bg-vn-dark"
                                 outline
                             />
                             <QBtn

From 2ea6fd2ea6b955ba531aa2adeefe30d1e6087690 Mon Sep 17 00:00:00 2001
From: wbuezas <wbuezas@verdnatura.es>
Date: Tue, 19 Mar 2024 13:50:15 -0300
Subject: [PATCH 38/42] Change vnconfirm export name

---
 src/composables/useVnConfirm.js                        | 2 +-
 src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/composables/useVnConfirm.js b/src/composables/useVnConfirm.js
index 430a290be..76c3f4f28 100644
--- a/src/composables/useVnConfirm.js
+++ b/src/composables/useVnConfirm.js
@@ -1,7 +1,7 @@
 import VnConfirm from 'components/ui/VnConfirm.vue';
 import { useQuasar } from 'quasar';
 
-export function useSummaryDialog() {
+export function useVnConfirm() {
     const quasar = useQuasar();
 
     const openConfirmationModal = (title, message, promise, successFn) => {
diff --git a/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue b/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue
index fc84c042b..10af631bc 100644
--- a/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue
+++ b/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue
@@ -10,7 +10,7 @@ import SendEmailDialog from 'components/common/SendEmailDialog.vue';
 import useNotify from 'src/composables/useNotify';
 import { useSession } from 'src/composables/useSession';
 import { usePrintService } from 'composables/usePrintService';
-import { useSummaryDialog } from 'composables/useVnConfirm';
+import { useVnConfirm } from 'composables/useVnConfirm';
 import axios from 'axios';
 
 const $props = defineProps({
@@ -26,7 +26,7 @@ const session = useSession();
 const token = session.getToken();
 const { t } = useI18n();
 const { openReport, sendEmail } = usePrintService();
-const { openConfirmationModal } = useSummaryDialog();
+const { openConfirmationModal } = useVnConfirm();
 const quasar = useQuasar();
 
 const transferInvoiceDialogRef = ref();

From 25aea39bfcb9200793183b14f135fd8f5777884d Mon Sep 17 00:00:00 2001
From: pablone <pablone@verdnatura.es>
Date: Wed, 20 Mar 2024 08:03:25 +0100
Subject: [PATCH 39/42] fix(spec): refs #7001 fix som e2e spec

---
 test/cypress/integration/invoiceIn/invoiceInList.spec.js | 4 ++--
 test/cypress/integration/worker/workerList.spec.js       | 7 +++++--
 2 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/test/cypress/integration/invoiceIn/invoiceInList.spec.js b/test/cypress/integration/invoiceIn/invoiceInList.spec.js
index ce79dc976..5e2a5aa4c 100644
--- a/test/cypress/integration/invoiceIn/invoiceInList.spec.js
+++ b/test/cypress/integration/invoiceIn/invoiceInList.spec.js
@@ -4,7 +4,7 @@ describe('InvoiceInList', () => {
     const firstChipId =
         ':nth-child(1) > :nth-child(1) > .justify-between > .flex > .q-chip > .q-chip__content';
     const firstDetailBtn = '.q-card:nth-child(1) .q-btn:nth-child(2)';
-    const summaryHeaders = '.summaryBody .header';
+    const summaryHeaders = '.summaryBody .header-link';
     const screen = '.q-page-container > .q-drawer-container > .fullscreen';
 
     beforeEach(() => {
@@ -17,7 +17,7 @@ describe('InvoiceInList', () => {
         cy.get(firstChipId)
             .invoke('text')
             .then((content) => {
-                const id = content.substring(4);
+                const id = content.replace(/\D/g, '');
                 cy.get(firstCard).click();
                 cy.url().should('include', `/invoice-in/${id}/summary`);
             });
diff --git a/test/cypress/integration/worker/workerList.spec.js b/test/cypress/integration/worker/workerList.spec.js
index bc4b2383e..c950f9fed 100644
--- a/test/cypress/integration/worker/workerList.spec.js
+++ b/test/cypress/integration/worker/workerList.spec.js
@@ -16,7 +16,10 @@ describe('WorkerList', () => {
     it('should open the worker summary', () => {
         cy.openListSummary(0);
         cy.get('.summaryHeader div').should('have.text', '1110 - Jessica Jones');
-        cy.get('.summary .header-link').eq(0).invoke('text').should('include', 'Basic data');
-        cy.get('.summary .header-link').eq(1).should('have.text', 'User data');
+        cy.get('.summary .header-link')
+            .eq(0)
+            .invoke('text')
+            .should('include', 'Basic data');
+        cy.get('.summary .header-link').eq(1).should('have.text', 'User data ');
     });
 });

From 2fa5b1303da385ee7bb1e136e6a24b8cab31c61a Mon Sep 17 00:00:00 2001
From: pablone <pablone@verdnatura.es>
Date: Wed, 20 Mar 2024 11:28:50 +0100
Subject: [PATCH 40/42] remove(vnChip): refs #7001 remove component

---
 src/components/common/VnChip.vue              | 10 ++++------
 src/components/common/VnLog.vue               | 13 ++++++-------
 src/components/common/VnSmsDialog.vue         |  5 +++--
 src/components/ui/CardList.vue                | 10 +++-------
 src/components/ui/VnSms.vue                   |  8 ++++----
 src/css/app.scss                              | 12 +++++++++++-
 src/pages/Claim/Card/ClaimLines.vue           | 13 ++++++-------
 src/pages/Claim/Card/ClaimSummary.vue         |  9 +++------
 src/pages/InvoiceIn/Card/InvoiceInSummary.vue |  8 ++++----
 src/pages/Order/OrderLines.vue                | 10 +++-------
 src/pages/Ticket/Card/TicketSummary.vue       |  9 +++------
 11 files changed, 50 insertions(+), 57 deletions(-)

diff --git a/src/components/common/VnChip.vue b/src/components/common/VnChip.vue
index dd2de2c64..bb9b46e71 100644
--- a/src/components/common/VnChip.vue
+++ b/src/components/common/VnChip.vue
@@ -1,13 +1,11 @@
 <script setup>
-const $props = defineProps({
-    color: { type: String, default: null },
+defineProps({
     text: { type: String, default: null },
-    dense: { type: String, default: null },
     textColor: { type: String, default: 'black' },
 });
 </script>
 <template>
-    <QChip :color="$props.color" :text-color="textColor" :dense="$props.dense">{{
-        text
-    }}</QChip>
+    <QChip v-bind="$attrs">
+        <slot>{{ text }}</slot>
+    </QChip>
 </template>
diff --git a/src/components/common/VnLog.vue b/src/components/common/VnLog.vue
index 0c8a70092..5c70b900d 100644
--- a/src/components/common/VnLog.vue
+++ b/src/components/common/VnLog.vue
@@ -14,7 +14,6 @@ import VnJsonValue from '../common/VnJsonValue.vue';
 import FetchData from '../FetchData.vue';
 import VnSelectFilter from './VnSelectFilter.vue';
 import VnUserLink from '../ui/VnUserLink.vue';
-import VnChip from '../common/VnChip.vue';
 
 const stateStore = useStateStore();
 const validationsStore = useValidator();
@@ -444,10 +443,10 @@ setLogTree();
                 >
                     <QItemSection>
                         <QItemLabel class="model-info q-mb-xs" v-if="!byRecord">
-                            <VnChip
+                            <QChip
+                                dense
                                 size="md"
-                                text-color="white"
-                                class="model-name q-mr-xs"
+                                class="model-name q-mr-xs text-white"
                                 v-if="
                                     !(modelLog.changedModel && modelLog.changedModelId) &&
                                     modelLog.model
@@ -456,9 +455,9 @@ setLogTree();
                                     backgroundColor: useColor(modelLog.model),
                                 }"
                                 :title="modelLog.model"
-                                :text="t(modelLog.modelI18n)"
-                                dense="dense"
-                            />
+                            >
+                                {{ t(modelLog.modelI18n) }}
+                            </QChip>
                             <span class="model-id" v-if="modelLog.id"
                                 >#{{ modelLog.id }}</span
                             >
diff --git a/src/components/common/VnSmsDialog.vue b/src/components/common/VnSmsDialog.vue
index 3027b14c7..59021cef4 100644
--- a/src/components/common/VnSmsDialog.vue
+++ b/src/components/common/VnSmsDialog.vue
@@ -4,7 +4,6 @@ import { useI18n } from 'vue-i18n';
 import { useDialogPluginComponent } from 'quasar';
 
 import VnInput from 'src/components/common/VnInput.vue';
-import VnChip from 'src/components/common/VnChip.vue';
 
 const { dialogRef, onDialogOK } = useDialogPluginComponent();
 const { t, availableLocales } = useI18n();
@@ -138,7 +137,9 @@ async function send() {
                         />
                     </template>
                     <template #counter>
-                        <VnChip :color="color" :text="totalLength + '/' + maxLength" />
+                        <QChip :color="color" dense>
+                            {{ totalLength }}/{{ maxLength }}
+                        </QChip>
                     </template>
                 </QInput>
             </QCardSection>
diff --git a/src/components/ui/CardList.vue b/src/components/ui/CardList.vue
index 9779c58eb..e8392b13e 100644
--- a/src/components/ui/CardList.vue
+++ b/src/components/ui/CardList.vue
@@ -1,6 +1,5 @@
 <script setup>
 import { useI18n } from 'vue-i18n';
-import VnChip from 'src/components/common/VnChip.vue';
 
 const { t } = useI18n();
 
@@ -29,12 +28,9 @@ const toggleCardCheck = (item) => {
                         <div class="title text-primary text-weight-bold text-h5">
                             {{ $props.title }}
                         </div>
-                        <VnChip
-                            :text="'ID:' + $props.id"
-                            class="q-chip-color"
-                            outline
-                            size="sm"
-                        />
+                        <QChip class="q-chip-color" outline size="sm">
+                            {{ t('ID') }}: {{ $props.id }}
+                        </QChip>
                     </div>
                     <QCheckbox
                         v-if="showCheckbox"
diff --git a/src/components/ui/VnSms.vue b/src/components/ui/VnSms.vue
index d45eef103..ceb24b2bb 100644
--- a/src/components/ui/VnSms.vue
+++ b/src/components/ui/VnSms.vue
@@ -4,7 +4,6 @@ import { date } from 'quasar';
 import VnPaginate from 'src/components/ui/VnPaginate.vue';
 import VnAvatar from '../ui/VnAvatar.vue';
 import VnUserLink from 'src/components/ui/VnUserLink.vue';
-import VnChip from '../common/VnChip.vue';
 
 const $props = defineProps({
     url: { type: String, default: null },
@@ -90,14 +89,15 @@ function formatNumber(number) {
                                     )
                                 }}</QItemLabel>
                                 <QItemLabel class="row center">
-                                    <VnChip
+                                    <QChip
                                         :color="
                                             row.sms.status == 'OK'
                                                 ? 'positive'
                                                 : 'negative'
                                         "
-                                        :text="row.sms.status"
-                                    />
+                                    >
+                                        {{ row.sms.status }}
+                                    </QChip>
                                 </QItemLabel>
                             </QItemSection>
                         </QItem>
diff --git a/src/css/app.scss b/src/css/app.scss
index 34d7b0a42..f1fc22527 100644
--- a/src/css/app.scss
+++ b/src/css/app.scss
@@ -1,5 +1,10 @@
 // app global css in SCSS form
 @import './icons.scss';
+body {
+    .q-chip__content {
+        color: 'red';
+    }
+}
 
 body.body--light {
     --font-color: black;
@@ -11,7 +16,8 @@ body.body--light {
 
     background-color: var(--vn-page-color);
 
-    .q-header .q-toolbar {
+    .q-header .q-toolbar,
+    .q-chip {
         color: var(--font-color);
     }
     .q-card,
@@ -29,6 +35,10 @@ body.body--dark {
     --vn-accent-color: #424242;
 
     background-color: #222;
+
+    .q-chip {
+        color: black;
+    }
 }
 
 a {
diff --git a/src/pages/Claim/Card/ClaimLines.vue b/src/pages/Claim/Card/ClaimLines.vue
index 248b5a69a..90dd31199 100644
--- a/src/pages/Claim/Card/ClaimLines.vue
+++ b/src/pages/Claim/Card/ClaimLines.vue
@@ -12,7 +12,6 @@ import FetchData from 'components/FetchData.vue';
 import VnDiscount from 'components/common/vnDiscount.vue';
 import ClaimLinesImport from './ClaimLinesImport.vue';
 import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
-import VnChip from 'src/components/common/VnChip.vue';
 
 const quasar = useQuasar();
 const route = useRoute();
@@ -162,16 +161,16 @@ function showImportDialog() {
         <div class="row q-gutter-md">
             <div>
                 {{ t('Amount') }}
-                <VnChip :dense="$q.screen.lt.sm" :text="toCurrency(amount)" />
+                <QChip :dense="$q.screen.lt.sm">
+                    {{ toCurrency(amount) }}
+                </QChip>
             </div>
             <QSeparator dark vertical />
             <div>
                 {{ t('Amount Claimed') }}
-                <VnChip
-                    color="positive"
-                    :dense="$q.screen.lt.sm"
-                    :text="toCurrency(amountClaimed)"
-                />
+                <QChip color="positive" :dense="$q.screen.lt.sm">
+                    {{ toCurrency(amountClaimed) }}
+                </QChip>
             </div>
         </div>
     </Teleport>
diff --git a/src/pages/Claim/Card/ClaimSummary.vue b/src/pages/Claim/Card/ClaimSummary.vue
index 12b650dcf..2d4d5603c 100644
--- a/src/pages/Claim/Card/ClaimSummary.vue
+++ b/src/pages/Claim/Card/ClaimSummary.vue
@@ -12,7 +12,6 @@ import ClaimNotes from 'src/pages/Claim/Card/ClaimNotes.vue';
 import VnUserLink from 'src/components/ui/VnUserLink.vue';
 import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue';
 import VnTitle from 'src/components/common/VnTitle.vue';
-import VnChip from 'src/components/common/VnChip.vue';
 
 const route = useRoute();
 const { t } = useI18n();
@@ -192,11 +191,9 @@ function openDialog(dmsId) {
                 />
                 <VnLv :label="t('claim.summary.state')">
                     <template #value>
-                        <VnChip
-                            :color="stateColor(claim.claimState.code)"
-                            :text="claim.claimState.description"
-                            dense="dense"
-                        />
+                        <QChip color="stateColor(claim.claimState.code)" dense>
+                            {{ claim.claimState.description }}</QChip
+                        >
                     </template>
                 </VnLv>
                 <VnLv :label="t('globals.salesPerson')">
diff --git a/src/pages/InvoiceIn/Card/InvoiceInSummary.vue b/src/pages/InvoiceIn/Card/InvoiceInSummary.vue
index e30d9151c..fc15d3bd9 100644
--- a/src/pages/InvoiceIn/Card/InvoiceInSummary.vue
+++ b/src/pages/InvoiceIn/Card/InvoiceInSummary.vue
@@ -6,7 +6,6 @@ import { toCurrency, toDate } from 'src/filters';
 import { getUrl } from 'src/composables/getUrl';
 import CardSummary from 'components/ui/CardSummary.vue';
 import VnLv from 'src/components/ui/VnLv.vue';
-import VnChip from 'src/components/common/VnChip.vue';
 import VnTitle from 'src/components/common/VnTitle.vue';
 
 onMounted(async () => {
@@ -302,7 +301,7 @@ function getLink(param) {
                         />
                         <VnLv :label="t('invoiceIn.summary.dueTotal')">
                             <template #value>
-                                <VnChip
+                                <QChip
                                     dense
                                     class="q-pa-xs"
                                     :color="amountsNotMatch ? 'negative' : 'transparent'"
@@ -311,8 +310,9 @@ function getLink(param) {
                                             ? t('invoiceIn.summary.noMatch')
                                             : t('invoiceIn.summary.dueTotal')
                                     "
-                                    :text="toCurrency(invoiceIn.totals.totalDueDay)"
-                                />
+                                >
+                                    {{ toCurrency(invoiceIn.totals.totalDueDay) }}
+                                </QChip>
                             </template>
                         </VnLv>
                     </div>
diff --git a/src/pages/Order/OrderLines.vue b/src/pages/Order/OrderLines.vue
index 053615fb4..5a31598e1 100644
--- a/src/pages/Order/OrderLines.vue
+++ b/src/pages/Order/OrderLines.vue
@@ -14,7 +14,6 @@ import VnConfirm from 'components/ui/VnConfirm.vue';
 import { toCurrency, toDate } from 'src/filters';
 import { useSession } from 'composables/useSession';
 import axios from 'axios';
-import VnChip from 'src/components/common/VnChip.vue';
 
 const route = useRoute();
 const { t } = useI18n();
@@ -151,12 +150,9 @@ async function confirmOrder() {
                                     >
                                         {{ row?.item?.name }}
                                     </div>
-                                    <VnChip
-                                        class="q-chip-color"
-                                        outline
-                                        size="sm"
-                                        :text="t('ID') + ':' + row.id"
-                                    />
+                                    <QChip class="q-chip-color" outline size="sm">
+                                        {{ t('ID') }}: {{ row.id }}
+                                    </QChip>
                                 </div>
                             </template>
                             <template #list-items>
diff --git a/src/pages/Ticket/Card/TicketSummary.vue b/src/pages/Ticket/Card/TicketSummary.vue
index a7608cae8..85d98f353 100644
--- a/src/pages/Ticket/Card/TicketSummary.vue
+++ b/src/pages/Ticket/Card/TicketSummary.vue
@@ -12,7 +12,6 @@ import VnLv from 'src/components/ui/VnLv.vue';
 import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue';
 import { getUrl } from 'src/composables/getUrl';
 import VnUserLink from 'src/components/ui/VnUserLink.vue';
-import VnChip from 'src/components/common/VnChip.vue';
 import VnTitle from 'src/components/common/VnTitle.vue';
 
 onUpdated(() => summaryRef.value.fetch());
@@ -155,11 +154,9 @@ async function changeState(value) {
                 />
                 <VnLv :label="t('ticket.summary.state')">
                     <template #value>
-                        <VnChip
-                            :color="ticket.ticketState?.state?.classColor ?? 'dark'"
-                            dense="dense"
-                            :text="ticket.ticketState?.state?.name"
-                        />
+                        <QChip :color="ticket.ticketState?.state?.classColor ?? 'dark'">
+                            {{ ticket.ticketState?.state?.name }}
+                        </QChip>
                     </template>
                 </VnLv>
                 <VnLv :label="t('ticket.summary.salesPerson')">

From 5580d1560689d6daf9c53b8e32304ef1dcc63041 Mon Sep 17 00:00:00 2001
From: pablone <pablone@verdnatura.es>
Date: Wed, 20 Mar 2024 11:30:43 +0100
Subject: [PATCH 41/42] remove: refs #7001 custom chip

---
 src/components/common/VnChip.vue      | 11 -----------
 src/pages/Claim/Card/ClaimSummary.vue |  6 +++---
 2 files changed, 3 insertions(+), 14 deletions(-)
 delete mode 100644 src/components/common/VnChip.vue

diff --git a/src/components/common/VnChip.vue b/src/components/common/VnChip.vue
deleted file mode 100644
index bb9b46e71..000000000
--- a/src/components/common/VnChip.vue
+++ /dev/null
@@ -1,11 +0,0 @@
-<script setup>
-defineProps({
-    text: { type: String, default: null },
-    textColor: { type: String, default: 'black' },
-});
-</script>
-<template>
-    <QChip v-bind="$attrs">
-        <slot>{{ text }}</slot>
-    </QChip>
-</template>
diff --git a/src/pages/Claim/Card/ClaimSummary.vue b/src/pages/Claim/Card/ClaimSummary.vue
index 2d4d5603c..612e0d755 100644
--- a/src/pages/Claim/Card/ClaimSummary.vue
+++ b/src/pages/Claim/Card/ClaimSummary.vue
@@ -191,9 +191,9 @@ function openDialog(dmsId) {
                 />
                 <VnLv :label="t('claim.summary.state')">
                     <template #value>
-                        <QChip color="stateColor(claim.claimState.code)" dense>
-                            {{ claim.claimState.description }}</QChip
-                        >
+                        <QChip :color="stateColor(claim.claimState.code)" dense>
+                            {{ claim.claimState.description }}
+                        </QChip>
                     </template>
                 </VnLv>
                 <VnLv :label="t('globals.salesPerson')">

From 1da314e425611b1b34bd4c1c62775522d4ad8c26 Mon Sep 17 00:00:00 2001
From: pablone <pablone@verdnatura.es>
Date: Wed, 20 Mar 2024 11:41:16 +0100
Subject: [PATCH 42/42] fix(claimLines): refs #7001 add style to clear qchip

---
 src/css/app.scss                    | 16 +++++-----------
 src/pages/Claim/Card/ClaimLines.vue |  2 +-
 2 files changed, 6 insertions(+), 12 deletions(-)

diff --git a/src/css/app.scss b/src/css/app.scss
index f1fc22527..c9b489043 100644
--- a/src/css/app.scss
+++ b/src/css/app.scss
@@ -1,10 +1,5 @@
 // app global css in SCSS form
 @import './icons.scss';
-body {
-    .q-chip__content {
-        color: 'red';
-    }
-}
 
 body.body--light {
     --font-color: black;
@@ -16,8 +11,7 @@ body.body--light {
 
     background-color: var(--vn-page-color);
 
-    .q-header .q-toolbar,
-    .q-chip {
+    .q-header .q-toolbar {
         color: var(--font-color);
     }
     .q-card,
@@ -35,10 +29,6 @@ body.body--dark {
     --vn-accent-color: #424242;
 
     background-color: #222;
-
-    .q-chip {
-        color: black;
-    }
 }
 
 a {
@@ -124,6 +114,10 @@ select:-webkit-autofill {
     content: ' *';
 }
 
+.q-chip {
+    color: black;
+}
+
 input[type='number'] {
     -moz-appearance: textfield;
 }
diff --git a/src/pages/Claim/Card/ClaimLines.vue b/src/pages/Claim/Card/ClaimLines.vue
index 90dd31199..70c257c69 100644
--- a/src/pages/Claim/Card/ClaimLines.vue
+++ b/src/pages/Claim/Card/ClaimLines.vue
@@ -161,7 +161,7 @@ function showImportDialog() {
         <div class="row q-gutter-md">
             <div>
                 {{ t('Amount') }}
-                <QChip :dense="$q.screen.lt.sm">
+                <QChip :dense="$q.screen.lt.sm" text-color="white">
                     {{ toCurrency(amount) }}
                 </QChip>
             </div>