diff --git a/CHANGELOG.md b/CHANGELOG.md index 8fffb1b53..8c59fc781 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added -- Added... +- (Clientes) => Añadida nueva sección "Pagos Web" para gestionar los pagos de todos los clientes +- (Tickets) => Añadida opción en el menú desplegable del ticket para enviar SMS al cliente +- (Reclamaciones) => Añadida nueva sección "Registros de auditoría" +- (Trabajadores) => Añadido módulo de trabajadores +- (General) => Añadida barra de búsqueda general en los listados principales ### Changed diff --git a/jsconfig.json b/jsconfig.json index f4abfc709..9c6a8010d 100644 --- a/jsconfig.json +++ b/jsconfig.json @@ -11,6 +11,7 @@ "assets/*": ["src/assets/*"], "boot/*": ["src/boot/*"], "stores/*": ["src/stores/*"], + "filters/*": ["src/filters/*"], "vue$": ["node_modules/vue/dist/vue.runtime.esm-bundler.js"] } }, diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue index e61b4b6c4..d361838e1 100644 --- a/src/components/ui/VnFilterPanel.vue +++ b/src/components/ui/VnFilterPanel.vue @@ -141,8 +141,11 @@ function formatValue(value) { -
- {{ t(`You didn't enter any filter`) }} +
+ {{ t(`No filters applied`) }}
es: - You didn't enter any filter: No has introducido ningún filtro + No filters applied: No se han aplicado filtros Applied filters: Filtros aplicados Remove filters: Eliminar filtros Refresh: Refrescar diff --git a/src/components/ui/VnSearchbar.vue b/src/components/ui/VnSearchbar.vue index 7585f09b3..47a7fc24c 100644 --- a/src/components/ui/VnSearchbar.vue +++ b/src/components/ui/VnSearchbar.vue @@ -51,14 +51,7 @@ const props = defineProps({ const router = useRouter(); const route = useRoute(); -const arrayData = useArrayData(props.dataKey, { - url: props.url, - filter: props.filter, - where: props.where, - limit: props.limit, - order: props.order, - userParams: props.userParams, -}); +const arrayData = useArrayData(props.dataKey, { ...props }); const store = arrayData.store; const searchText = ref(''); diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js index 01f8ca294..c7808f9a8 100644 --- a/src/composables/useArrayData.js +++ b/src/composables/useArrayData.js @@ -30,9 +30,20 @@ export function useArrayData(key, userOptions) { }); function setOptions() { + const allowedOptions = [ + 'url', + 'filter', + 'where', + 'order', + 'limit', + 'skip', + 'userParams', + 'userFilter' + ]; if (typeof userOptions === 'object') { for (const option in userOptions) { - if (userOptions[option] == null) continue; + const isEmpty = userOptions[option] == null || userOptions[option] == '' + if (isEmpty || !allowedOptions.includes(option)) continue; if (Object.prototype.hasOwnProperty.call(store, option)) { store[option] = userOptions[option]; @@ -62,6 +73,7 @@ export function useArrayData(key, userOptions) { Object.assign(params, store.userParams); + store.isLoading = true const response = await axios.get(store.url, { signal: canceller.signal, params, @@ -82,6 +94,8 @@ export function useArrayData(key, userOptions) { updateStateParams(); } + store.isLoading = false + canceller = null; } @@ -139,7 +153,8 @@ export function useArrayData(key, userOptions) { }); } - const totalRows = computed(() => store.data && store.data.length | 0); + const totalRows = computed(() => store.data && store.data.length || 0); + const isLoading = computed(() => store.isLoading || false) return { fetch, @@ -152,5 +167,6 @@ export function useArrayData(key, userOptions) { hasMoreData, totalRows, updateStateParams, + isLoading }; } diff --git a/src/i18n/en/index.js b/src/i18n/en/index.js index be4603ea8..078ad3815 100644 --- a/src/i18n/en/index.js +++ b/src/i18n/en/index.js @@ -60,6 +60,7 @@ export default { pageTitles: { customers: 'Customers', list: 'List', + webPayments: 'Web Payments', createCustomer: 'Create customer', summary: 'Summary', basicData: 'Basic Data', diff --git a/src/i18n/es/index.js b/src/i18n/es/index.js index b3336ca7a..5a069fba8 100644 --- a/src/i18n/es/index.js +++ b/src/i18n/es/index.js @@ -60,9 +60,10 @@ export default { pageTitles: { customers: 'Clientes', list: 'Listado', + webPayments: 'Pagos Web', createCustomer: 'Crear cliente', - summary: 'Resumen', basicData: 'Datos básicos', + summary: 'Resumen' }, list: { phone: 'Teléfono', diff --git a/src/pages/Customer/CustomerPayments.vue b/src/pages/Customer/CustomerPayments.vue new file mode 100644 index 000000000..c1853ac20 --- /dev/null +++ b/src/pages/Customer/CustomerPayments.vue @@ -0,0 +1,294 @@ + + + + + + + +es: + Web Payments: Pagos Web + Confirm transaction: Confirmar transacción + Transaction ID: ID transacción + Customer ID: ID cliente + Customer Name: Nombre cliente + State: Estado + Dated: Fecha + Amount: Importe + Actions: Acciones + Confirmed: Confirmada + Unconfirmed: Sin confirmar + Change view: Cambiar vista + Payment confirmed: Pago confirmado + diff --git a/src/pages/Customer/CustomerPaymentsFilter.vue b/src/pages/Customer/CustomerPaymentsFilter.vue new file mode 100644 index 000000000..1d26b2d6a --- /dev/null +++ b/src/pages/Customer/CustomerPaymentsFilter.vue @@ -0,0 +1,78 @@ + + + + + +en: + params: + orderFk: Order + clientFk: Customer + amount: Amount +es: + params: + orderFk: Pedido + clientFk: Cliente + amount: Importe + Order ID: ID pedido + Customer ID: ID cliente + Amount: Importe + diff --git a/src/router/modules/customer.js b/src/router/modules/customer.js index 7b48413f2..c7e978eec 100644 --- a/src/router/modules/customer.js +++ b/src/router/modules/customer.js @@ -10,7 +10,7 @@ export default { component: RouterView, redirect: { name: 'CustomerMain' }, menus: { - main: ['CustomerList', 'CustomerCreate'], + main: ['CustomerList', 'CustomerPayments', 'CustomerCreate'], card: ['CustomerBasicData'], }, children: [ @@ -29,6 +29,15 @@ export default { }, component: () => import('src/pages/Customer/CustomerList.vue') }, + { + path: 'payments', + name: 'CustomerPayments', + meta: { + title: 'webPayments', + icon: 'vn:onlinepayment', + }, + component: () => import('src/pages/Customer/CustomerPayments.vue') + }, { path: 'create', name: 'CustomerCreate', diff --git a/src/stores/useArrayDataStore.js b/src/stores/useArrayDataStore.js index 9d904d757..f9a32a6fa 100644 --- a/src/stores/useArrayDataStore.js +++ b/src/stores/useArrayDataStore.js @@ -18,6 +18,7 @@ export const useArrayDataStore = defineStore('arrayDataStore', () => { skip: 0, order: '', data: ref(), + isLoading: false }; } diff --git a/test/vitest/__tests__/pages/Customer/CustomerPayments.spec.js b/test/vitest/__tests__/pages/Customer/CustomerPayments.spec.js new file mode 100644 index 000000000..b56b29c20 --- /dev/null +++ b/test/vitest/__tests__/pages/Customer/CustomerPayments.spec.js @@ -0,0 +1,54 @@ +import { vi, describe, expect, it, beforeAll, afterEach } from 'vitest'; +import { createWrapper, axios } from 'app/test/vitest/helper'; +import CustomerPayments from 'pages/Customer/CustomerPayments.vue'; + +describe('CustomerPayments', () => { + let vm; + + + beforeAll(() => { + vm = createWrapper(CustomerPayments, { + global: { + stubs: ['Paginate'], + mocks: { + fetch: vi.fn(), + }, + } + }).vm; + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + describe('confirmTransaction()', () => { + it('should make a POST request and then call to quasar notify method', async () => { + vi.spyOn(axios, 'post').mockResolvedValue({ data: true }); + vi.spyOn(vm.quasar, 'notify'); + + await vm.confirmTransaction({ id: 1 }); + + + expect(vm.quasar.notify).toHaveBeenCalledWith( + expect.objectContaining({ + message: 'Payment confirmed', + type: 'positive' + }) + ); + }); + }); + + describe('stateColor()', () => { + it('should return "positive" when isConfirmed property is truthy', async () => { + const result = await vm.stateColor({ isConfirmed: true }); + + expect(result).toEqual('positive'); + }); + + it('should return "primary" when isConfirmed property is falsy', async () => { + const result = await vm.stateColor({ isConfirmed: false }); + + expect(result).toEqual('primary'); + }); + }); +});