diff --git a/.gitignore b/.gitignore index 213384ef5..617169f6f 100644 --- a/.gitignore +++ b/.gitignore @@ -29,5 +29,5 @@ yarn-error.log* *.sln # Cypress directories and files -/tests/cypress/videos -/tests/cypress/screenshots \ No newline at end of file +/test/cypress/videos +/test/cypress/screenshots diff --git a/src/components/common/__tests__/VnDms.spec.js b/src/components/common/__tests__/VnDms.spec.js new file mode 100644 index 000000000..03028aee7 --- /dev/null +++ b/src/components/common/__tests__/VnDms.spec.js @@ -0,0 +1,146 @@ +import { createWrapper, axios } from 'app/test/vitest/helper'; +import { vi, afterEach, beforeEach, beforeAll, describe, expect, it } from 'vitest'; +import VnDms from 'src/components/common/VnDms.vue'; + +class MockFormData { + constructor() { + this.entries = {}; + } + + append(key, value) { + if (!key) { + throw new Error('Key is required for FormData.append'); + } + this.entries[key] = value; + } + + get(key) { + return this.entries[key] || null; + } + + getAll() { + return this.entries; + } +} + +global.FormData = MockFormData; + +describe('VnDms', () => { + let wrapper; + let vm; + let postMock; + + const postResponseMock = { data: { success: true } }; + + const data = { + hasFile: true, + hasFileAttached: true, + reference: 'DMS-test', + warehouseFk: 1, + companyFk: 2, + dmsTypeFk: 3, + description: 'This is a test description', + files: { name: 'example.txt', content: new Blob(['file content'], { type: 'text/plain' })}, + }; + + const expectedBody = { + hasFile: true, + hasFileAttached: true, + reference: 'DMS-test', + warehouseId: 1, + companyId: 2, + dmsTypeId: 3, + description: 'This is a test description', + }; + + beforeAll(() => { + wrapper = createWrapper(VnDms, { + propsData: { + url: '/test', + formInitialData: { id: 1, reference: 'test' }, + model: 'Worker', + } + }); + wrapper = wrapper.wrapper; + vm = wrapper.vm; + vi.spyOn(vm, '$emit'); + }); + + beforeEach(() => { + postMock = vi.spyOn(axios, 'post').mockResolvedValue(postResponseMock); + vm.dms = data; + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + describe('mapperDms', () => { + it('should map DMS data correctly and add file to FormData', () => { + const [formData, params] = vm.mapperDms(data); + + expect(formData.get('example.txt')).toBe(data.files); + expect(expectedBody).toEqual(params.params); + }); + + it('should map DMS data correctly without file', () => { + delete data.files; + + const [formData, params] = vm.mapperDms(data); + + expect(formData.getAll()).toEqual({}); + expect(expectedBody).toEqual(params.params); + }); + }); + + describe('getUrl', () => { + it('should returns prop url when is set', async () => { + expect(vm.getUrl()).toBe('/test'); + }); + + it('should returns url dms/"props.formInitialData.id"/updateFile when prop url is null', async () => { + await wrapper.setProps({ url: null }); + expect(vm.getUrl()).toBe('dms/1/updateFile'); + }); + + it('should returns url "props.model"/"route.params.id"/uploadFile when formInitialData is null', async () => { + await wrapper.setProps({ formInitialData: null }); + vm.route.params.id = '123'; + expect(vm.getUrl()).toBe('Worker/123/uploadFile'); + }); + }); + + describe('save', () => { + it('should save data correctly', async () => { + await vm.save(); + expect(postMock).toHaveBeenCalledWith(vm.getUrl(), expect.any(FormData), { params: expectedBody }); + expect(wrapper.emitted('onDataSaved')).toBeTruthy(); + }); + }); + + describe('defaultData', () => { + it('should set dms with formInitialData', async () => { + const testData = { + hasFile: false, + hasFileAttached: false, + reference: 'defaultData-test', + warehouseFk: 2, + companyFk: 3, + dmsTypeFk: 2, + description: 'This is a test description' + } + await wrapper.setProps({ formInitialData: testData }); + vm.defaultData(); + + expect(vm.dms).toEqual(testData); + }); + + it('should add reference with "route.params.id" to dms if formInitialData is null', async () => { + await wrapper.setProps({ formInitialData: null }); + vm.route.params.id= '111'; + vm.defaultData(); + + expect(vm.dms.reference).toBe('111'); + }); + }); +}); \ No newline at end of file diff --git a/src/components/ui/CatalogItem.vue b/src/components/ui/CatalogItem.vue index 9670d9b68..7806562b2 100644 --- a/src/components/ui/CatalogItem.vue +++ b/src/components/ui/CatalogItem.vue @@ -41,7 +41,7 @@ const card = toRef(props, 'item');
- + {{ card.name }} diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue index aba797678..93f069cc6 100644 --- a/src/components/ui/VnFilterPanel.vue +++ b/src/components/ui/VnFilterPanel.vue @@ -190,7 +190,10 @@ const getLocale = (label) => { const globalLocale = `globals.params.${param}`; if (te(globalLocale)) return t(globalLocale); else if (te(t(`params.${param}`))); - else return t(`${route.meta.moduleName.toLowerCase()}.params.${param}`); + else { + const camelCaseModuleName = route.meta.moduleName.charAt(0).toLowerCase() + route.meta.moduleName.slice(1); + return t(`${camelCaseModuleName}.params.${param}`); + } }; diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js index d7838d58e..89d4dee5c 100644 --- a/src/composables/useArrayData.js +++ b/src/composables/useArrayData.js @@ -170,10 +170,9 @@ export function useArrayData(key, userOptions) { async function addOrder(field, direction = 'ASC') { const newOrder = field + ' ' + direction; - let order = store.order || []; - if (typeof order == 'string') order = [order]; + const order = toArray(store.order); - let index = order.findIndex((o) => o.split(' ')[0] === field); + let index = getOrderIndex(order, field); if (index > -1) { order[index] = newOrder; } else { @@ -190,16 +189,23 @@ export function useArrayData(key, userOptions) { } async function deleteOrder(field) { - let order = store.order ?? []; - if (typeof order == 'string') order = [order]; - - const index = order.findIndex((o) => o.split(' ')[0] === field); + const order = toArray(store.order); + const index = getOrderIndex(order, field); if (index > -1) order.splice(index, 1); store.order = order; fetch({}); } + function getOrderIndex(order, field) { + return order.findIndex((o) => o.split(' ')[0] === field); + } + + function toArray(str = []) { + if (Array.isArray(str)) return str; + if (typeof str === 'string') return str.split(',').map((item) => item.trim()); + } + function sanitizerParams(params, exprBuilder) { for (const param in params) { if (params[param] === '' || params[param] === null) { @@ -290,8 +296,7 @@ export function useArrayData(key, userOptions) { Object.assign(params, store.userParams); if (params.filter) params.filter.skip = store.skip; - if (store?.order && typeof store?.order == 'string') store.order = [store.order]; - if (store.order?.length) params.filter.order = [...store.order]; + if (store.order) params.filter.order = toArray(store.order); else delete params.filter.order; return { filter, params, limit: filter.limit }; diff --git a/src/css/app.scss b/src/css/app.scss index 7ef52c9ca..59e945f05 100644 --- a/src/css/app.scss +++ b/src/css/app.scss @@ -316,11 +316,11 @@ input::-webkit-inner-spin-button { } .q-item > .q-item__section:has(.q-checkbox) { - max-width: min-content; + max-width: fit-content; } .row > .column:has(.q-checkbox) { - max-width: min-content; + max-width: fit-content; } .q-field__inner { .q-field__control { diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml index cdd75f29a..3d50337ef 100644 --- a/src/i18n/locale/en.yml +++ b/src/i18n/locale/en.yml @@ -5,6 +5,7 @@ globals: quantity: Quantity language: Language entity: Entity + preview: Preview user: User details: Details collapseMenu: Collapse lateral menu @@ -734,62 +735,6 @@ travel: destination: Destination thermograph: Thermograph travelFileDescription: 'Travel id { travelId }' -item: - descriptor: - buyer: Buyer - color: Color - category: Category - available: Available - warehouseText: 'Calculated on the warehouse of { warehouseName }' - itemDiary: Item diary - list: - id: Identifier - stems: Stems - category: Category - typeName: Type - isActive: Active - userName: Buyer - weightByPiece: Weight/Piece - stemMultiplier: Multiplier - fixedPrice: - itemFk: Item ID - groupingPrice: Grouping price - packingPrice: Packing price - hasMinPrice: Has min price - minPrice: Min price - started: Started - ended: Ended - create: - priority: Priority - buyRequest: - requester: Requester - requested: Requested - attender: Atender - achieved: Achieved - concept: Concept - summary: - otherData: Other data - tax: Tax - botanical: Botanical - barcode: Barcode - completeName: Complete name - family: Familiy - stems: Stems - multiplier: Multiplier - buyer: Buyer - doPhoto: Do photo - intrastatCode: Intrastat code - ref: Reference - relevance: Relevance - weight: Weight (gram)/stem - units: Units/box - expense: Expense - generic: Generic - recycledPlastic: Recycled plastic - nonRecycledPlastic: Non recycled plastic - minSalesQuantity: Min sales quantity - genus: Genus - specie: Specie components: topbar: {} itemsFilterPanel: diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml index abadaa2dc..1eae05f51 100644 --- a/src/i18n/locale/es.yml +++ b/src/i18n/locale/es.yml @@ -5,6 +5,7 @@ globals: language: Idioma quantity: Cantidad entity: Entidad + preview: Vista previa user: Usuario details: Detalles collapseMenu: Contraer menú lateral @@ -730,62 +731,6 @@ travel: destination: Destino thermograph: Termógrafo travelFileDescription: 'Id envío { travelId }' -item: - descriptor: - buyer: Comprador - color: Color - category: Categoría - available: Disponible - warehouseText: 'Calculado sobre el almacén de { warehouseName }' - itemDiary: Registro de compra-venta - list: - id: Identificador - stems: Tallos - category: Reino - typeName: Tipo - isActive: Activo - weightByPiece: Peso (gramos)/tallo - userName: Comprador - stemMultiplier: Multiplicador - fixedPrice: - itemFk: ID Artículo - groupingPrice: Precio grouping - packingPrice: Precio packing - hasMinPrice: Tiene precio mínimo - minPrice: Precio min - started: Inicio - ended: Fin - create: - priority: Prioridad - summary: - otherData: Otros datos - tax: IVA - botanical: Botánico - barcode: Código de barras - completeName: Nombre completo - family: Familia - stems: Tallos - multiplier: Multiplicador - buyer: Comprador - doPhoto: Hacer foto - intrastatCode: Código intrastat - ref: Referencia - relevance: Relevancia - weight: Peso (gramos)/tallo - units: Unidades/caja - expense: Gasto - generic: Genérico - recycledPlastic: Plástico reciclado - nonRecycledPlastic: Plástico no reciclado - minSalesQuantity: Cantidad mínima de venta - genus: Genus - specie: Specie - buyRequest: - requester: Solicitante - requested: Solicitado - attender: Comprador - achieved: Conseguido - concept: Concepto components: topbar: {} itemsFilterPanel: diff --git a/src/pages/Customer/Card/CustomerConsumption.vue b/src/pages/Customer/Card/CustomerConsumption.vue index 640e37ed3..f0d8dea47 100644 --- a/src/pages/Customer/Card/CustomerConsumption.vue +++ b/src/pages/Customer/Card/CustomerConsumption.vue @@ -152,7 +152,8 @@ const updateDateParams = (value, params) => { v-if="campaignList" data-key="CustomerConsumption" url="Clients/consumption" - :order="['itemTypeFk', 'itemName', 'itemSize', 'description']" + :order="['itemTypeFk', 'itemName', 'itemSize', 'description']" + :filter="{ where: { clientFk: route.params.id } }" :columns="columns" search-url="consumption" :user-params="userParams" diff --git a/src/pages/Customer/Card/CustomerDescriptor.vue b/src/pages/Customer/Card/CustomerDescriptor.vue index cb49109d0..4a064843a 100644 --- a/src/pages/Customer/Card/CustomerDescriptor.vue +++ b/src/pages/Customer/Card/CustomerDescriptor.vue @@ -187,14 +187,18 @@ const debtWarning = computed(() => { - {{ t('Go to user') }} + {{ t('globals.pageTitles.createOrder') }} { }; const clientFk = { ticket: 'clientId', - order: 'clientFk', }; const key = clientFk[type]; if (!key) return; @@ -70,11 +69,6 @@ const openCreateForm = (type) => { {{ t('globals.pageTitles.createTicket') }} - - - {{ t('globals.pageTitles.createOrder') }} - - {{ t('Send SMS') }} diff --git a/src/pages/Customer/CustomerList.vue b/src/pages/Customer/CustomerList.vue index f6458fd64..f345fcda3 100644 --- a/src/pages/Customer/CustomerList.vue +++ b/src/pages/Customer/CustomerList.vue @@ -408,7 +408,7 @@ function handleLocation(data, location) { order: ['id DESC'], }" > -