From de398391403bc7ef2c2d38e12974f96967e50c63 Mon Sep 17 00:00:00 2001 From: jtubau Date: Thu, 30 Jan 2025 09:07:08 +0100 Subject: [PATCH 1/5] fix: refs #7322 handle null responses in client, agency and address fetching --- src/pages/Ticket/Card/TicketDescriptorMenu.vue | 10 ++++++---- src/pages/Ticket/TicketCreate.vue | 15 +++++++++------ src/pages/Ticket/TicketCreateDialog.vue | 15 +++++++++------ src/pages/Ticket/TicketList.vue | 15 +++++++++------ 4 files changed, 33 insertions(+), 22 deletions(-) diff --git a/src/pages/Ticket/Card/TicketDescriptorMenu.vue b/src/pages/Ticket/Card/TicketDescriptorMenu.vue index 89f7015d7..63e45c8ab 100644 --- a/src/pages/Ticket/Card/TicketDescriptorMenu.vue +++ b/src/pages/Ticket/Card/TicketDescriptorMenu.vue @@ -69,14 +69,16 @@ const onAddressSelected = (addressId) => { } const fetchClient = async () => { - const { data } = await getClient(client.value) - const [retrievedClient] = data; + const response = await getClient(client.value) + if (!response) return; + const [retrievedClient] = response.data; selectedClient.value = retrievedClient; }; const fetchAddresses = async () => { - const { data } = await getAddresses(client.value); - addressesOptions.value = data; + const response = await getAddresses(client.value); + if (!response) return; + addressesOptions.value = response.data; const { defaultAddress } = selectedClient.value; address.value = defaultAddress.id; diff --git a/src/pages/Ticket/TicketCreate.vue b/src/pages/Ticket/TicketCreate.vue index 4c32448e4..904e04cb3 100644 --- a/src/pages/Ticket/TicketCreate.vue +++ b/src/pages/Ticket/TicketCreate.vue @@ -39,14 +39,16 @@ onBeforeMount(async () => { }); const fetchClient = async (formData) => { - const { data } = await getClient(formData.clientId); - const [client] = data; + const response = await getClient(formData.clientId); + if (!response) return; + const [client] = response.data; selectedClient.value = client; }; const fetchAddresses = async (formData) => { - const { data } = await getAddresses(formData.clientId); - addressesOptions.value = data; + const response = await getAddresses(formData.clientId); + if (!response) return; + addressesOptions.value = response.data; const { defaultAddress } = selectedClient.value; formData.addressId = defaultAddress.id; @@ -58,8 +60,9 @@ const onClientSelected = async (formData) => { }; const fetchAvailableAgencies = async (formData) => { - const { data } = await getAgencies(formData); - agenciesOptions.value = data; + const response = await getAgencies(formData); + if (!response) return; + agenciesOptions.value = response.data; const defaultAgency = agenciesOptions.value.find( (agency) => diff --git a/src/pages/Ticket/TicketCreateDialog.vue b/src/pages/Ticket/TicketCreateDialog.vue index c8b126d7d..7a126d2c2 100644 --- a/src/pages/Ticket/TicketCreateDialog.vue +++ b/src/pages/Ticket/TicketCreateDialog.vue @@ -39,14 +39,16 @@ onBeforeMount(async () => { }); const fetchClient = async (formData) => { - const { data } = await getClient(formData.clientId); - const [client] = data; + const response = await getClient(formData.clientId); + if (!response) return; + const [client] = response.data; selectedClient.value = client; }; const fetchAddresses = async (formData) => { - const { data } = await getAddresses(formData.clientId); - addressesOptions.value = data; + const response = await getAddresses(formData.clientId); + if (!response) return; + addressesOptions.value = response.data; const { defaultAddress } = selectedClient.value; formData.addressId = defaultAddress.id; @@ -58,8 +60,9 @@ const onClientSelected = async (formData) => { }; const fetchAvailableAgencies = async (formData) => { - const { data } = await getAgencies(formData); - agenciesOptions.value = data; + const response = await getAgencies(formData); + if (!response) return; + agenciesOptions.value = response.data; const defaultAgency = agenciesOptions.value.find( (agency) => diff --git a/src/pages/Ticket/TicketList.vue b/src/pages/Ticket/TicketList.vue index 8cf1184eb..208bc4460 100644 --- a/src/pages/Ticket/TicketList.vue +++ b/src/pages/Ticket/TicketList.vue @@ -240,8 +240,9 @@ const onClientSelected = async (formData) => { }; const fetchAvailableAgencies = async (formData) => { - const { data } = await getAgencies(formData); - agenciesOptions.value = data; + const response = await getAgencies(formData); + if (!response) return; + agenciesOptions.value = response.data; const defaultAgency = agenciesOptions.value.find( (agency) => @@ -252,14 +253,16 @@ const fetchAvailableAgencies = async (formData) => { }; const fetchClient = async (formData) => { - const { data } = await getClient(formData.clientId); - const [client] = data; + const response = await getClient(formData.clientId); + if (!response) return; + const [client] = response.data; selectedClient.value = client; }; const fetchAddresses = async (formData) => { - const { data } = await getAddresses(formData.clientId); - addressesOptions.value = data; + const response = await getAddresses(formData.clientId); + if (!response) return; + addressesOptions.value = response.data; const { defaultAddress } = selectedClient.value; formData.addressId = defaultAddress.id; From b10cb9f09fe6bdddb2d9608888f816ade7e88ea7 Mon Sep 17 00:00:00 2001 From: jtubau Date: Fri, 31 Jan 2025 07:55:32 +0100 Subject: [PATCH 2/5] refactor: refs #7322 update getAgencies to handle client and return default agency --- .../Route/Agency/composables/getAgencies.js | 13 ++++++++-- src/pages/Ticket/TicketCreate.vue | 23 +++++++++------- src/pages/Ticket/TicketCreateDialog.vue | 23 +++++++++------- src/pages/Ticket/TicketList.vue | 26 ++++++++++++------- 4 files changed, 55 insertions(+), 30 deletions(-) diff --git a/src/pages/Route/Agency/composables/getAgencies.js b/src/pages/Route/Agency/composables/getAgencies.js index 7299d7e23..dd1370a55 100644 --- a/src/pages/Route/Agency/composables/getAgencies.js +++ b/src/pages/Route/Agency/composables/getAgencies.js @@ -1,12 +1,21 @@ import axios from 'axios'; +import agency from 'src/router/modules/agency'; -export async function getAgencies(formData) { +export async function getAgencies(formData, client) { if (!formData.warehouseId || !formData.addressId || !formData.landed) return; + + let defaultAgency = null; let params = { warehouseFk: formData.warehouseId, addressFk: formData.addressId, landed: formData.landed, }; - return await axios.get('Agencies/getAgenciesWithWarehouse', { params }); + const { data } = await axios.get('Agencies/getAgenciesWithWarehouse', { params }); + + if(data && client) { + defaultAgency = data.find((agency) => agency.agencyModeFk === client.defaultAddress.agencyModeFk ); + }; + + return {options: data, agency: defaultAgency} } diff --git a/src/pages/Ticket/TicketCreate.vue b/src/pages/Ticket/TicketCreate.vue index 904e04cb3..96d200547 100644 --- a/src/pages/Ticket/TicketCreate.vue +++ b/src/pages/Ticket/TicketCreate.vue @@ -38,6 +38,11 @@ onBeforeMount(async () => { await onClientSelected(initialFormState); }); +function resetAgenciesSelector(formData) { + agenciesOptions.value = []; + formData.agencyModeId = null; +} + const fetchClient = async (formData) => { const response = await getClient(formData.clientId); if (!response) return; @@ -55,21 +60,21 @@ const fetchAddresses = async (formData) => { }; const onClientSelected = async (formData) => { + resetAgenciesSelector(formData); await fetchClient(formData); await fetchAddresses(formData); }; const fetchAvailableAgencies = async (formData) => { - const response = await getAgencies(formData); + resetAgenciesSelector(formData); + const response= await getAgencies(formData, selectedClient.value); if (!response) return; - agenciesOptions.value = response.data; - - const defaultAgency = agenciesOptions.value.find( - (agency) => - agency.agencyModeFk === selectedClient.value.defaultAddress.agencyModeFk - ); - - if (defaultAgency) formData.agencyModeId = defaultAgency.agencyModeFk; + + const { options, agency } = response + if(options) + agenciesOptions.value = options; + if(agency) + formData.agencyModeId = agency; }; const redirectToTicketList = (_, { id }) => { diff --git a/src/pages/Ticket/TicketCreateDialog.vue b/src/pages/Ticket/TicketCreateDialog.vue index 7a126d2c2..2245c5c81 100644 --- a/src/pages/Ticket/TicketCreateDialog.vue +++ b/src/pages/Ticket/TicketCreateDialog.vue @@ -38,6 +38,11 @@ onBeforeMount(async () => { await onClientSelected(initialFormState); }); +function resetAgenciesSelector(formData) { + agenciesOptions.value = []; + if(formData) formData.agencyModeId = null; +} + const fetchClient = async (formData) => { const response = await getClient(formData.clientId); if (!response) return; @@ -55,21 +60,21 @@ const fetchAddresses = async (formData) => { }; const onClientSelected = async (formData) => { + resetAgenciesSelector(formData); await fetchClient(formData); await fetchAddresses(formData); }; const fetchAvailableAgencies = async (formData) => { - const response = await getAgencies(formData); + resetAgenciesSelector(formData); + const response= await getAgencies(formData, selectedClient.value); if (!response) return; - agenciesOptions.value = response.data; - - const defaultAgency = agenciesOptions.value.find( - (agency) => - agency.agencyModeFk === selectedClient.value.defaultAddress.agencyModeFk - ); - - if (defaultAgency) formData.agencyModeId = defaultAgency.agencyModeFk; + + const { options, agency } = response + if(options) + agenciesOptions.value = options; + if(agency) + formData.agencyModeId = agency; }; const redirectToTicketList = (_, { id }) => { diff --git a/src/pages/Ticket/TicketList.vue b/src/pages/Ticket/TicketList.vue index 208bc4460..8df19c0d9 100644 --- a/src/pages/Ticket/TicketList.vue +++ b/src/pages/Ticket/TicketList.vue @@ -229,27 +229,33 @@ const columns = computed(() => [ ], }, ]); + +function resetAgenciesSelector(formData) { + agenciesOptions.value = []; + if(formData) formData.agencyModeId = null; +} + function redirectToLines(id) { const url = `#/ticket/${id}/sale`; window.open(url, '_blank'); } -const onClientSelected = async (formData) => { +const onClientSelected = async (formData) => { + resetAgenciesSelector(formData); await fetchClient(formData); await fetchAddresses(formData); }; const fetchAvailableAgencies = async (formData) => { - const response = await getAgencies(formData); + resetAgenciesSelector(formData); + const response= await getAgencies(formData, selectedClient.value); if (!response) return; - agenciesOptions.value = response.data; - - const defaultAgency = agenciesOptions.value.find( - (agency) => - agency.agencyModeFk === selectedClient.value.defaultAddress.agencyModeFk - ); - - if (defaultAgency) formData.agencyModeId = defaultAgency.agencyModeFk; + + const { options, agency } = response + if(options) + agenciesOptions.value = options; + if(agency) + formData.agencyModeId = agency; }; const fetchClient = async (formData) => { From 5250e59389a6cd8db8b31b3b2aa12dc044e658f9 Mon Sep 17 00:00:00 2001 From: jtubau Date: Fri, 31 Jan 2025 07:55:53 +0100 Subject: [PATCH 3/5] test: refs #7322 add unit tests for getAgencies to validate agency retrieval logic --- .../composables/__tests__/getAgencies.spec.js | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/pages/Route/Agency/composables/__tests__/getAgencies.spec.js b/src/pages/Route/Agency/composables/__tests__/getAgencies.spec.js index 9897173f1..99586b55b 100644 --- a/src/pages/Route/Agency/composables/__tests__/getAgencies.spec.js +++ b/src/pages/Route/Agency/composables/__tests__/getAgencies.spec.js @@ -3,6 +3,8 @@ import axios from 'axios'; import { getAgencies } from 'src/pages/Route/Agency/composables/getAgencies'; vi.mock('axios'); +const response = { data: [{ agencyModeFk: 'Agency1' }, { agencyModeFk: 'Agency2' }] }; +axios.get.mockResolvedValue(response); describe('getAgencies', () => { afterEach(() => { @@ -52,4 +54,23 @@ describe('getAgencies', () => { expect(axios.get).not.toHaveBeenCalled(); }); + + it('should return options and agency when default agency is found', async () => { + const formData = { warehouseId: '123', addressId: '456', landed: 'true' }; + const client = { defaultAddress: { agencyModeFk: 'Agency1' } }; + + const { options, agency } = await getAgencies(formData, client); + + expect(options).toEqual(response.data); + expect(agency).toEqual(response.data[0]); + }); + + it('should return options and agency when client is not provided', async () => { + const formData = { warehouseId: '123', addressId: '456', landed: 'true' }; + + const { options, agency } = await getAgencies(formData); + + expect(options).toEqual(response.data); + expect(agency).toBeNull(); + }); }); From f2ef6c1c4eb04c444dad30d4182760e76335c2c5 Mon Sep 17 00:00:00 2001 From: jtubau Date: Mon, 3 Feb 2025 11:33:51 +0100 Subject: [PATCH 4/5] refactor: refs #7322 update API functions to accept filters for enhanced data retrieval --- src/i18n/locale/en.yml | 44 +------------------ .../Customer/composables/getAddresses.js | 3 +- src/pages/Customer/composables/getClient.js | 3 +- .../Route/Agency/composables/getAgencies.js | 7 ++- 4 files changed, 11 insertions(+), 46 deletions(-) diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml index 3cce2a853..ad0165e2d 100644 --- a/src/i18n/locale/en.yml +++ b/src/i18n/locale/en.yml @@ -377,7 +377,7 @@ login: loginError: Invalid username or password fieldRequired: This field is required twoFactorRequired: Two-factor verification required -twoFactorRequired: +twoFactor: validate: Validate insert: Enter the verification code explanation: >- @@ -456,48 +456,6 @@ ticket: consigneeStreet: Street create: address: Address -invoiceOut: - card: - issued: Issued - customerCard: Customer card - ticketList: Ticket List - summary: - issued: Issued - dued: Due - booked: Booked - taxBreakdown: Tax breakdown - taxableBase: Taxable base - rate: Rate - fee: Fee - tickets: Tickets - totalWithVat: Amount - globalInvoices: - errors: - chooseValidClient: Choose a valid client - chooseValidCompany: Choose a valid company - chooseValidPrinter: Choose a valid printer - chooseValidSerialType: Choose a serial type - 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 tickets to invoice - criticalInvoiceError: 'Critical invoicing error, process stopped' - invalidSerialTypeForAll: The serial type must be global when invoicing all clients - table: - addressId: Address id - streetAddress: Street - statusCard: - percentageText: '{getPercentage}% {getAddressNumber} of {getNAddresses}' - pdfsNumberText: '{nPdfs} of {totalPdfs} PDFs' - negativeBases: - clientId: Client Id - base: Base - active: Active - hasToInvoice: Has to Invoice - verifiedData: Verified Data - comercial: Comercial - errors: - downloadCsvFailed: CSV download failed department: chat: Chat bossDepartment: Boss Department diff --git a/src/pages/Customer/composables/getAddresses.js b/src/pages/Customer/composables/getAddresses.js index eecc0150b..e65e64455 100644 --- a/src/pages/Customer/composables/getAddresses.js +++ b/src/pages/Customer/composables/getAddresses.js @@ -1,8 +1,9 @@ import axios from 'axios'; -export async function getAddresses(clientId) { +export async function getAddresses(clientId, _filter = {}) { if (!clientId) return; const filter = { + ..._filter, fields: ['nickname', 'street', 'city', 'id'], where: { isActive: true }, order: 'nickname ASC', diff --git a/src/pages/Customer/composables/getClient.js b/src/pages/Customer/composables/getClient.js index ecacc67c0..3b9e811de 100644 --- a/src/pages/Customer/composables/getClient.js +++ b/src/pages/Customer/composables/getClient.js @@ -1,7 +1,8 @@ import axios from 'axios'; -export async function getClient(clientId) { +export async function getClient(clientId, _filter = {}) { const filter = { + ..._filter, include: { relation: 'defaultAddress', scope: { diff --git a/src/pages/Route/Agency/composables/getAgencies.js b/src/pages/Route/Agency/composables/getAgencies.js index dd1370a55..850f87456 100644 --- a/src/pages/Route/Agency/composables/getAgencies.js +++ b/src/pages/Route/Agency/composables/getAgencies.js @@ -1,11 +1,16 @@ import axios from 'axios'; import agency from 'src/router/modules/agency'; -export async function getAgencies(formData, client) { +export async function getAgencies(formData, client, _filter = {}) { if (!formData.warehouseId || !formData.addressId || !formData.landed) return; + const filter = { + ..._filter + }; + let defaultAgency = null; let params = { + filter: JSON.stringify(filter), warehouseFk: formData.warehouseId, addressFk: formData.addressId, landed: formData.landed, From 278788ead394100155e73c6cae102f6bbc3a5784 Mon Sep 17 00:00:00 2001 From: jtubau Date: Mon, 3 Feb 2025 11:59:13 +0100 Subject: [PATCH 5/5] test: refs #7322 update getAgencies unit test to include filter parameters for enhanced API call validation --- .../Agency/composables/__tests__/getAgencies.spec.js | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/pages/Route/Agency/composables/__tests__/getAgencies.spec.js b/src/pages/Route/Agency/composables/__tests__/getAgencies.spec.js index 99586b55b..ccf7872cb 100644 --- a/src/pages/Route/Agency/composables/__tests__/getAgencies.spec.js +++ b/src/pages/Route/Agency/composables/__tests__/getAgencies.spec.js @@ -11,8 +11,9 @@ describe('getAgencies', () => { vi.clearAllMocks(); }); - const generateParams = (formData) => ({ + const generateParams = (formData, filter = {}) => ({ params: { + filter: JSON.stringify(filter), warehouseFk: formData.warehouseId, addressFk: formData.addressId, landed: formData.landed, @@ -25,10 +26,15 @@ describe('getAgencies', () => { addressId: '456', landed: 'true', }; + const filter = { + fields: ['nickname', 'street', 'city', 'id'], + where: { isActive: true }, + order: 'nickname ASC', + }; - await getAgencies(formData); + await getAgencies(formData, null, filter); - expect(axios.get).toHaveBeenCalledWith('Agencies/getAgenciesWithWarehouse', generateParams(formData)); + expect(axios.get).toHaveBeenCalledWith('Agencies/getAgenciesWithWarehouse', generateParams(formData, filter)); }); it('should not call API when formData is missing required landed field', async () => {