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) {