From baf1c56b56063c977faf6f89862d898d6fd6ff37 Mon Sep 17 00:00:00 2001
From: jtubau <jtubau@verdnatura.es>
Date: Mon, 10 Mar 2025 08:13:39 +0100
Subject: [PATCH 1/6] fix: agency list filters

---
 src/components/VnTable/VnFilter.vue           |  3 +-
 src/components/VnTable/VnTable.vue            | 16 +++++++++--
 src/pages/Route/Agency/AgencyList.vue         | 28 ++++++++++---------
 src/pages/Route/Agency/Card/AgencySummary.vue | 12 ++++----
 src/pages/Route/Agency/locale/en.yml          |  7 +++--
 src/pages/Route/Agency/locale/es.yml          |  5 ++--
 src/pages/Route/locale/en.yml                 |  4 ++-
 src/pages/Route/locale/es.yml                 |  4 ++-
 8 files changed, 50 insertions(+), 29 deletions(-)

diff --git a/src/components/VnTable/VnFilter.vue b/src/components/VnTable/VnFilter.vue
index e9660e4c2..82d7c772c 100644
--- a/src/components/VnTable/VnFilter.vue
+++ b/src/components/VnTable/VnFilter.vue
@@ -6,6 +6,7 @@ import VnSelect from 'components/common/VnSelect.vue';
 import VnInput from 'components/common/VnInput.vue';
 import VnInputDate from 'components/common/VnInputDate.vue';
 import VnInputTime from 'components/common/VnInputTime.vue';
+import VnCheckbox from 'components/common/VnCheckbox.vue';
 import VnColumn from 'components/VnTable/VnColumn.vue';
 
 const $props = defineProps({
@@ -106,7 +107,7 @@ const components = {
         },
     },
     checkbox: {
-        component: markRaw(QCheckbox),
+        component: markRaw(VnCheckbox),
         event: updateEvent,
         attrs: {
             class: $props.showTitle ? 'q-py-sm' : 'q-px-md q-py-xs fit',
diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue
index d0c657f8a..d323817b0 100644
--- a/src/components/VnTable/VnTable.vue
+++ b/src/components/VnTable/VnTable.vue
@@ -920,12 +920,24 @@ const rowCtrlClickFunction = computed(() => {
                                                         :row-index="index"
                                                     >
                                                         <VnColumn
-                                                            :column="col"
+                                                            :column="{
+                                                                ...col,
+                                                                disable:
+                                                                    col?.component ===
+                                                                    'checkbox'
+                                                                        ? true
+                                                                        : false,
+                                                            }"
                                                             :row="row"
                                                             :is-editable="false"
                                                             v-model="row[col.name]"
                                                             component-prop="columnField"
-                                                            :show-label="true"
+                                                            :show-label="
+                                                                col?.component ===
+                                                                'checkbox'
+                                                                    ? false
+                                                                    : true
+                                                            "
                                                         />
                                                     </slot>
                                                 </span>
diff --git a/src/pages/Route/Agency/AgencyList.vue b/src/pages/Route/Agency/AgencyList.vue
index 5c2904bf3..c01dd272c 100644
--- a/src/pages/Route/Agency/AgencyList.vue
+++ b/src/pages/Route/Agency/AgencyList.vue
@@ -2,10 +2,13 @@
 import { computed } from 'vue';
 import { useRouter } from 'vue-router';
 import { useI18n } from 'vue-i18n';
+import { useSummaryDialog } from 'src/composables/useSummaryDialog';
 import VnTable from 'components/VnTable/VnTable.vue';
 import VnSection from 'src/components/common/VnSection.vue';
+import AgencySummary from 'pages/Route/Agency/Card/AgencySummary.vue';
 
 const { t } = useI18n();
+const { viewSummary } = useSummaryDialog();
 const router = useRouter();
 const dataKey = 'AgencyList';
 function navigate(id) {
@@ -40,16 +43,22 @@ const columns = computed(() => [
     },
     {
         align: 'left',
-        label: t('isOwn'),
+        label: t('agency.isOwn'),
         name: 'isOwn',
         component: 'checkbox',
+        columnFilter: {
+            inWhere: true,
+        },
         cardVisible: true,
     },
     {
         align: 'left',
-        label: t('isAnyVolumeAllowed'),
+        label: t('agency.isAnyVolumeAllowed'),
         name: 'isAnyVolumeAllowed',
         component: 'checkbox',
+        columnFilter: {
+            inWhere: true,
+        },
         cardVisible: true,
     },
     {
@@ -58,9 +67,10 @@ const columns = computed(() => [
         name: 'tableActions',
         actions: [
             {
-                title: t('Client ticket list'),
+                title: t('globals.pageTitles.summary'),
                 icon: 'preview',
-                action: (row) => navigate(row.id),
+                action: (row) => viewSummary(row?.id, AgencySummary),
+                isPrimary: true,
             },
         ],
     },
@@ -82,7 +92,7 @@ const columns = computed(() => [
             <VnTable
                 :data-key
                 :columns="columns"
-                is-editable="false"
+                :is-editable="false"
                 :right-search="false"
                 :use-model="true"
                 redirect="route/agency"
@@ -103,11 +113,3 @@ const columns = computed(() => [
     justify-content: center;
 }
 </style>
-<i18n>
-    es:
-        isOwn: Tiene propietario
-        isAnyVolumeAllowed: Permite cualquier volumen
-    en:
-        isOwn: Has owner
-        isAnyVolumeAllowed: Allows any volume
-</i18n>
diff --git a/src/pages/Route/Agency/Card/AgencySummary.vue b/src/pages/Route/Agency/Card/AgencySummary.vue
index 71a6d1066..ab274939a 100644
--- a/src/pages/Route/Agency/Card/AgencySummary.vue
+++ b/src/pages/Route/Agency/Card/AgencySummary.vue
@@ -6,29 +6,31 @@ import { useI18n } from 'vue-i18n';
 import CardSummary from 'components/ui/CardSummary.vue';
 import VnLv from 'components/ui/VnLv.vue';
 import VnTitle from 'src/components/common/VnTitle.vue';
+import VnCheckbox from 'components/common/VnCheckbox.vue';
 
+const route = useRoute();
 const $props = defineProps({ id: { type: Number, default: 0 } });
 const { t } = useI18n();
-const entityId = computed(() => $props.id || useRoute().params.id);
+const entityId = computed(() => $props.id || route.params.id);
 </script>
 
 <template>
     <div class="q-pa-md">
-        <CardSummary :url="`Agencies/${entityId}`" data-key="Agency">
+        <CardSummary :url="`Agencies/${entityId}`" data-key="Agency" module-name="Agency">
             <template #header="{ entity: agency }">{{ agency.name }}</template>
             <template #body="{ entity: agency }">
                 <QCard class="vn-one">
                     <VnTitle
-                        :url="`#/agency/${entityId}/basic-data`"
+                        :url="`#/${route.meta.moduleName.toLowerCase()}/agency/${entityId}/basic-data`"
                         :text="t('globals.pageTitles.basicData')"
                     />
                     <VnLv :label="t('globals.name')" :value="agency.name" />
-                    <QCheckbox
+                    <VnCheckbox
                         :label="t('agency.isOwn')"
                         v-model="agency.isOwn"
                         :disable="true"
                     />
-                    <QCheckbox
+                    <VnCheckbox
                         :label="t('agency.isAnyVolumeAllowed')"
                         v-model="agency.isAnyVolumeAllowed"
                         :disable="true"
diff --git a/src/pages/Route/Agency/locale/en.yml b/src/pages/Route/Agency/locale/en.yml
index 93f8b4aaa..78a687f2e 100644
--- a/src/pages/Route/Agency/locale/en.yml
+++ b/src/pages/Route/Agency/locale/en.yml
@@ -1,11 +1,12 @@
 agency:
     search: Search agency
-    searchInfo:    You can search by name
+    searchInfo: You can search by name and by id
     isOwn: Own
     isAnyVolumeAllowed: Any volume allowed
+    removeItem: Agency removed successfully
     notification:
-        removeItemError: Error removing agency
-        removeItem: WorkCenter removed successfully
+        removeItemError: Error removing work center
+        removeItem: Work center removed successfully
     pageTitles:
         agency: Agency
     searchBar:
diff --git a/src/pages/Route/Agency/locale/es.yml b/src/pages/Route/Agency/locale/es.yml
index 1efed0e9c..b6237a9f7 100644
--- a/src/pages/Route/Agency/locale/es.yml
+++ b/src/pages/Route/Agency/locale/es.yml
@@ -1,15 +1,14 @@
 agency:
     search: Buscar agencia
-    searchInfo: Puedes buscar por nombre
+    searchInfo: Puedes buscar por nombre y por id
     isOwn: Propio
     isAnyVolumeAllowed: Cualquier volumen
     removeItem: Agencia eliminada correctamente
     notification:
-        removeItemError: Error al eliminar la agencia
+        removeItemError: Error al eliminar la el centro de trabajo
         removeItem: Centro de trabajo eliminado correctamente
     pageTitles:
         agency: Agencia
     searchBar:
         info: Puedes buscar por nombre o id
         label: Buscar agencia...
-
diff --git a/src/pages/Route/locale/en.yml b/src/pages/Route/locale/en.yml
index cc445f412..1a0e5111b 100644
--- a/src/pages/Route/locale/en.yml
+++ b/src/pages/Route/locale/en.yml
@@ -16,6 +16,8 @@ route:
         shipped: Shipped
         agencyAgreement: Agency agreement
         agencyModeName: Agency route
+        isOwn: Own
+        isAnyVolumeallowed: Any volume allowed
     Worker: Worker
     Agency: Agency
     Vehicle: Vehicle
@@ -54,4 +56,4 @@ route:
             clientFk: Client id
             shipped: Preparation date
             viewCmr: View CMR
-            downloadCmrs: Download CMRs
\ No newline at end of file
+            downloadCmrs: Download CMRs
diff --git a/src/pages/Route/locale/es.yml b/src/pages/Route/locale/es.yml
index 51d43774a..c20cbda9d 100644
--- a/src/pages/Route/locale/es.yml
+++ b/src/pages/Route/locale/es.yml
@@ -16,6 +16,8 @@ route:
         ticketFk: Id ticket
         routeFK: Id ruta
         shipped: Fecha preparación
+        isOwn: Propio
+        isAnyVolumeAllowed: Cualquier volumen
     Worker: Trabajador
     Agency: Agencia
     Vehicle: Vehículo
@@ -55,4 +57,4 @@ route:
             clientFk: Id cliente
             shipped: Fecha preparación
             viewCmr: Ver CMR
-            downloadCmrs: Descargar CMRs
\ No newline at end of file
+            downloadCmrs: Descargar CMRs

From d42b6a643d97346271fd9be9b4b65d03e0386e71 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Tue, 11 Mar 2025 12:43:11 +0100
Subject: [PATCH 2/6] fix: solve problem when discount is 0

---
 src/pages/Ticket/Card/TicketSale.vue | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/src/pages/Ticket/Card/TicketSale.vue b/src/pages/Ticket/Card/TicketSale.vue
index 61b50230a..fd1a3da45 100644
--- a/src/pages/Ticket/Card/TicketSale.vue
+++ b/src/pages/Ticket/Card/TicketSale.vue
@@ -310,7 +310,7 @@ const changeDiscount = async (sale) => {
     }
 };
 
-const updateDiscounts = async (sales, newDiscount = null) => {
+const updateDiscounts = async (sales, newDiscount) => {
     const salesTracking = await fetchSalesTracking();
 
     const someSaleIsPrepared = salesTracking.some((sale) =>
@@ -320,12 +320,11 @@ const updateDiscounts = async (sales, newDiscount = null) => {
     else updateDiscount(sales, newDiscount);
 };
 
-const updateDiscount = async (sales, newDiscount = null) => {
-    const saleIds = sales.map((sale) => sale.id);
-    const _newDiscount = newDiscount || edit.value.discount;
+const updateDiscount = async (sales, newDiscount = 0) => {
+    const salesIds = sales.map(({ id }) => id);
     const params = {
-        salesIds: saleIds,
-        newDiscount: _newDiscount,
+        salesIds,
+        newDiscount,
         manaCode: manaCode.value,
     };
     await axios.post(`Tickets/${route.params.id}/updateDiscount`, params);
@@ -664,6 +663,7 @@ watch(
             selection: 'multiple',
         }"
         :right-search="false"
+        :search-url="false"
         :column-search="false"
         :disable-option="{ card: true }"
         auto-load
@@ -692,7 +692,7 @@ watch(
         </template>
         <template #column-image="{ row }">
             <div class="image-wrapper">
-                <VnImg :id="parseInt(row?.item?.id)" class="rounded" />
+                <VnImg v-if="row.item" :id="parseInt(row?.item?.id)" class="rounded" />
             </div>
         </template>
         <template #column-visible="{ row }">
@@ -740,7 +740,7 @@ watch(
                     {{ row?.item?.subName.toUpperCase() }}
                 </div>
             </div>
-            <FetchedTags :item="row.item" :max-length="6" />
+            <FetchedTags v-if="row.item" :item="row.item" :max-length="6" />
             <QPopupProxy v-if="row.id && isTicketEditable">
                 <VnInput
                     v-model="row.concept"

From bf41ab168d9e5c813cae5efedf9ef55480789008 Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Wed, 12 Mar 2025 08:55:48 +0100
Subject: [PATCH 3/6] feat: add icon deleted

---
 src/components/TicketProblems.vue          | 11 +++++++++++
 src/pages/Ticket/Card/TicketDescriptor.vue |  2 +-
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/src/components/TicketProblems.vue b/src/components/TicketProblems.vue
index 5978f4e21..c11cc2e7b 100644
--- a/src/components/TicketProblems.vue
+++ b/src/components/TicketProblems.vue
@@ -28,6 +28,17 @@ defineProps({ row: { type: Object, required: true } });
                 {{ t('ticketSale.reserved') }}
             </QTooltip>
         </QIcon>
+        <QIcon
+            v-if="row?.isDeleted"
+            color="primary"
+            name="vn:deletedTicket"
+            size="xs"
+            data-cy="ticketDeletedIcon"
+        >
+            <QTooltip>
+                {{ t('Ticket deleted') }}
+            </QTooltip>
+        </QIcon>
         <QIcon
             v-if="row?.hasRisk"
             name="vn:risk"
diff --git a/src/pages/Ticket/Card/TicketDescriptor.vue b/src/pages/Ticket/Card/TicketDescriptor.vue
index 1e585592f..128544343 100644
--- a/src/pages/Ticket/Card/TicketDescriptor.vue
+++ b/src/pages/Ticket/Card/TicketDescriptor.vue
@@ -95,7 +95,7 @@ function ticketFilter(ticket) {
         </template>
         <template #icons="{ entity }">
             <QCardActions class="q-gutter-x-xs">
-                <TicketProblems :row="{ ...entity?.client, ...problems }" />
+                <TicketProblems :row="{ ...entity?.client, ...problems, ...entity }" />
             </QCardActions>
         </template>
         <template #actions="{ entity }">

From 44198ae7a71e3142b636991ffac96dd8cfef5adf Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Wed, 12 Mar 2025 08:56:43 +0100
Subject: [PATCH 4/6] fix: reset rows selected

---
 src/pages/Ticket/Card/TicketSale.vue               | 7 ++++---
 test/cypress/integration/ticket/ticketSale.spec.js | 6 ++++--
 2 files changed, 8 insertions(+), 5 deletions(-)

diff --git a/src/pages/Ticket/Card/TicketSale.vue b/src/pages/Ticket/Card/TicketSale.vue
index fd1a3da45..345427256 100644
--- a/src/pages/Ticket/Card/TicketSale.vue
+++ b/src/pages/Ticket/Card/TicketSale.vue
@@ -186,6 +186,7 @@ const getRowUpdateInputEvents = (sale) => ({
 const resetChanges = async () => {
     arrayData.fetch({ append: false });
     tableRef.value.reload();
+    selectedRows.value = [];
 };
 const changeQuantity = async (sale) => {
     if (!sale.itemFk || sale.quantity == null || sale?.originalQuantity === sale.quantity)
@@ -195,6 +196,7 @@ const changeQuantity = async (sale) => {
     if (await isSalePrepared(sale)) {
         await confirmUpdate(() => updateQuantity(sale));
     } else await updateQuantity(sale);
+    resetChanges();
 };
 
 const updateQuantity = async (sale) => {
@@ -203,7 +205,6 @@ const updateQuantity = async (sale) => {
         sale.isNew = false;
         await axios.post(`Sales/${id}/updateQuantity`, { quantity });
         notify('globals.dataSaved', 'positive');
-        resetChanges();
     } catch (e) {
         const { quantity } = tableRef.value.CrudModelRef.originalData.find(
             (s) => s.id === sale.id,
@@ -235,7 +236,7 @@ const addSale = async (sale) => {
 
     notify('globals.dataSaved', 'positive');
     sale.isNew = false;
-    arrayData.fetch({});
+    resetChanges();
 };
 const changeConcept = async (sale) => {
     if (await isSalePrepared(sale)) {
@@ -473,7 +474,7 @@ const endNewRow = (row) => {
 };
 
 async function confirmUpdate(cb) {
-    await quasar
+    quasar
         .dialog({
             component: VnConfirm,
             componentProps: {
diff --git a/test/cypress/integration/ticket/ticketSale.spec.js b/test/cypress/integration/ticket/ticketSale.spec.js
index 81ea761c4..556b1f433 100644
--- a/test/cypress/integration/ticket/ticketSale.spec.js
+++ b/test/cypress/integration/ticket/ticketSale.spec.js
@@ -1,7 +1,7 @@
 /// <reference types="cypress" />
 
 describe('TicketSale', () => {
-    describe.skip('Free ticket #31', () => {
+    describe('Free ticket #31', () => {
         beforeEach(() => {
             cy.login('developer');
             cy.viewport(1920, 1080);
@@ -44,6 +44,7 @@ describe('TicketSale', () => {
             cy.dataCy('recalculatePriceItem').click();
             cy.wait('@recalculatePrice').its('response.statusCode').should('eq', 200);
             cy.checkNotification('Data saved');
+            cy.dataCy('ticketSaleMoreActionsDropdown').should('be.disabled');
         });
 
         it('should update discount when "Update discount" is clicked', () => {
@@ -58,6 +59,7 @@ describe('TicketSale', () => {
             cy.dataCy('saveManaBtn').click();
             cy.waitForElement('.q-notification__message');
             cy.checkNotification('Data saved');
+            cy.dataCy('ticketSaleMoreActionsDropdown').should('be.disabled');
         });
 
         it('adds claim', () => {
@@ -120,7 +122,7 @@ describe('TicketSale', () => {
             cy.url().should('match', /\/ticket\/31\/log/);
         });
     });
-    describe.skip('Ticket prepared #23', () => {
+    describe('Ticket prepared #23', () => {
         beforeEach(() => {
             cy.login('developer');
             cy.viewport(1920, 1080);

From 9306f88b99643127ad4f064524e0767950f4314e Mon Sep 17 00:00:00 2001
From: Javier Segarra <jsegarra@verdnatura.es>
Date: Wed, 12 Mar 2025 09:59:44 +0100
Subject: [PATCH 5/6] fix: ticketSale

---
 src/pages/Ticket/Card/TicketSale.vue          |  23 +-
 .../integration/ticket/ticketSale.spec.js     | 269 +++++++++---------
 2 files changed, 153 insertions(+), 139 deletions(-)

diff --git a/src/pages/Ticket/Card/TicketSale.vue b/src/pages/Ticket/Card/TicketSale.vue
index 345427256..ece871918 100644
--- a/src/pages/Ticket/Card/TicketSale.vue
+++ b/src/pages/Ticket/Card/TicketSale.vue
@@ -174,14 +174,18 @@ const getSaleTotal = (sale) => {
     return price - discount;
 };
 
-const getRowUpdateInputEvents = (sale) => ({
-    'keyup.enter': () => {
-        changeQuantity(sale);
-    },
-    blur: () => {
-        changeQuantity(sale);
-    },
-});
+const getRowUpdateInputEvents = (sale) => {
+    return {
+        'keyup.enter': (evt) => {
+            console.error(evt);
+            changeQuantity(sale);
+        },
+        blur: (evt) => {
+            console.error(evt);
+            changeQuantity(sale);
+        },
+    };
+};
 
 const resetChanges = async () => {
     arrayData.fetch({ append: false });
@@ -191,12 +195,12 @@ const resetChanges = async () => {
 const changeQuantity = async (sale) => {
     if (!sale.itemFk || sale.quantity == null || sale?.originalQuantity === sale.quantity)
         return;
+    else sale.originalQuantity = sale.quantity;
     if (!sale.id) return addSale(sale);
 
     if (await isSalePrepared(sale)) {
         await confirmUpdate(() => updateQuantity(sale));
     } else await updateQuantity(sale);
-    resetChanges();
 };
 
 const updateQuantity = async (sale) => {
@@ -205,6 +209,7 @@ const updateQuantity = async (sale) => {
         sale.isNew = false;
         await axios.post(`Sales/${id}/updateQuantity`, { quantity });
         notify('globals.dataSaved', 'positive');
+        resetChanges();
     } catch (e) {
         const { quantity } = tableRef.value.CrudModelRef.originalData.find(
             (s) => s.id === sale.id,
diff --git a/test/cypress/integration/ticket/ticketSale.spec.js b/test/cypress/integration/ticket/ticketSale.spec.js
index 556b1f433..61ba9fe4f 100644
--- a/test/cypress/integration/ticket/ticketSale.spec.js
+++ b/test/cypress/integration/ticket/ticketSale.spec.js
@@ -1,141 +1,14 @@
 /// <reference types="cypress" />
+const firstRow = 'tbody > :nth-child(1)';
 
 describe('TicketSale', () => {
-    describe('Free ticket #31', () => {
-        beforeEach(() => {
-            cy.login('developer');
-            cy.viewport(1920, 1080);
-            cy.visit('/#/ticket/31/sale');
-        });
-
-        const firstRow = 'tbody > :nth-child(1)';
-
-        const selectFirstRow = () => {
-            cy.waitForElement(firstRow);
-            cy.get(firstRow).find('.q-checkbox__inner').click();
-        };
-
-        it('it should add item to basket', () => {
-            cy.window().then((win) => {
-                cy.stub(win, 'open').as('windowOpen');
-            });
-            cy.dataCy('ticketSaleAddToBasketBtn').should('exist');
-            cy.dataCy('ticketSaleAddToBasketBtn').click();
-            cy.get('@windowOpen').should('be.calledWithMatch', /\/order\/\d+\/catalog/);
-        });
-
-        it('should send SMS', () => {
-            selectFirstRow();
-            cy.dataCy('ticketSaleMoreActionsDropdown').click();
-            cy.waitForElement('[data-cy="sendShortageSMSItem"]');
-            cy.dataCy('sendShortageSMSItem').should('exist');
-            cy.dataCy('sendShortageSMSItem').click();
-            cy.dataCy('vnSmsDialog').should('exist');
-            cy.dataCy('sendSmsBtn').click();
-            cy.checkNotification('SMS sent');
-        });
-
-        it('should recalculate price when "Recalculate price" is clicked', () => {
-            cy.intercept('POST', '**/recalculatePrice').as('recalculatePrice');
-            selectFirstRow();
-            cy.dataCy('ticketSaleMoreActionsDropdown').click();
-            cy.waitForElement('[data-cy="recalculatePriceItem"]');
-            cy.dataCy('recalculatePriceItem').should('exist');
-            cy.dataCy('recalculatePriceItem').click();
-            cy.wait('@recalculatePrice').its('response.statusCode').should('eq', 200);
-            cy.checkNotification('Data saved');
-            cy.dataCy('ticketSaleMoreActionsDropdown').should('be.disabled');
-        });
-
-        it('should update discount when "Update discount" is clicked', () => {
-            selectFirstRow();
-            cy.dataCy('ticketSaleMoreActionsDropdown').click();
-            cy.waitForElement('[data-cy="updateDiscountItem"]');
-            cy.dataCy('updateDiscountItem').should('exist');
-            cy.dataCy('updateDiscountItem').click();
-            cy.waitForElement('[data-cy="ticketSaleDiscountInput"]');
-            cy.dataCy('ticketSaleDiscountInput').find('input').focus();
-            cy.dataCy('ticketSaleDiscountInput').find('input').type('10');
-            cy.dataCy('saveManaBtn').click();
-            cy.waitForElement('.q-notification__message');
-            cy.checkNotification('Data saved');
-            cy.dataCy('ticketSaleMoreActionsDropdown').should('be.disabled');
-        });
-
-        it('adds claim', () => {
-            selectFirstRow();
-            cy.dataCy('ticketSaleMoreActionsDropdown').click();
-            cy.dataCy('createClaimItem').click();
-            cy.dataCy('VnConfirm_confirm').click();
-            cy.url().should('contain', 'claim/');
-            // Delete created claim to avoid cluttering the database
-            cy.dataCy('descriptor-more-opts').click();
-            cy.dataCy('deleteClaim').click();
-            cy.dataCy('VnConfirm_confirm').click();
-            cy.checkNotification('Data deleted');
-        });
-
-        it('marks row as reserved', () => {
-            selectFirstRow();
-            cy.dataCy('ticketSaleMoreActionsDropdown').click();
-            cy.waitForElement('[data-cy="markAsReservedItem"]');
-            cy.dataCy('markAsReservedItem').click();
-            cy.dataCy('ticketSaleReservedIcon').should('exist');
-        });
-
-        it('unmarks row as reserved', () => {
-            selectFirstRow();
-            cy.dataCy('ticketSaleMoreActionsDropdown').click();
-            cy.waitForElement('[data-cy="unmarkAsReservedItem"]');
-            cy.dataCy('unmarkAsReservedItem').click();
-            cy.dataCy('ticketSaleReservedIcon').should('not.exist');
-        });
-
-        it('refunds row with warehouse', () => {
-            selectFirstRow();
-            cy.dataCy('ticketSaleMoreActionsDropdown').click();
-            cy.dataCy('ticketSaleRefundItem').click();
-            cy.dataCy('ticketSaleRefundWithWarehouse').click();
-            cy.checkNotification('The following refund ticket have been created');
-        });
-
-        it('refunds row without warehouse', () => {
-            selectFirstRow();
-            cy.dataCy('ticketSaleMoreActionsDropdown').click();
-            cy.dataCy('ticketSaleRefundItem').click();
-            cy.dataCy('ticketSaleRefundWithoutWarehouse').click();
-            cy.checkNotification('The following refund ticket have been created');
-        });
-
-        it('transfer sale to a new ticket', () => {
-            cy.visit('/#/ticket/32/sale');
-            cy.get('.q-item > .q-item__label').should('have.text', ' #32');
-            selectFirstRow();
-            cy.dataCy('ticketSaleTransferBtn').click();
-            cy.dataCy('ticketTransferPopup').should('exist');
-            cy.dataCy('ticketTransferNewTicketBtn').click();
-            cy.get('.q-item > .q-item__label').should('not.have.text', ' #32');
-        });
-
-        it('should redirect to ticket logs', () => {
-            cy.get(firstRow).find('.q-btn:last').click();
-            cy.url().should('match', /\/ticket\/31\/log/);
-        });
-    });
-    describe('Ticket prepared #23', () => {
+    describe('Ticket  #23', () => {
         beforeEach(() => {
             cy.login('developer');
             cy.viewport(1920, 1080);
             cy.visit('/#/ticket/23/sale');
         });
 
-        const firstRow = 'tbody > :nth-child(1)';
-
-        const selectFirstRow = () => {
-            cy.waitForElement(firstRow);
-            cy.get(firstRow).find('.q-checkbox__inner').click();
-        };
-
         it('update price', () => {
             const price = Number((Math.random() * 99 + 1).toFixed(2));
             cy.waitForElement(firstRow);
@@ -198,8 +71,144 @@ describe('TicketSale', () => {
                 .should('have.value', `${quantity}`);
         });
     });
-});
+    describe('Ticket to add claim #24', () => {
+        beforeEach(() => {
+            cy.login('developer');
+            cy.viewport(1920, 1080);
+            cy.visit('/#/ticket/24/sale');
+        });
 
+        it('adds claim', () => {
+            selectFirstRow();
+            cy.dataCy('ticketSaleMoreActionsDropdown').click();
+            cy.dataCy('createClaimItem').click();
+            cy.dataCy('VnConfirm_confirm').click();
+            cy.url().should('contain', 'claim/');
+            // Delete created claim to avoid cluttering the database
+            cy.dataCy('descriptor-more-opts').click();
+            cy.dataCy('deleteClaim').click();
+            cy.dataCy('VnConfirm_confirm').click();
+        });
+    });
+    describe('Free ticket #31', () => {
+        beforeEach(() => {
+            cy.login('developer');
+            cy.viewport(1920, 1080);
+            cy.visit('/#/ticket/31/sale');
+        });
+
+        it('it should add item to basket', () => {
+            cy.window().then((win) => {
+                cy.stub(win, 'open').as('windowOpen');
+            });
+            cy.dataCy('ticketSaleAddToBasketBtn').should('exist');
+            cy.dataCy('ticketSaleAddToBasketBtn').click();
+            cy.get('@windowOpen').should('be.calledWithMatch', /\/order\/\d+\/catalog/);
+        });
+
+        it('should send SMS', () => {
+            selectFirstRow();
+            cy.dataCy('ticketSaleMoreActionsDropdown').click();
+            cy.waitForElement('[data-cy="sendShortageSMSItem"]');
+            cy.dataCy('sendShortageSMSItem').should('exist');
+            cy.dataCy('sendShortageSMSItem').click();
+            cy.dataCy('vnSmsDialog').should('exist');
+            cy.dataCy('sendSmsBtn').click();
+            cy.checkNotification('SMS sent');
+        });
+
+        it('should recalculate price when "Recalculate price" is clicked', () => {
+            cy.intercept('POST', '**/recalculatePrice').as('recalculatePrice');
+            selectFirstRow();
+            cy.dataCy('ticketSaleMoreActionsDropdown').click();
+            cy.waitForElement('[data-cy="recalculatePriceItem"]');
+            cy.dataCy('recalculatePriceItem').should('exist');
+            cy.dataCy('recalculatePriceItem').click();
+            cy.wait('@recalculatePrice').its('response.statusCode').should('eq', 200);
+            cy.checkNotification('Data saved');
+            cy.dataCy('ticketSaleMoreActionsDropdown').should('be.disabled');
+        });
+
+        it('should update discount when "Update discount" is clicked', () => {
+            selectFirstRow();
+            cy.dataCy('ticketSaleMoreActionsDropdown').click();
+            cy.waitForElement('[data-cy="updateDiscountItem"]');
+            cy.dataCy('updateDiscountItem').should('exist');
+            cy.dataCy('updateDiscountItem').click();
+            cy.waitForElement('[data-cy="ticketSaleDiscountInput"]');
+            cy.dataCy('ticketSaleDiscountInput').find('input').focus();
+            cy.dataCy('ticketSaleDiscountInput').find('input').type('10');
+            cy.dataCy('saveManaBtn').click();
+            cy.waitForElement('.q-notification__message');
+            cy.checkNotification('Data saved');
+            cy.dataCy('ticketSaleMoreActionsDropdown').should('be.disabled');
+        });
+
+        it('adds claim', () => {
+            selectFirstRow();
+            cy.dataCy('ticketSaleMoreActionsDropdown').click();
+            cy.dataCy('createClaimItem').click();
+            cy.dataCy('VnConfirm_confirm').click();
+            cy.checkNotification('Future ticket date not allowed');
+        });
+
+        it('marks row as reserved', () => {
+            selectFirstRow();
+            cy.dataCy('ticketSaleMoreActionsDropdown').click();
+            cy.waitForElement('[data-cy="markAsReservedItem"]');
+            cy.dataCy('markAsReservedItem').click();
+            cy.dataCy('ticketSaleReservedIcon').should('exist');
+        });
+
+        it('unmarks row as reserved', () => {
+            selectFirstRow();
+            cy.dataCy('ticketSaleMoreActionsDropdown').click();
+            cy.waitForElement('[data-cy="unmarkAsReservedItem"]');
+            cy.dataCy('unmarkAsReservedItem').click();
+            cy.dataCy('ticketSaleReservedIcon').should('not.exist');
+        });
+
+        it('refunds row with warehouse', () => {
+            selectFirstRow();
+            cy.dataCy('ticketSaleMoreActionsDropdown').click();
+            cy.dataCy('ticketSaleRefundItem').click();
+            cy.dataCy('ticketSaleRefundWithWarehouse').click();
+            cy.checkNotification('The following refund ticket have been created');
+        });
+
+        it('refunds row without warehouse', () => {
+            selectFirstRow();
+            cy.dataCy('ticketSaleMoreActionsDropdown').click();
+            cy.dataCy('ticketSaleRefundItem').click();
+            cy.dataCy('ticketSaleRefundWithoutWarehouse').click();
+            cy.checkNotification('The following refund ticket have been created');
+        });
+
+        it('should redirect to ticket logs', () => {
+            cy.get(firstRow).find('.q-btn:last').click();
+            cy.url().should('match', /\/ticket\/31\/log/);
+        });
+    });
+    describe('Ticket to transfer #32', () => {
+        beforeEach(() => {
+            cy.login('developer');
+            cy.viewport(1920, 1080);
+            cy.visit('/#/ticket/32/sale');
+        });
+        it('transfer sale to a new ticket', () => {
+            cy.get('.q-item > .q-item__label').should('have.text', ' #32');
+            selectFirstRow();
+            cy.dataCy('ticketSaleTransferBtn').click();
+            cy.dataCy('ticketTransferPopup').should('exist');
+            cy.dataCy('ticketTransferNewTicketBtn').click();
+            cy.get('.q-item > .q-item__label').should('not.have.text', ' #32');
+        });
+    });
+});
+function selectFirstRow() {
+    cy.waitForElement(firstRow);
+    cy.get(firstRow).find('.q-checkbox__inner').click();
+}
 function handleVnConfirm() {
     cy.get('[data-cy="VnConfirm_confirm"]').click();
     cy.waitForElement('.q-notification__message');

From afb0e912d69edd914a4b5e0d5ca68d475baf5bc6 Mon Sep 17 00:00:00 2001
From: Jon <jon@verdnatura.es>
Date: Wed, 12 Mar 2025 09:46:02 +0100
Subject: [PATCH 6/6] test: fix selectOption wait to ariaControl is visible

---
 test/cypress/integration/client/clientBalance.spec.js |  3 ++-
 test/cypress/support/commands.js                      | 10 ++++++++++
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/test/cypress/integration/client/clientBalance.spec.js b/test/cypress/integration/client/clientBalance.spec.js
index 56ce01692..0228d71bc 100644
--- a/test/cypress/integration/client/clientBalance.spec.js
+++ b/test/cypress/integration/client/clientBalance.spec.js
@@ -6,9 +6,10 @@ describe('Client balance', () => {
         cy.visit('#/customer/1101/balance');
     });
     it('Should create a mandate', () => {
+        cy.waitSpinner();
         cy.get('.q-page-sticky > div > .q-btn').click();
         cy.selectOption('[data-cy="paymentBank"]', 2);
-        cy.dataCy('paymentAmount_input').type('100');
+        cy.dataCy('paymentAmount_input').clear().type('100');
         cy.saveCard();
     });
 });
diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js
index dfec341cd..c2dd1579f 100755
--- a/test/cypress/support/commands.js
+++ b/test/cypress/support/commands.js
@@ -92,6 +92,14 @@ Cypress.Commands.add('getValue', (selector) => {
     });
 });
 
+Cypress.Commands.add('waitSpinner', () => {
+    cy.get('body').then(($body) => {
+        if ($body.find('[data-cy="loading-spinner"]').length) {
+            cy.get('[data-cy="loading-spinner"]').should('not.be.visible');
+        }
+    });
+});
+
 // Fill Inputs
 Cypress.Commands.add('selectOption', (selector, option, timeout = 2500) => {
     cy.waitForElement(selector, timeout);
@@ -109,6 +117,7 @@ Cypress.Commands.add('selectOption', (selector, option, timeout = 2500) => {
 
 function selectItem(selector, option, ariaControl, hasWrite = true) {
     if (!hasWrite) cy.wait(100);
+    cy.waitSpinner();
 
     getItems(ariaControl).then((items) => {
         const matchingItem = items
@@ -128,6 +137,7 @@ function getItems(ariaControl, startTime = Cypress._.now(), timeout = 2500) {
         .should('exist')
         .find('.q-item')
         .should('exist')
+        .should('be.visible')
         .then(($items) => {
             if (!$items?.length || $items.first().text().trim() === '') {
                 if (Cypress._.now() - startTime > timeout) {