From 36473d979bea628a0c9f66f3659ff30f86793742 Mon Sep 17 00:00:00 2001 From: jtubau Date: Wed, 15 Jan 2025 13:38:45 +0100 Subject: [PATCH 1/2] feat: refs #7322 add address selection for ticket transfer --- .../Ticket/Card/TicketDescriptorMenu.vue | 83 ++++++++++++++++--- 1 file changed, 73 insertions(+), 10 deletions(-) diff --git a/src/pages/Ticket/Card/TicketDescriptorMenu.vue b/src/pages/Ticket/Card/TicketDescriptorMenu.vue index 38e2af612..8f60cf509 100644 --- a/src/pages/Ticket/Card/TicketDescriptorMenu.vue +++ b/src/pages/Ticket/Card/TicketDescriptorMenu.vue @@ -41,6 +41,9 @@ const ticketSummary = useArrayData('TicketSummary'); const { ticket } = toRefs(props); const ticketId = computed(() => props.ticket.id ?? currentRoute.value.params.id); const client = ref(); +const address = ref(); +const addressesOptions = ref([]); +const selectedClient = ref(); const showTransferDialog = ref(false); const showTurnDialog = ref(false); const showChangeTimeDialog = ref(false); @@ -52,6 +55,45 @@ const weight = ref(); const hasDocuwareFile = ref(); const quasar = useQuasar(); const canRestoreTicket = ref(false); + +const onClientSelected = async(clientId) =>{ + await fetchClient(clientId); + await fetchAddresses(clientId); +}; + +const fetchClient = async (clientId) => { + const filter = { + include: { + relation: 'defaultAddress', + scope: { + fields: ['id', 'nickname'], + }, + }, + where: { id: clientId }, + }; + const params = { filter: JSON.stringify(filter) }; + const { data } = await axios.get('Clients', { params }); + const [client] = data; + selectedClient.value = client; +}; + +const fetchAddresses = async (clientId) => { + if (!clientId) return; + + const filter = { + fields: ['nickname', 'street', 'city', 'id', 'isActive'], + order: ['isDefaultAddress DESC', 'isActive DESC', 'nickname ASC'], + }; + const params = { filter: JSON.stringify(filter) }; + const { data } = await axios.get(`Clients/${clientId}/addresses`, { + params, + }); + addressesOptions.value = data; + + const { defaultAddress } = selectedClient.value; + address.value = defaultAddress.id; +}; + const actions = { clone: async () => { const opts = { message: t('Ticket cloned'), type: 'positive' }; @@ -260,17 +302,14 @@ async function makeInvoice() { window.location.reload(); } -async function transferClient(client) { +async function transferClient(client, address) { const params = { clientFk: client, + addressFk: address, }; - const { data } = await axios.patch( - `Tickets/${ticketId.value}/transferClient`, - params - ); - - if (data) window.location.reload(); + await axios.patch( `Tickets/${ticketId.value}/transferClient`, params ); + window.location.reload(); } async function addTurn(day) { @@ -446,7 +485,7 @@ async function ticketToRestore() { + + + @@ -762,7 +824,7 @@ async function ticketToRestore() { en: addTurn: Add turn invoiceIds: "Invoices have been generated with the following ids: {invoiceIds}" - + es: Show Delivery Note...: Ver albarán... Send Delivery Note...: Enviar albarán... @@ -814,4 +876,5 @@ es: Are you sure you want to restore the ticket?: ¿Seguro que quieres restaurar el ticket? You are going to restore this ticket: Vas a restaurar este ticket Ticket restored: Ticket restaurado + Select a client to enable: Selecciona un cliente para habilitar -- 2.40.1 From 99df3c5df294fc74918d349c44b5d0eaac691d11 Mon Sep 17 00:00:00 2001 From: jtubau Date: Thu, 23 Jan 2025 14:28:21 +0100 Subject: [PATCH 2/2] refactor: refs #7322 extract repeated functions and create tests --- .../__tests__/getAddresses.spec.js | 33 +++++++++++ .../composables/__tests__/getClient.spec.js | 41 +++++++++++++ .../Customer/composables/getAddresses.js | 14 +++++ src/pages/Customer/composables/getClient.js | 15 +++++ .../composables/__tests__/getAgencies.spec.js | 55 ++++++++++++++++++ .../Route/Agency/composables/getAgencies.js | 12 ++++ .../Ticket/Card/TicketDescriptorMenu.vue | 57 ++++++++----------- src/pages/Ticket/TicketCreate.vue | 38 ++----------- src/pages/Ticket/TicketCreateDialog.vue | 38 ++----------- src/pages/Ticket/TicketList.vue | 38 ++----------- 10 files changed, 211 insertions(+), 130 deletions(-) create mode 100644 src/pages/Customer/composables/__tests__/getAddresses.spec.js create mode 100644 src/pages/Customer/composables/__tests__/getClient.spec.js create mode 100644 src/pages/Customer/composables/getAddresses.js create mode 100644 src/pages/Customer/composables/getClient.js create mode 100644 src/pages/Route/Agency/composables/__tests__/getAgencies.spec.js create mode 100644 src/pages/Route/Agency/composables/getAgencies.js diff --git a/src/pages/Customer/composables/__tests__/getAddresses.spec.js b/src/pages/Customer/composables/__tests__/getAddresses.spec.js new file mode 100644 index 000000000..9e04a83cc --- /dev/null +++ b/src/pages/Customer/composables/__tests__/getAddresses.spec.js @@ -0,0 +1,33 @@ +import { describe, it, expect, vi, afterEach } from 'vitest'; +import axios from 'axios'; +import { getAddresses } from 'src/pages/Customer/composables/getAddresses'; + +vi.mock('axios'); + +describe('getAddresses', () => { + afterEach(() => { + vi.clearAllMocks(); + }); + + it('should fetch addresses with correct parameters for a valid clientId', async () => { + const clientId = '12345'; + + await getAddresses(clientId); + + expect(axios.get).toHaveBeenCalledWith(`Clients/${clientId}/addresses`, { + params: { + filter: JSON.stringify({ + fields: ['nickname', 'street', 'city', 'id'], + where: { isActive: true }, + order: 'nickname ASC', + }), + }, + }); + }); + + it('should return undefined when clientId is not provided', async () => { + await getAddresses(undefined); + + expect(axios.get).not.toHaveBeenCalled(); + }); +}); \ No newline at end of file diff --git a/src/pages/Customer/composables/__tests__/getClient.spec.js b/src/pages/Customer/composables/__tests__/getClient.spec.js new file mode 100644 index 000000000..a83d3d89f --- /dev/null +++ b/src/pages/Customer/composables/__tests__/getClient.spec.js @@ -0,0 +1,41 @@ +import { describe, it, expect, vi, afterEach } from 'vitest'; +import axios from 'axios'; +import { getClient } from 'src/pages/Customer/composables/getClient'; + +vi.mock('axios'); + +describe('getClient', () => { + afterEach(() => { + vi.clearAllMocks(); + }); + + const generateParams = (clientId) => ({ + params: { + filter: JSON.stringify({ + include: { + relation: 'defaultAddress', + scope: { + fields: ['id', 'agencyModeFk'], + }, + }, + where: { id: clientId }, + }), + }, + }); + + it('should fetch client data with correct parameters for a valid clientId', async () => { + const clientId = '12345'; + + await getClient(clientId); + + expect(axios.get).toHaveBeenCalledWith('Clients', generateParams(clientId)); + }); + + it('should return undefined when clientId is not provided', async () => { + const clientId = undefined; + + await getClient(clientId); + + expect(axios.get).toHaveBeenCalledWith('Clients', generateParams(clientId)); + }); +}); diff --git a/src/pages/Customer/composables/getAddresses.js b/src/pages/Customer/composables/getAddresses.js new file mode 100644 index 000000000..eecc0150b --- /dev/null +++ b/src/pages/Customer/composables/getAddresses.js @@ -0,0 +1,14 @@ +import axios from 'axios'; + +export async function getAddresses(clientId) { + if (!clientId) return; + const filter = { + fields: ['nickname', 'street', 'city', 'id'], + where: { isActive: true }, + order: 'nickname ASC', + }; + const params = { filter: JSON.stringify(filter) }; + return await axios.get(`Clients/${clientId}/addresses`, { + params, + }); +}; \ No newline at end of file diff --git a/src/pages/Customer/composables/getClient.js b/src/pages/Customer/composables/getClient.js new file mode 100644 index 000000000..ecacc67c0 --- /dev/null +++ b/src/pages/Customer/composables/getClient.js @@ -0,0 +1,15 @@ +import axios from 'axios'; + +export async function getClient(clientId) { + const filter = { + include: { + relation: 'defaultAddress', + scope: { + fields: ['id', 'agencyModeFk'], + }, + }, + where: { id: clientId }, + }; + const params = { filter: JSON.stringify(filter) }; + return await axios.get('Clients', { params }); +}; \ No newline at end of file diff --git a/src/pages/Route/Agency/composables/__tests__/getAgencies.spec.js b/src/pages/Route/Agency/composables/__tests__/getAgencies.spec.js new file mode 100644 index 000000000..9897173f1 --- /dev/null +++ b/src/pages/Route/Agency/composables/__tests__/getAgencies.spec.js @@ -0,0 +1,55 @@ +import { describe, it, expect, vi, afterEach } from 'vitest'; +import axios from 'axios'; +import { getAgencies } from 'src/pages/Route/Agency/composables/getAgencies'; + +vi.mock('axios'); + +describe('getAgencies', () => { + afterEach(() => { + vi.clearAllMocks(); + }); + + const generateParams = (formData) => ({ + params: { + warehouseFk: formData.warehouseId, + addressFk: formData.addressId, + landed: formData.landed, + }, + }); + + it('should fetch agencies data with correct parameters for valid formData', async () => { + const formData = { + warehouseId: '123', + addressId: '456', + landed: 'true', + }; + + await getAgencies(formData); + + expect(axios.get).toHaveBeenCalledWith('Agencies/getAgenciesWithWarehouse', generateParams(formData)); + }); + + it('should not call API when formData is missing required landed field', async () => { + const formData = { warehouseId: '123', addressId: '456' }; + + await getAgencies(formData); + + expect(axios.get).not.toHaveBeenCalled(); + }); + + it('should not call API when formData is missing required addressId field', async () => { + const formData = { warehouseId: '123', landed: 'true' }; + + await getAgencies(formData); + + expect(axios.get).not.toHaveBeenCalled(); + }); + + it('should not call API when formData is missing required warehouseId field', async () => { + const formData = { addressId: '456', landed: 'true' }; + + await getAgencies(formData); + + expect(axios.get).not.toHaveBeenCalled(); + }); +}); diff --git a/src/pages/Route/Agency/composables/getAgencies.js b/src/pages/Route/Agency/composables/getAgencies.js new file mode 100644 index 000000000..7299d7e23 --- /dev/null +++ b/src/pages/Route/Agency/composables/getAgencies.js @@ -0,0 +1,12 @@ +import axios from 'axios'; + +export async function getAgencies(formData) { + if (!formData.warehouseId || !formData.addressId || !formData.landed) return; + let params = { + warehouseFk: formData.warehouseId, + addressFk: formData.addressId, + landed: formData.landed, + }; + + return await axios.get('Agencies/getAgenciesWithWarehouse', { params }); +} diff --git a/src/pages/Ticket/Card/TicketDescriptorMenu.vue b/src/pages/Ticket/Card/TicketDescriptorMenu.vue index 8f60cf509..89f7015d7 100644 --- a/src/pages/Ticket/Card/TicketDescriptorMenu.vue +++ b/src/pages/Ticket/Card/TicketDescriptorMenu.vue @@ -16,6 +16,8 @@ import VnInputTime from 'src/components/common/VnInputTime.vue'; import { useAcl } from 'src/composables/useAcl'; import VnInputNumber from 'src/components/common/VnInputNumber.vue'; import { useArrayData } from 'src/composables/useArrayData'; +import { getAddresses } from 'src/pages/Customer/composables/getAddresses'; +import { getClient } from 'src/pages/Customer/composables/getClient'; const props = defineProps({ ticket: { @@ -40,8 +42,8 @@ const { openReport, sendEmail } = usePrintService(); const ticketSummary = useArrayData('TicketSummary'); const { ticket } = toRefs(props); const ticketId = computed(() => props.ticket.id ?? currentRoute.value.params.id); -const client = ref(); -const address = ref(); +const client = ref(null); +const address = ref(null); const addressesOptions = ref([]); const selectedClient = ref(); const showTransferDialog = ref(false); @@ -57,37 +59,23 @@ const quasar = useQuasar(); const canRestoreTicket = ref(false); const onClientSelected = async(clientId) =>{ - await fetchClient(clientId); - await fetchAddresses(clientId); + client.value = clientId; + await fetchClient(); + await fetchAddresses(); }; -const fetchClient = async (clientId) => { - const filter = { - include: { - relation: 'defaultAddress', - scope: { - fields: ['id', 'nickname'], - }, - }, - where: { id: clientId }, - }; - const params = { filter: JSON.stringify(filter) }; - const { data } = await axios.get('Clients', { params }); - const [client] = data; - selectedClient.value = client; +const onAddressSelected = (addressId) => { + address.value = addressId; +} + +const fetchClient = async () => { + const { data } = await getClient(client.value) + const [retrievedClient] = data; + selectedClient.value = retrievedClient; }; -const fetchAddresses = async (clientId) => { - if (!clientId) return; - - const filter = { - fields: ['nickname', 'street', 'city', 'id', 'isActive'], - order: ['isDefaultAddress DESC', 'isActive DESC', 'nickname ASC'], - }; - const params = { filter: JSON.stringify(filter) }; - const { data } = await axios.get(`Clients/${clientId}/addresses`, { - params, - }); +const fetchAddresses = async () => { + const { data } = await getAddresses(client.value); addressesOptions.value = data; const { defaultAddress } = selectedClient.value; @@ -302,12 +290,12 @@ async function makeInvoice() { window.location.reload(); } -async function transferClient(client, address) { +async function transferClient() { const params = { - clientFk: client, - addressFk: address, + clientFk: client.value, + addressFk: address.value, }; - + await axios.patch( `Tickets/${ticketId.value}/transferClient`, params ); window.location.reload(); } @@ -485,7 +473,7 @@ async function ticketToRestore() {