diff --git a/package.json b/package.json index 17f39cad71..d23ed0cedd 100644 --- a/package.json +++ b/package.json @@ -1,74 +1,74 @@ { - "name": "salix-front", - "version": "25.06.0", - "description": "Salix frontend", - "productName": "Salix", - "author": "Verdnatura", - "private": true, - "packageManager": "pnpm@8.15.1", - "type": "module", - "scripts": { - "resetDatabase": "cd ../salix && gulp docker", - "lint": "eslint --ext .js,.vue ./", - "format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore", - "test:e2e": "cypress open", - "test:e2e:ci": "npm run resetDatabase && cd ../salix-front && cypress run", - "test": "echo \"See package.json => scripts for available tests.\" && exit 0", - "test:unit": "vitest", - "test:unit:ci": "vitest run", - "commitlint": "commitlint --edit", - "prepare": "npx husky install", - "addReferenceTag": "node .husky/addReferenceTag.js", - "docs:dev": "vitepress dev docs", - "docs:build": "vitepress build docs", - "docs:preview": "vitepress preview docs" - }, - "dependencies": { - "@quasar/cli": "^2.4.1", - "@quasar/extras": "^1.16.16", - "axios": "^1.4.0", - "chromium": "^3.0.3", - "croppie": "^2.6.5", - "moment": "^2.30.1", - "pinia": "^2.1.3", - "quasar": "^2.17.7", - "validator": "^13.9.0", - "vue": "^3.5.13", - "vue-i18n": "^9.3.0", - "vue-router": "^4.2.5" - }, - "devDependencies": { - "@commitlint/cli": "^19.2.1", - "@commitlint/config-conventional": "^19.1.0", - "@intlify/unplugin-vue-i18n": "^0.8.2", - "@pinia/testing": "^0.1.2", - "@quasar/app-vite": "^2.0.8", - "@quasar/quasar-app-extension-qcalendar": "^4.0.2", - "@quasar/quasar-app-extension-testing-unit-vitest": "^0.4.0", - "@vue/test-utils": "^2.4.4", - "autoprefixer": "^10.4.14", - "cypress": "^13.6.6", - "cypress-mochawesome-reporter": "^3.8.2", - "eslint": "^9.18.0", - "eslint-config-prettier": "^10.0.1", - "eslint-plugin-cypress": "^4.1.0", - "eslint-plugin-vue": "^9.32.0", - "husky": "^8.0.0", - "postcss": "^8.4.23", - "prettier": "^3.4.2", - "sass": "^1.83.4", - "vitepress": "^1.6.3", - "vitest": "^0.34.0" - }, - "engines": { - "node": "^20 || ^18 || ^16", - "npm": ">= 8.1.2", - "yarn": ">= 1.21.1", - "bun": ">= 1.0.25" - }, - "overrides": { - "@vitejs/plugin-vue": "^5.2.1", - "vite": "^6.0.11", - "vitest": "^0.31.1" - } + "name": "salix-front", + "version": "25.08.0", + "description": "Salix frontend", + "productName": "Salix", + "author": "Verdnatura", + "private": true, + "packageManager": "pnpm@8.15.1", + "type": "module", + "scripts": { + "resetDatabase": "cd ../salix && gulp docker", + "lint": "eslint --ext .js,.vue ./", + "format": "prettier --write \"**/*.{js,vue,scss,html,md,json}\" --ignore-path .gitignore", + "test:e2e": "cypress open", + "test:e2e:ci": "npm run resetDatabase && cd ../salix-front && cypress run", + "test": "echo \"See package.json => scripts for available tests.\" && exit 0", + "test:unit": "vitest", + "test:unit:ci": "vitest run", + "commitlint": "commitlint --edit", + "prepare": "npx husky install", + "addReferenceTag": "node .husky/addReferenceTag.js", + "docs:dev": "vitepress dev docs", + "docs:build": "vitepress build docs", + "docs:preview": "vitepress preview docs" + }, + "dependencies": { + "@quasar/cli": "^2.4.1", + "@quasar/extras": "^1.16.16", + "axios": "^1.4.0", + "chromium": "^3.0.3", + "croppie": "^2.6.5", + "moment": "^2.30.1", + "pinia": "^2.1.3", + "quasar": "^2.17.7", + "validator": "^13.9.0", + "vue": "^3.5.13", + "vue-i18n": "^9.3.0", + "vue-router": "^4.2.5" + }, + "devDependencies": { + "@commitlint/cli": "^19.2.1", + "@commitlint/config-conventional": "^19.1.0", + "@intlify/unplugin-vue-i18n": "^0.8.2", + "@pinia/testing": "^0.1.2", + "@quasar/app-vite": "^2.0.8", + "@quasar/quasar-app-extension-qcalendar": "^4.0.2", + "@quasar/quasar-app-extension-testing-unit-vitest": "^0.4.0", + "@vue/test-utils": "^2.4.4", + "autoprefixer": "^10.4.14", + "cypress": "^13.6.6", + "cypress-mochawesome-reporter": "^3.8.2", + "eslint": "^9.18.0", + "eslint-config-prettier": "^10.0.1", + "eslint-plugin-cypress": "^4.1.0", + "eslint-plugin-vue": "^9.32.0", + "husky": "^8.0.0", + "postcss": "^8.4.23", + "prettier": "^3.4.2", + "sass": "^1.83.4", + "vitepress": "^1.6.3", + "vitest": "^0.34.0" + }, + "engines": { + "node": "^20 || ^18 || ^16", + "npm": ">= 8.1.2", + "yarn": ">= 1.21.1", + "bun": ">= 1.0.25" + }, + "overrides": { + "@vitejs/plugin-vue": "^5.2.1", + "vite": "^6.0.11", + "vitest": "^0.31.1" + } } \ No newline at end of file diff --git a/src/boot/keyShortcut.js b/src/boot/keyShortcut.js index 5afb5b74a3..6da06c8bf0 100644 --- a/src/boot/keyShortcut.js +++ b/src/boot/keyShortcut.js @@ -1,6 +1,6 @@ export default { - mounted: function (el, binding) { - const shortcut = binding.value ?? '+'; + mounted(el, binding) { + const shortcut = binding.value || '+'; const { key, ctrl, alt, callback } = typeof shortcut === 'string' @@ -8,25 +8,24 @@ export default { key: shortcut, ctrl: true, alt: true, - callback: () => - document - .querySelector(`button[shortcut="${shortcut}"]`) - ?.click(), + callback: () => el?.click(), } : binding.value; + if (!el.hasAttribute('shortcut')) { + el.setAttribute('shortcut', key); + } + const handleKeydown = (event) => { if (event.key === key && (!ctrl || event.ctrlKey) && (!alt || event.altKey)) { callback(); } }; - // Attach the event listener to the window window.addEventListener('keydown', handleKeydown); - el._handleKeydown = handleKeydown; }, - unmounted: function (el) { + unmounted(el) { if (el._handleKeydown) { window.removeEventListener('keydown', el._handleKeydown); } diff --git a/src/components/ItemsFilterPanel.vue b/src/components/ItemsFilterPanel.vue index dc2a344354..48f607a308 100644 --- a/src/components/ItemsFilterPanel.vue +++ b/src/components/ItemsFilterPanel.vue @@ -282,7 +282,7 @@ const setCategoryList = (data) => { { + const [item] = filteredItems.value; + if (item) router.push({ name: item.name }); +}; @@ -188,10 +193,11 @@ function normalize(text) { filled dense autofocus + @keyup.enter.stop="searchModule()" /> - + - + - + es: diff --git a/src/components/TicketProblems.vue b/src/components/TicketProblems.vue index 2965396b11..a24735a5f3 100644 --- a/src/components/TicketProblems.vue +++ b/src/components/TicketProblems.vue @@ -2,26 +2,9 @@ defineProps({ row: { type: Object, required: true } }); - + - {{ $t('salesTicketsTable.noVerifiedData') }} - - - {{ $t('salesTicketsTable.purchaseRequest') }} - - - {{ $t('salesTicketsTable.notVisible') }} - - - {{ $t('salesTicketsTable.clientFrozen') }} - - - + {{ $t('salesTicketsTable.componentLack') }} - + + + {{ $t('ticket.summary.hasItemDelay') }} + + + + + {{ $t('salesTicketsTable.hasItemLost') }} + + + + {{ $t('salesTicketsTable.notVisible') }} + + + + {{ $t('ticketList.rounding') }} + + + + {{ $t('salesTicketsTable.purchaseRequest') }} + + + {{ $t('salesTicketsTable.noVerifiedData') }} + + + {{ $t('salesTicketsTable.clientFrozen') }} + + {{ $t('salesTicketsTable.tooLittle') }} diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue index 17fabf10dc..04b7c0a46a 100644 --- a/src/components/VnTable/VnTable.vue +++ b/src/components/VnTable/VnTable.vue @@ -500,7 +500,7 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) { { $props.rowClick && $props.rowClick(row); @@ -581,7 +581,6 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) { @@ -648,7 +647,7 @@ function handleSelection({ evt, added, rows: selectedRows }, rows) { color="primary" fab icon="add" - shortcut="+" + v-shortcut="'+'" data-cy="vnTableCreateBtn" /> @@ -807,12 +806,15 @@ es: .grid-two { display: grid; - grid-template-columns: repeat(auto-fit, minmax(150px, max-content)); - max-width: 100%; - margin: 0 auto; - overflow: scroll; - white-space: wrap; - width: 100%; + grid-template-columns: 2fr 2fr; + .vn-label-value { + flex-direction: column; + white-space: nowrap; + .fields { + display: flex; + } + } + white-space: nowrap; } .w-80 { diff --git a/src/components/__tests__/UserPanel.spec.js b/src/components/__tests__/UserPanel.spec.js new file mode 100644 index 0000000000..ac20f911e7 --- /dev/null +++ b/src/components/__tests__/UserPanel.spec.js @@ -0,0 +1,61 @@ +import { vi, describe, expect, it, beforeEach, beforeAll, afterEach } from 'vitest'; +import { createWrapper } from 'app/test/vitest/helper'; +import UserPanel from 'src/components/UserPanel.vue'; +import axios from 'axios'; +import { useState } from 'src/composables/useState'; + +describe('UserPanel', () => { + let wrapper; + let vm; + let state; + + beforeEach(() => { + wrapper = createWrapper(UserPanel, {}); + state = useState(); + state.setUser({ + id: 115, + name: 'itmanagement', + nickname: 'itManagementNick', + lang: 'en', + darkMode: false, + companyFk: 442, + warehouseFk: 1, + }); + wrapper = wrapper.wrapper; + vm = wrapper.vm; + }); + + afterEach(() => { + vi.clearAllMocks(); + }); + + it('should fetch warehouses data on mounted', async () => { + const fetchData = wrapper.findComponent({ name: 'FetchData' }); + expect(fetchData.props('url')).toBe('Warehouses'); + expect(fetchData.props('autoLoad')).toBe(true); + }); + + it('should toggle dark mode correctly and update preferences', async () => { + await vm.saveDarkMode(true); + expect(axios.patch).toHaveBeenCalledWith('/UserConfigs/115', { darkMode: true }); + expect(vm.user.darkMode).toBe(true); + vm.updatePreferences(); + expect(vm.darkMode).toBe(true); + }); + + it('should change user language and update preferences', async () => { + const userLanguage = 'es'; + await vm.saveLanguage(userLanguage); + expect(axios.patch).toHaveBeenCalledWith('/VnUsers/115', { lang: userLanguage }); + expect(vm.user.lang).toBe(userLanguage); + vm.updatePreferences(); + expect(vm.locale).toBe(userLanguage); + }); + + it('should update user data', async () => { + const key = 'name'; + const value = 'itboss'; + await vm.saveUserData(key, value); + expect(axios.post).toHaveBeenCalledWith('UserConfigs/setUserConfig', { [key]: value }); + }); +}); diff --git a/src/components/common/VnDmsList.vue b/src/components/common/VnDmsList.vue index 36c87bab05..424781a269 100644 --- a/src/components/common/VnDmsList.vue +++ b/src/components/common/VnDmsList.vue @@ -17,7 +17,7 @@ import { useSession } from 'src/composables/useSession'; const route = useRoute(); const quasar = useQuasar(); const { t } = useI18n(); -const rows = ref(); +const rows = ref([]); const dmsRef = ref(); const formDialog = ref({}); const token = useSession().getTokenMultimedia(); @@ -389,6 +389,14 @@ defineExpose({ + + + {{ t('No data to display') }} + + @@ -405,7 +413,7 @@ defineExpose({ fab color="primary" icon="add" - shortcut="+" + v-shortcut @click="showFormDialog()" class="fill-icon" > diff --git a/src/components/common/VnLog.vue b/src/components/common/VnLog.vue index fdf2e52ee6..d870e487f5 100644 --- a/src/components/common/VnLog.vue +++ b/src/components/common/VnLog.vue @@ -268,7 +268,7 @@ async function applyFilter() { filter.where.and.push(selectedFilters.value); } - paginate.value.fetch(filter); + paginate.value.fetch({ filter }); } function setDate(type) { @@ -404,7 +404,7 @@ watch( ref="paginate" :data-key="`${model}Log`" :url="`${model}Logs`" - :filter="filter" + :user-filter="filter" :skeleton="false" auto-load @on-fetch="setLogTree" diff --git a/src/components/common/VnSelectWorker.vue b/src/components/common/VnSelectWorker.vue index 8d60eb014b..961804be15 100644 --- a/src/components/common/VnSelectWorker.vue +++ b/src/components/common/VnSelectWorker.vue @@ -53,6 +53,7 @@ const url = computed(() => { :fields="['id', 'name', 'nickname', 'code']" :filter-options="['id', 'name', 'nickname', 'code']" sort-by="nickname ASC" + data-cy="vnWorkerSelect" > diff --git a/src/components/common/__tests__/VnNotes.spec.js b/src/components/common/__tests__/VnNotes.spec.js index 8f24a7f142..2603bf03c9 100644 --- a/src/components/common/__tests__/VnNotes.spec.js +++ b/src/components/common/__tests__/VnNotes.spec.js @@ -1,51 +1,78 @@ -import { describe, it, expect, vi, beforeAll, afterEach, beforeEach } from 'vitest'; +import { + describe, + it, + expect, + vi, + beforeAll, + afterEach, + beforeEach, + afterAll, +} from 'vitest'; import { createWrapper, axios } from 'app/test/vitest/helper'; import VnNotes from 'src/components/ui/VnNotes.vue'; +import vnDate from 'src/boot/vnDate'; describe('VnNotes', () => { let vm; let wrapper; let spyFetch; let postMock; - let expectedBody; - const mockData= {name: 'Tony', lastName: 'Stark', text: 'Test Note', observationTypeFk: 1}; - - function generateExpectedBody() { - expectedBody = {...vm.$props.body, ...{ text: vm.newNote.text, observationTypeFk: vm.newNote.observationTypeFk }}; - } - - async function setTestParams(text, observationType, type){ - vm.newNote.text = text; - vm.newNote.observationTypeFk = observationType; - wrapper.setProps({ selectType: type }); - } - - beforeAll(async () => { - vi.spyOn(axios, 'get').mockReturnValue({ data: [] }); - + let patchMock; + let expectedInsertBody; + let expectedUpdateBody; + const defaultOptions = { + url: '/test', + body: { name: 'Tony', lastName: 'Stark' }, + selectType: false, + saveUrl: null, + justInput: false, + }; + function generateWrapper( + options = defaultOptions, + text = null, + observationType = null, + ) { + vi.spyOn(axios, 'get').mockResolvedValue({ data: [] }); wrapper = createWrapper(VnNotes, { - propsData: { - url: '/test', - body: { name: 'Tony', lastName: 'Stark' }, - } + propsData: options, }); wrapper = wrapper.wrapper; vm = wrapper.vm; - }); + vm.newNote.text = text; + vm.newNote.observationTypeFk = observationType; + } + + function createSpyFetch() { + spyFetch = vi.spyOn(vm.$refs.vnPaginateRef, 'fetch'); + } + + function generateExpectedBody() { + expectedInsertBody = { + ...vm.$props.body, + ...{ text: vm.newNote.text, observationTypeFk: vm.newNote.observationTypeFk }, + }; + expectedUpdateBody = { ...vm.$props.body, ...{ notes: vm.newNote.text } }; + } beforeEach(() => { - postMock = vi.spyOn(axios, 'post').mockResolvedValue(mockData); - spyFetch = vi.spyOn(vm.vnPaginateRef, 'fetch').mockImplementation(() => vi.fn()); + postMock = vi.spyOn(axios, 'post'); + patchMock = vi.spyOn(axios, 'patch'); }); afterEach(() => { vi.clearAllMocks(); - expectedBody = {}; + expectedInsertBody = {}; + expectedUpdateBody = {}; + }); + + afterAll(() => { + vi.restoreAllMocks(); }); describe('insert', () => { - it('should not call axios.post and vnPaginateRef.fetch if newNote.text is null', async () => { - await setTestParams( null, null, true ); + it('should not call axios.post and vnPaginateRef.fetch when newNote.text is null', async () => { + generateWrapper({ selectType: true }); + createSpyFetch(); await vm.insert(); @@ -53,8 +80,9 @@ describe('VnNotes', () => { expect(spyFetch).not.toHaveBeenCalled(); }); - it('should not call axios.post and vnPaginateRef.fetch if newNote.text is empty', async () => { - await setTestParams( "", null, false ); + it('should not call axios.post and vnPaginateRef.fetch when newNote.text is empty', async () => { + generateWrapper(null, ''); + createSpyFetch(); await vm.insert(); @@ -62,8 +90,9 @@ describe('VnNotes', () => { expect(spyFetch).not.toHaveBeenCalled(); }); - it('should not call axios.post and vnPaginateRef.fetch if observationTypeFk is missing and selectType is true', async () => { - await setTestParams( "Test Note", null, true ); + it('should not call axios.post and vnPaginateRef.fetch when observationTypeFk is null and selectType is true', async () => { + generateWrapper({ selectType: true }, 'Test Note'); + createSpyFetch(); await vm.insert(); @@ -71,37 +100,57 @@ describe('VnNotes', () => { expect(spyFetch).not.toHaveBeenCalled(); }); - it('should call axios.post and vnPaginateRef.fetch if observationTypeFk is missing and selectType is false', async () => { - await setTestParams( "Test Note", null, false ); - + it('should call axios.post and vnPaginateRef.fetch when observationTypeFk is missing and selectType is false', async () => { + generateWrapper(null, 'Test Note'); + createSpyFetch(); generateExpectedBody(); await vm.insert(); - expect(postMock).toHaveBeenCalledWith(vm.$props.url, expectedBody); - expect(spyFetch).toHaveBeenCalled(); - }); - - it('should call axios.post and vnPaginateRef.fetch if observationTypeFk is setted and selectType is false', async () => { - await setTestParams( "Test Note", 1, false ); - - generateExpectedBody(); - - await vm.insert(); - - expect(postMock).toHaveBeenCalledWith(vm.$props.url, expectedBody); + expect(postMock).toHaveBeenCalledWith(vm.$props.url, expectedInsertBody); expect(spyFetch).toHaveBeenCalled(); }); it('should call axios.post and vnPaginateRef.fetch when newNote is valid', async () => { - await setTestParams( "Test Note", 1, true ); - + generateWrapper({ selectType: true }, 'Test Note', 1); + createSpyFetch(); generateExpectedBody(); - + await vm.insert(); - expect(postMock).toHaveBeenCalledWith(vm.$props.url, expectedBody); + expect(postMock).toHaveBeenCalledWith(vm.$props.url, expectedInsertBody); expect(spyFetch).toHaveBeenCalled(); }); }); -}); \ No newline at end of file + + describe('update', () => { + it('should call axios.patch with saveUrl when saveUrl is set and justInput is true', async () => { + generateWrapper({ + url: '/business', + justInput: true, + saveUrl: '/saveUrlTest', + }); + generateExpectedBody(); + + await vm.update(); + + expect(patchMock).toHaveBeenCalledWith(vm.$props.saveUrl, expectedUpdateBody); + }); + + it('should call axios.patch with url when saveUrl is not set and justInput is true', async () => { + generateWrapper({ + url: '/business', + body: { workerFk: 1110 }, + justInput: true, + }); + generateExpectedBody(); + + await vm.update(); + + expect(patchMock).toHaveBeenCalledWith( + `${vm.$props.url}/${vm.$props.body.workerFk}`, + expectedUpdateBody, + ); + }); + }); +}); diff --git a/src/components/ui/CardDescriptor.vue b/src/components/ui/CardDescriptor.vue index eb5fc1c78c..275d919d6b 100644 --- a/src/components/ui/CardDescriptor.vue +++ b/src/components/ui/CardDescriptor.vue @@ -74,7 +74,7 @@ onBeforeMount(async () => { () => [$props.url, $props.filter], async () => { if (!isSameDataKey.value) await getData(); - } + }, ); }); @@ -109,7 +109,7 @@ const iconModule = computed(() => route.matched[1].meta.icon); const toModule = computed(() => route.matched[1].path.split('/').length > 2 ? route.matched[1].redirect - : route.matched[1].children[0].redirect + : route.matched[1].children[0].redirect, ); diff --git a/src/components/ui/CardSummary.vue b/src/components/ui/CardSummary.vue index f0a87f84e3..6a61994c1d 100644 --- a/src/components/ui/CardSummary.vue +++ b/src/components/ui/CardSummary.vue @@ -15,6 +15,10 @@ const props = defineProps({ type: Object, default: null, }, + userFilter: { + type: Object, + default: null, + }, entityId: { type: [Number, String], default: null, @@ -34,6 +38,7 @@ const isSummary = ref(); const arrayData = useArrayData(props.dataKey, { url: props.url, filter: props.filter, + userFilter: props.userFilter, skip: 0, oneRecord: true, }); @@ -204,4 +209,13 @@ async function fetch() { .summaryHeader { color: $white; } + +.cardSummary :deep(.q-card__section[content]) { + display: flex; + flex-wrap: wrap; + padding: 0; + > * { + flex: 1; + } +} diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue index 93f069cc6c..80128018a6 100644 --- a/src/components/ui/VnFilterPanel.vue +++ b/src/components/ui/VnFilterPanel.vue @@ -114,7 +114,7 @@ async function clearFilters() { arrayData.resetPagination(); // Filtrar los params no removibles const removableFilters = Object.keys(userParams.value).filter((param) => - $props.unremovableParams.includes(param) + $props.unremovableParams.includes(param), ); const newParams = {}; // Conservar solo los params que no son removibles @@ -162,13 +162,13 @@ const formatTags = (tags) => { const tags = computed(() => { const filteredTags = tagsList.value.filter( - (tag) => !($props.customTags || []).includes(tag.label) + (tag) => !($props.customTags || []).includes(tag.label), ); return formatTags(filteredTags); }); const customTags = computed(() => - tagsList.value.filter((tag) => ($props.customTags || []).includes(tag.label)) + tagsList.value.filter((tag) => ($props.customTags || []).includes(tag.label)), ); async function remove(key) { @@ -188,10 +188,13 @@ function formatValue(value) { const getLocale = (label) => { const param = label.split('.').at(-1); const globalLocale = `globals.params.${param}`; + const moduleName = route.meta.moduleName; + const moduleLocale = `${moduleName.toLowerCase()}.${param}`; if (te(globalLocale)) return t(globalLocale); - else if (te(t(`params.${param}`))); + else if (te(moduleLocale)) return t(moduleLocale); else { - const camelCaseModuleName = route.meta.moduleName.charAt(0).toLowerCase() + route.meta.moduleName.slice(1); + const camelCaseModuleName = + moduleName.charAt(0).toLowerCase() + moduleName.slice(1); return t(`${camelCaseModuleName}.params.${param}`); } }; diff --git a/src/components/ui/VnNotes.vue b/src/components/ui/VnNotes.vue index 1690a94ba1..5b1d6e726d 100644 --- a/src/components/ui/VnNotes.vue +++ b/src/components/ui/VnNotes.vue @@ -1,6 +1,6 @@ { auto-load @on-fetch="(data) => (observationTypes = data)" /> - - + + + {{ t('New note') }} @@ -75,19 +138,19 @@ onBeforeRouteLeave((to, from, next) => { v-model="newNote.observationTypeFk" option-label="description" style="flex: 0.15" - :required="true" + :required="isRequired" @keyup.enter.stop="insert" /> { icon="save" color="primary" flat - @click="insert" + @click="handleClick" class="q-mb-xs" dense data-cy="saveNote" @@ -106,6 +169,7 @@ onBeforeRouteLeave((to, from, next) => { { } } } +.just-input { + padding-right: 18px; + margin-bottom: 2px; + box-shadow: none; +} es: @@ -205,4 +274,6 @@ onBeforeRouteLeave((to, from, next) => { New note: Nueva nota Save (Enter): Guardar (Intro) Observation type: Tipo de observación + New note is empty: La nueva nota esta vacia + Are you sure remove this note?: Estas seguro de quitar esta nota? diff --git a/src/components/ui/VnPaginate.vue b/src/components/ui/VnPaginate.vue index 7bb81591f0..b067381f65 100644 --- a/src/components/ui/VnPaginate.vue +++ b/src/components/ui/VnPaginate.vue @@ -123,7 +123,7 @@ watch( () => props.data, () => { store.data = props.data; - } + }, ); watch( @@ -132,12 +132,12 @@ watch( if (!mounted.value) return; emit('onChange', data); }, - { immediate: true } + { immediate: true }, ); watch( () => [props.url, props.filter], - ([url, filter]) => mounted.value && fetch({ url, filter }) + ([url, filter]) => mounted.value && fetch({ url, filter }), ); const addFilter = async (filter, params) => { await arrayData.addFilter({ filter, params }); @@ -198,7 +198,7 @@ function endPagination() { async function onLoad(index, done) { if (!store.data || !mounted.value) return done(); - if (store.data.length === 0 || !props.url) return done(false); + if (store.data.length === 0 || !arrayData.store.url) return done(false); pagination.value.page = pagination.value.page + 1; diff --git a/src/components/ui/VnSubToolbar.vue b/src/components/ui/VnSubToolbar.vue index 5ded4be001..8d4126d1d5 100644 --- a/src/components/ui/VnSubToolbar.vue +++ b/src/components/ui/VnSubToolbar.vue @@ -19,23 +19,26 @@ onMounted(() => { const observer = new MutationObserver( () => (hasContent.value = - actions.value?.childNodes?.length + data.value?.childNodes?.length) + actions.value?.childNodes?.length + data.value?.childNodes?.length), ); if (actions.value) observer.observe(actions.value, opts); if (data.value) observer.observe(data.value, opts); }); -onBeforeUnmount(() => stateStore.toggleSubToolbar()); +const actionsChildCount = () => !!actions.value?.childNodes?.length; + +onBeforeUnmount(() => stateStore.toggleSubToolbar() && hasSubToolbar); - + + diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js index 2423f92e7c..fcc61972a9 100644 --- a/src/composables/useArrayData.js +++ b/src/composables/useArrayData.js @@ -94,6 +94,9 @@ export function useArrayData(key, userOptions) { if (params.filter.where || exprFilter) params.filter.where = { ...params.filter.where, ...exprFilter }; + + if (!params?.filter?.order?.length) delete params?.filter?.order; + params.filter = JSON.stringify(params.filter); store.isLoading = true; diff --git a/src/css/app.scss b/src/css/app.scss index 7296b079f3..59e945f05c 100644 --- a/src/css/app.scss +++ b/src/css/app.scss @@ -212,6 +212,10 @@ select:-webkit-autofill { justify-content: center; } +.q-card__section[dense] { + padding: 0; +} + input[type='number'] { -moz-appearance: textfield; } diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml index 8a9b0c889d..f8af279537 100644 --- a/src/i18n/locale/en.yml +++ b/src/i18n/locale/en.yml @@ -332,10 +332,13 @@ globals: wasteRecalc: Waste recaclulate operator: Operator parking: Parking + vehicleList: Vehicles + vehicle: Vehicle unsavedPopup: title: Unsaved changes will be lost subtitle: Are you sure exit without saving? params: + description: Description clientFk: Client id salesPersonFk: Sales person warehouseFk: Warehouse @@ -358,7 +361,13 @@ globals: correctingFk: Rectificative daysOnward: Days onward countryFk: Country + countryCodeFk: Country companyFk: Company + model: Model + fuel: Fuel + active: Active + inactive: Inactive + deliveryPoint: Delivery point errors: statusUnauthorized: Access denied statusInternalServerError: An internal server error has ocurred @@ -378,7 +387,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: >- @@ -457,48 +466,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/i18n/locale/es.yml b/src/i18n/locale/es.yml index acfe181fed..ed1f8eceb6 100644 --- a/src/i18n/locale/es.yml +++ b/src/i18n/locale/es.yml @@ -332,10 +332,13 @@ globals: wasteRecalc: Recalcular mermas operator: Operario parking: Parking + vehicleList: Vehículos + vehicle: Vehículo unsavedPopup: title: Los cambios que no haya guardado se perderán subtitle: ¿Seguro que quiere salir sin guardar? params: + description: Descripción clientFk: Id cliente salesPersonFk: Comercial warehouseFk: Almacén @@ -356,6 +359,7 @@ globals: daysOnward: Días adelante packing: ITP countryFk: País + countryCodeFk: País companyFk: Empresa errors: statusUnauthorized: Acceso denegado diff --git a/src/layouts/MainLayout.vue b/src/layouts/MainLayout.vue index 2a84e5aa15..3ad1c79bc6 100644 --- a/src/layouts/MainLayout.vue +++ b/src/layouts/MainLayout.vue @@ -2,7 +2,7 @@ import Navbar from 'src/components/NavBar.vue'; - + diff --git a/src/pages/Account/Card/AccountMailAlias.vue b/src/pages/Account/Card/AccountMailAlias.vue index efd2b481b2..54798d6ab4 100644 --- a/src/pages/Account/Card/AccountMailAlias.vue +++ b/src/pages/Account/Card/AccountMailAlias.vue @@ -86,7 +86,7 @@ watch( () => route.params.id, () => { getAccountData(); - } + }, ); onMounted(async () => await getAccountData(false)); @@ -130,7 +130,8 @@ onMounted(async () => await getAccountData(false)); openConfirmationModal( t('User will be removed from alias'), t('¿Seguro que quieres continuar?'), - () => deleteMailAlias(row, rows, rowIndex) + () => + deleteMailAlias(row, rows, rowIndex), ) " > @@ -157,7 +158,7 @@ onMounted(async () => await getAccountData(false)); icon="add" color="primary" @click="openCreateMailAliasForm()" - shortcut="+" + v-shortcut="'+'" > {{ t('warehouses.add') }} diff --git a/src/pages/Account/Role/Card/SubRoles.vue b/src/pages/Account/Role/Card/SubRoles.vue index 6cac94667d..d1a1463750 100644 --- a/src/pages/Account/Role/Card/SubRoles.vue +++ b/src/pages/Account/Role/Card/SubRoles.vue @@ -63,7 +63,7 @@ watch( store.url = urlPath.value; store.filter = filter.value; fetchSubRoles(); - } + }, ); const fetchSubRoles = () => paginateRef.value.fetch(); @@ -109,7 +109,7 @@ const redirectToRoleSummary = (id) => openConfirmationModal( t('El rol va a ser eliminado'), t('¿Seguro que quieres continuar?'), - () => deleteSubRole(row, rows, rowIndex) + () => deleteSubRole(row, rows, rowIndex), ) " > @@ -131,7 +131,7 @@ const redirectToRoleSummary = (id) => diff --git a/src/pages/Claim/Card/ClaimLines.vue b/src/pages/Claim/Card/ClaimLines.vue index 7c545b15b1..27d6140492 100644 --- a/src/pages/Claim/Card/ClaimLines.vue +++ b/src/pages/Claim/Card/ClaimLines.vue @@ -57,7 +57,6 @@ function onFetch(rows, newRows) { const price = row.quantity * sale.price; const discount = (sale.discount * price) / 100; amountClaimed.value = amountClaimed.value + (price - discount); - } } @@ -208,7 +207,6 @@ async function saveWhenHasChanges() { selection="multiple" v-model:selected="selected" :grid="$q.screen.lt.md" - > @@ -319,7 +317,13 @@ async function saveWhenHasChanges() { - + @@ -330,9 +334,10 @@ async function saveWhenHasChanges() { width: 100%; } .grid-style-transition { - transition: transform 0.28s, background-color 0.28s; + transition: + transform 0.28s, + background-color 0.28s; } - diff --git a/src/pages/Claim/Card/ClaimPhoto.vue b/src/pages/Claim/Card/ClaimPhoto.vue index ec619cc7d4..fb2f818c16 100644 --- a/src/pages/Claim/Card/ClaimPhoto.vue +++ b/src/pages/Claim/Card/ClaimPhoto.vue @@ -61,7 +61,7 @@ watch( () => { claimDmsFilter.value.where.id = router.currentRoute.value.params.id; claimDmsRef.value.fetch(); - } + }, ); function openDialog(dmsId) { @@ -249,7 +249,7 @@ function onDrag() { diff --git a/src/pages/Customer/Card/CustomerAddress.vue b/src/pages/Customer/Card/CustomerAddress.vue index 657cc7ae77..b94c414544 100644 --- a/src/pages/Customer/Card/CustomerAddress.vue +++ b/src/pages/Customer/Card/CustomerAddress.vue @@ -61,7 +61,7 @@ watch( (newValue) => { if (!newValue) return; getClientData(newValue); - } + }, ); const getClientData = async (id) => { @@ -137,7 +137,7 @@ const toCustomerAddressEdit = (addressId) => { { t( isDefaultAddress(item) ? 'Default address' - : 'Set as default' + : 'Set as default', ) }} @@ -216,7 +216,7 @@ const toCustomerAddressEdit = (addressId) => { color="primary" fab icon="add" - shortcut="+" + v-shortcut="'+'" /> {{ t('New consignee') }} diff --git a/src/pages/Customer/Card/CustomerBalance.vue b/src/pages/Customer/Card/CustomerBalance.vue index 04ef5f882f..11db92eab9 100644 --- a/src/pages/Customer/Card/CustomerBalance.vue +++ b/src/pages/Customer/Card/CustomerBalance.vue @@ -158,7 +158,7 @@ const columns = computed(() => [ openConfirmationModal( t('Send compensation'), t('Do you want to report compensation to the client by mail?'), - () => sendEmail(`Receipts/${id}/balance-compensation-email`) + () => sendEmail(`Receipts/${id}/balance-compensation-email`), ), }, ], @@ -291,7 +291,7 @@ const showBalancePdf = ({ id }) => { color="primary" fab icon="add" - shortcut="+" + v-shortcut="'+'" /> {{ t('New payment') }} diff --git a/src/pages/Customer/Card/CustomerContacts.vue b/src/pages/Customer/Card/CustomerContacts.vue index c420f650e8..d03f712446 100644 --- a/src/pages/Customer/Card/CustomerContacts.vue +++ b/src/pages/Customer/Card/CustomerContacts.vue @@ -62,7 +62,7 @@ const customerContactsRef = ref(null); color="primary" flat icon="add" - shortcut="+" + v-shortcut="'+'" > {{ t('Add contact') }} diff --git a/src/pages/Customer/Card/CustomerCreditContracts.vue b/src/pages/Customer/Card/CustomerCreditContracts.vue index 0ff074793c..09f7d2ee31 100644 --- a/src/pages/Customer/Card/CustomerCreditContracts.vue +++ b/src/pages/Customer/Card/CustomerCreditContracts.vue @@ -195,7 +195,7 @@ const updateData = () => { color="primary" fab icon="add" - shortcut="+" + v-shortcut="'+'" /> {{ t('New contract') }} diff --git a/src/pages/Customer/Card/CustomerDescriptor.vue b/src/pages/Customer/Card/CustomerDescriptor.vue index f7e19444e7..a646ad299e 100644 --- a/src/pages/Customer/Card/CustomerDescriptor.vue +++ b/src/pages/Customer/Card/CustomerDescriptor.vue @@ -199,6 +199,7 @@ const debtWarning = computed(() => { query: { createForm: JSON.stringify({ clientFk: entity.id, + addressId: entity.defaultAddressFk, }), }, }" diff --git a/src/pages/Customer/Card/CustomerFileManagement.vue b/src/pages/Customer/Card/CustomerFileManagement.vue index 134d8dbd68..b565db6e72 100644 --- a/src/pages/Customer/Card/CustomerFileManagement.vue +++ b/src/pages/Customer/Card/CustomerFileManagement.vue @@ -236,7 +236,7 @@ const toCustomerFileManagementCreate = () => { @click.stop="toCustomerFileManagementCreate()" color="primary" fab - shortcut="+" + v-shortcut="'+'" icon="add" /> diff --git a/src/pages/Customer/Card/CustomerNotes.vue b/src/pages/Customer/Card/CustomerNotes.vue index b851746963..189b599042 100644 --- a/src/pages/Customer/Card/CustomerNotes.vue +++ b/src/pages/Customer/Card/CustomerNotes.vue @@ -23,5 +23,6 @@ const noteFilter = computed(() => { :body="{ clientFk: route.params.id }" style="overflow-y: auto" :select-type="true" + required /> diff --git a/src/pages/Customer/Card/CustomerSamples.vue b/src/pages/Customer/Card/CustomerSamples.vue index f126911123..19a7f87594 100644 --- a/src/pages/Customer/Card/CustomerSamples.vue +++ b/src/pages/Customer/Card/CustomerSamples.vue @@ -104,7 +104,7 @@ const tableRef = ref(); color="primary" fab icon="add" - shortcut="+" + v-shortcut="'+'" /> {{ t('Send sample') }} diff --git a/src/pages/Customer/components/CustomerAddressEdit.vue b/src/pages/Customer/components/CustomerAddressEdit.vue index 10d5107e20..5b36650f7e 100644 --- a/src/pages/Customer/components/CustomerAddressEdit.vue +++ b/src/pages/Customer/components/CustomerAddressEdit.vue @@ -49,7 +49,7 @@ const getData = async (observations) => { notes.value = originalNotes .map((observation) => { const type = observationTypes.value.find( - (type) => type.id === observation.observationTypeFk + (type) => type.id === observation.observationTypeFk, ); return type ? { @@ -112,8 +112,8 @@ function getPayload() { (oNote) => oNote.id === note.id && (note.description !== oNote.description || - note.observationTypeFk !== oNote.observationTypeFk) - ) + note.observationTypeFk !== oNote.observationTypeFk), + ), ) .map((note) => ({ data: note, @@ -130,9 +130,7 @@ async function handleDialog(data) { .dialog({ component: VnConfirm, componentProps: { - title: t( - 'confirmTicket' - ), + title: t('confirmTicket'), message: t('confirmDeletionMessage'), }, }) @@ -341,7 +339,7 @@ function handleLocation(data, location) { class="cursor-pointer add-icon q-mt-md" flat icon="add" - shortcut="+" + v-shortcut="'+'" > {{ t('Add note') }} diff --git a/src/pages/Customer/composables/getAddresses.js b/src/pages/Customer/composables/getAddresses.js index eecc0150be..e65e644559 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 ecacc67c08..3b9e811de8 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/Department/Card/DepartmentDescriptor.vue b/src/pages/Department/Card/DepartmentDescriptor.vue index 17bf2627ce..ecd7fa36c6 100644 --- a/src/pages/Department/Card/DepartmentDescriptor.vue +++ b/src/pages/Department/Card/DepartmentDescriptor.vue @@ -56,7 +56,7 @@ const { openConfirmationModal } = useVnConfirm(); openConfirmationModal( t('Are you sure you want to delete it?'), t('Delete department'), - removeDepartment + removeDepartment, ) " > diff --git a/src/pages/Entry/Card/EntryNotes.vue b/src/pages/Entry/Card/EntryNotes.vue index 55cac04379..459c3b069a 100644 --- a/src/pages/Entry/Card/EntryNotes.vue +++ b/src/pages/Entry/Card/EntryNotes.vue @@ -17,7 +17,7 @@ const selected = ref([]); const sortEntryObservationOptions = (data) => { entryObservationsOptions.value = [...data].sort((a, b) => - a.description.localeCompare(b.description) + a.description.localeCompare(b.description), ); }; @@ -142,7 +142,7 @@ const columns = computed(() => [ fab color="primary" icon="add" - shortcut="+" + v-shortcut="'+'" @click="entryObservationsRef.insert()" /> diff --git a/src/pages/Entry/EntryBuysTableDialog.vue b/src/pages/Entry/EntryBuysTableDialog.vue index a2d8c9117f..86a9b018f4 100644 --- a/src/pages/Entry/EntryBuysTableDialog.vue +++ b/src/pages/Entry/EntryBuysTableDialog.vue @@ -134,6 +134,7 @@ function downloadCSV(rows) { @click=" openReport(`Entries/${entityId}/labelSupplier`) " + data-cy="printLabelsBtn" /> diff --git a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue index c01ec4ab4a..a3beabdb66 100644 --- a/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue +++ b/src/pages/InvoiceIn/Card/InvoiceInBasicData.vue @@ -215,7 +215,7 @@ function deleteFile(dmsFk) { v-else icon="add_circle" round - shortcut="+" + v-shortcut="'+'" padding="xs" @click=" () => { diff --git a/src/pages/InvoiceIn/Card/InvoiceInDueDay.vue b/src/pages/InvoiceIn/Card/InvoiceInDueDay.vue index d2c6d0a2d4..cb3271dc12 100644 --- a/src/pages/InvoiceIn/Card/InvoiceInDueDay.vue +++ b/src/pages/InvoiceIn/Card/InvoiceInDueDay.vue @@ -232,7 +232,7 @@ async function insert() { [ code != 'EUR'; function taxRate(invoiceInTax) { const sageTaxTypeId = invoiceInTax.taxTypeSageFk; const taxRateSelection = sageTaxTypes.value.find( - (transaction) => transaction.id == sageTaxTypeId + (transaction) => transaction.id == sageTaxTypeId, ); const taxTypeSage = taxRateSelection?.rate ?? 0; const taxableBase = invoiceInTax?.taxableBase ?? 0; @@ -131,14 +131,14 @@ function autocompleteExpense(evt, row, col) { const param = isNaN(val) ? row[col.model] : val; const lookup = expenses.value.find( - ({ id }) => id == useAccountShortToStandard(param) + ({ id }) => id == useAccountShortToStandard(param), ); expenseRef.value.vnSelectDialogRef.vnSelectRef.toggleOption(lookup); } -const taxableBaseTotal = computed(() => { - return getTotal(invoiceInFormRef.value.formData, 'taxableBase', ); +const taxableBaseTotal = computed(() => { + return getTotal(invoiceInFormRef.value.formData, 'taxableBase'); }); const taxRateTotal = computed(() => { @@ -147,13 +147,9 @@ const taxRateTotal = computed(() => { }); }); - const combinedTotal = computed(() => { return +taxableBaseTotal.value + +taxRateTotal.value; }); - - - { row.taxableBase = await getExchange( val, row.currencyFk, - invoiceIn.issued + invoiceIn.issued, ); } " @@ -426,7 +422,7 @@ const combinedTotal = computed(() => { color="primary" icon="add" size="lg" - shortcut="+" + v-shortcut="'+'" round @click="invoiceInFormRef.insert()" > diff --git a/src/pages/InvoiceIn/InvoiceInList.vue b/src/pages/InvoiceIn/InvoiceInList.vue index 805f41726d..e1723e3b17 100644 --- a/src/pages/InvoiceIn/InvoiceInList.vue +++ b/src/pages/InvoiceIn/InvoiceInList.vue @@ -177,7 +177,7 @@ const cols = computed(() => [ :required="true" /> [ :required="true" /> diff --git a/src/pages/Item/Card/ItemBarcode.vue b/src/pages/Item/Card/ItemBarcode.vue index 6db5943c77..590b524cd8 100644 --- a/src/pages/Item/Card/ItemBarcode.vue +++ b/src/pages/Item/Card/ItemBarcode.vue @@ -92,7 +92,7 @@ const submit = async (rows) => { class="cursor-pointer fill-icon-on-hover" color="primary" icon="add_circle" - shortcut="+" + v-shortcut="'+'" flat > diff --git a/src/pages/Item/Card/ItemDiary.vue b/src/pages/Item/Card/ItemDiary.vue index c2f2c19a02..4b6775183c 100644 --- a/src/pages/Item/Card/ItemDiary.vue +++ b/src/pages/Item/Card/ItemDiary.vue @@ -125,7 +125,7 @@ onMounted(async () => { inventoriedDate.value = (await axios.get('Configs/findOne')).data?.inventoried || today; - if (query.warehouseFk) ref.warehouseFk = query.warehouseFk; + if (query.warehouseFk) ref.warehouseFk = +query.warehouseFk; else if (!ref.warehouseFk && user.value) ref.warehouseFk = user.value.warehouseFk; if (ref.date) showWhatsBeforeInventory.value = true; ref.itemFk = route.params.id; @@ -143,7 +143,7 @@ onMounted(async () => { const fetchItemBalances = async () => await arrayDataItemBalances.fetch({}); const getBadgeAttrs = (_date) => { - const isSameDate = date.isSameDate(today.value, _date); + const isSameDate = date.isSameDate(today, _date); const attrs = { 'text-color': isSameDate ? 'black' : 'white', color: isSameDate ? 'warning' : 'transparent', @@ -153,8 +153,6 @@ const getBadgeAttrs = (_date) => { const scrollToToday = async () => { await nextTick(); - const today = Date.vnNew(); - today.setHours(0, 0, 0, 0); const todayCell = document.querySelector(`td[data-date="${today.toISOString()}"]`); if (todayCell) { todayCell.scrollIntoView({ behavior: 'smooth', block: 'center' }); diff --git a/src/pages/Item/Card/ItemTags.vue b/src/pages/Item/Card/ItemTags.vue index 5876cf8dc1..ed23ab5a68 100644 --- a/src/pages/Item/Card/ItemTags.vue +++ b/src/pages/Item/Card/ItemTags.vue @@ -175,7 +175,7 @@ const insertTag = (rows) => { @click="insertTag(rows)" color="primary" icon="add" - shortcut="+" + v-shortcut="'+'" fab > diff --git a/src/pages/Item/ItemTypeList.vue b/src/pages/Item/ItemTypeList.vue index 93e98a8ae8..d4030e1d89 100644 --- a/src/pages/Item/ItemTypeList.vue +++ b/src/pages/Item/ItemTypeList.vue @@ -5,6 +5,9 @@ import VnTable from 'components/VnTable/VnTable.vue'; import FetchData from 'components/FetchData.vue'; import WorkerDescriptorProxy from '../Worker/Card/WorkerDescriptorProxy.vue'; import VnSection from 'src/components/common/VnSection.vue'; +import VnInput from 'src/components/common/VnInput.vue'; +import VnSelectWorker from 'src/components/common/VnSelectWorker.vue'; +import VnSelect from 'src/components/common/VnSelect.vue'; const { t } = useI18n(); const tableRef = ref(); @@ -60,20 +63,17 @@ const columns = computed(() => [ label: t('code'), isTitle: true, cardVisible: true, - create: true, }, { align: 'left', name: 'name', label: t('globals.name'), cardVisible: true, - create: true, }, { align: 'left', label: t('worker'), name: 'workerFk', - create: true, component: 'select', attrs: { url: 'Workers/search', @@ -100,7 +100,6 @@ const columns = computed(() => [ align: 'left', name: 'categoryFk', label: t('ItemCategory'), - create: true, component: 'select', attrs: { options: itemCategoriesOptions.value, @@ -112,7 +111,6 @@ const columns = computed(() => [ align: 'left', name: 'Temperature', label: t('Temperature'), - create: true, component: 'select', attrs: { options: temperatureOptions.value, @@ -180,6 +178,29 @@ const columns = computed(() => [ + + + + + + + diff --git a/src/pages/Monitor/Ticket/MonitorTickets.vue b/src/pages/Monitor/Ticket/MonitorTickets.vue index e6b4631a00..3b5dccb56f 100644 --- a/src/pages/Monitor/Ticket/MonitorTickets.vue +++ b/src/pages/Monitor/Ticket/MonitorTickets.vue @@ -293,7 +293,7 @@ const columns = computed(() => [ title: t('globals.preview'), icon: 'preview', color: 'primary', - action: (row) => viewSummary(row.id, TicketSummary), + action: (row) => viewSummary(row.id, TicketSummary, 'lg-width'), isPrimary: true, attrs: { flat: true, diff --git a/src/pages/Monitor/locale/en.yml b/src/pages/Monitor/locale/en.yml index 21324087c4..496c8761a1 100644 --- a/src/pages/Monitor/locale/en.yml +++ b/src/pages/Monitor/locale/en.yml @@ -38,6 +38,7 @@ salesTicketsTable: payMethod: Pay method department: Department packing: ITP + hasItemLost: Item lost searchBar: label: Search tickets info: Search tickets by id or alias diff --git a/src/pages/Monitor/locale/es.yml b/src/pages/Monitor/locale/es.yml index 30afb1904b..f6a29879f0 100644 --- a/src/pages/Monitor/locale/es.yml +++ b/src/pages/Monitor/locale/es.yml @@ -39,6 +39,7 @@ salesTicketsTable: payMethod: Método de pago department: Departamento packing: ITP + hasItemLost: Artículo perdido searchBar: label: Buscar tickets info: Buscar tickets por identificador o alias diff --git a/src/pages/Order/Card/CatalogFilterValueDialog.vue b/src/pages/Order/Card/CatalogFilterValueDialog.vue index b91e7d229b..d1bd48c9e9 100644 --- a/src/pages/Order/Card/CatalogFilterValueDialog.vue +++ b/src/pages/Order/Card/CatalogFilterValueDialog.vue @@ -110,7 +110,7 @@ const getSelectedTagValues = async (tag) => { /^value\d+$/.test(k)) .map((v) => x[v]) .filter((v) => v) - .sort() + .sort(), ); tagValue.value = resultValueTags; } @@ -76,7 +75,7 @@ watch( (val) => { extractTags(val); }, - { immediate: true } + { immediate: true }, ); diff --git a/src/pages/Order/Card/OrderCatalogFilter.vue b/src/pages/Order/Card/OrderCatalogFilter.vue index 262f503fd1..76e6089834 100644 --- a/src/pages/Order/Card/OrderCatalogFilter.vue +++ b/src/pages/Order/Card/OrderCatalogFilter.vue @@ -184,7 +184,7 @@ function addOrder(value, field, params) { {{ t( categoryList.find((c) => c.id == customTag.value)?.name || - '' + '', ) }} @@ -296,7 +296,7 @@ function addOrder(value, field, params) { [ label: t('module.created'), component: 'date', cardVisible: true, - format: (row) => toDateTimeFormat(row?.landed), + format: (row) => toDateTimeFormat(row?.created), columnField: { component: null, }, diff --git a/src/pages/Route/Agency/Card/AgencyWorkcenter.vue b/src/pages/Route/Agency/Card/AgencyWorkcenter.vue index 7103ea9ced..576933883d 100644 --- a/src/pages/Route/Agency/Card/AgencyWorkcenter.vue +++ b/src/pages/Route/Agency/Card/AgencyWorkcenter.vue @@ -88,7 +88,7 @@ async function deleteWorCenter(id) { - + { afterEach(() => { vi.clearAllMocks(); }); - const generateParams = (formData) => ({ + const generateParams = (formData, filter = {}) => ({ params: { + filter: JSON.stringify(filter), warehouseFk: formData.warehouseId, addressFk: formData.addressId, landed: formData.landed, @@ -23,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 () => { @@ -52,4 +60,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(); + }); }); diff --git a/src/pages/Route/Agency/composables/getAgencies.js b/src/pages/Route/Agency/composables/getAgencies.js index 7299d7e23b..850f874560 100644 --- a/src/pages/Route/Agency/composables/getAgencies.js +++ b/src/pages/Route/Agency/composables/getAgencies.js @@ -1,12 +1,26 @@ import axios from 'axios'; +import agency from 'src/router/modules/agency'; -export async function getAgencies(formData) { +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, }; - 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/Route/Card/RouteFilter.vue b/src/pages/Route/Card/RouteFilter.vue index 72bfed1da7..21858102be 100644 --- a/src/pages/Route/Card/RouteFilter.vue +++ b/src/pages/Route/Card/RouteFilter.vue @@ -100,7 +100,7 @@ const emit = defineEmits(['search']); { { [ create: true, component: 'select', attrs: { - url: 'vehicles', - fields: ['id', 'numberPlate'], + url: 'vehicles/active', optionLabel: 'numberPlate', optionFilterValue: 'numberPlate', find: { diff --git a/src/pages/Route/RouteTickets.vue b/src/pages/Route/RouteTickets.vue index 56e3143b4e..4278e0c71c 100644 --- a/src/pages/Route/RouteTickets.vue +++ b/src/pages/Route/RouteTickets.vue @@ -120,8 +120,8 @@ const deletePriorities = async () => { try { await Promise.all( selectedRows.value.map((ticket) => - axios.patch(`Tickets/${ticket?.id}/`, { priority: null }) - ) + axios.patch(`Tickets/${ticket?.id}/`, { priority: null }), + ), ); } finally { refreshKey.value++; @@ -132,8 +132,8 @@ const setOrderedPriority = async () => { try { await Promise.all( ticketList.value.map((ticket, index) => - axios.patch(`Tickets/${ticket?.id}/`, { priority: index + 1 }) - ) + axios.patch(`Tickets/${ticket?.id}/`, { priority: index + 1 }), + ), ); } finally { refreshKey.value++; @@ -162,7 +162,7 @@ const setHighestPriority = async (ticket, ticketList) => { const goToBuscaman = async (ticket = null) => { await openBuscaman( routeEntity.value?.vehicleFk, - ticket ? [ticket] : selectedRows.value + ticket ? [ticket] : selectedRows.value, ); }; @@ -393,7 +393,13 @@ const openSmsDialog = async () => { - + {{ t('Add ticket') }} diff --git a/src/pages/Route/Vehicle/Card/VehicleBasicData.vue b/src/pages/Route/Vehicle/Card/VehicleBasicData.vue new file mode 100644 index 0000000000..e78bc6edd2 --- /dev/null +++ b/src/pages/Route/Vehicle/Card/VehicleBasicData.vue @@ -0,0 +1,162 @@ + + + (warehouses = data)" + auto-load + /> + (companies = data)" + auto-load + /> + (countries = data)" + auto-load + /> + (fuelTypes = data)" + auto-load + /> + (deliveryPoints = data)" + auto-load + /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/pages/Route/Vehicle/Card/VehicleCard.vue b/src/pages/Route/Vehicle/Card/VehicleCard.vue new file mode 100644 index 0000000000..852879651a --- /dev/null +++ b/src/pages/Route/Vehicle/Card/VehicleCard.vue @@ -0,0 +1,13 @@ + + + + diff --git a/src/pages/Route/Vehicle/Card/VehicleDescriptor.vue b/src/pages/Route/Vehicle/Card/VehicleDescriptor.vue new file mode 100644 index 0000000000..f31ffe847c --- /dev/null +++ b/src/pages/Route/Vehicle/Card/VehicleDescriptor.vue @@ -0,0 +1,50 @@ + + + + + { + try { + await axios.delete(`Vehicles/${entity.id}`); + notify('vehicle.remove', 'positive'); + $router.push({ name: 'VehicleList' }); + } catch (e) { + throw e; + } + } + " + > + + {{ $t('vehicle.delete') }} + + + + + + + + + + + + +es: + Vehicle removed: Vehículo eliminado + diff --git a/src/pages/Route/Vehicle/Card/VehicleSummary.vue b/src/pages/Route/Vehicle/Card/VehicleSummary.vue new file mode 100644 index 0000000000..981870cb29 --- /dev/null +++ b/src/pages/Route/Vehicle/Card/VehicleSummary.vue @@ -0,0 +1,127 @@ + + + + + {{ entity.id }} - {{ entity.numberPlate }} + + + + + + + + + + + + + + + {{ entity.supplier?.name }} + + + + + + + + {{ entity.supplierCooler?.name }} + + + + + + + + + + + + + + + + {{ $t('globals.download') }} + + + + + + + + + + + + + + + + + + diff --git a/src/pages/Route/Vehicle/VehicleFilter.js b/src/pages/Route/Vehicle/VehicleFilter.js new file mode 100644 index 0000000000..cbf5cc621c --- /dev/null +++ b/src/pages/Route/Vehicle/VehicleFilter.js @@ -0,0 +1,76 @@ +export default { + fields: [ + 'id', + 'description', + 'isActive', + 'isKmTruckRate', + 'warehouseFk', + 'companyFk', + 'numberPlate', + 'chassis', + 'supplierFk', + 'supplierCoolerFk', + 'tradeMark', + 'fuelTypeFk', + 'import', + 'importCooler', + 'vin', + 'model', + 'ppeFk', + 'countryCodeFk', + 'leasing', + 'bankPolicyFk', + 'vehicleTypeFk', + 'deliveryPointFk', + ], + include: [ + { + relation: 'warehouse', + scope: { + fields: ['id', 'name'], + }, + }, + { + relation: 'company', + scope: { + fields: ['id', 'code'], + }, + }, + { + relation: 'supplier', + scope: { + fields: ['id', 'name'], + }, + }, + { + relation: 'supplierCooler', + scope: { + fields: ['id', 'name'], + }, + }, + { + relation: 'fuelType', + scope: { + fields: ['id', 'name'], + }, + }, + { + relation: 'bankPolicy', + scope: { + fields: ['id', 'ref', 'dmsFk'], + }, + }, + { + relation: 'ppe', + scope: { + fields: ['id'], + }, + }, + { + relation: 'deliveryPoint', + scope: { + fields: ['id', 'name'], + }, + }, + ], +}; diff --git a/src/pages/Route/Vehicle/VehicleList.vue b/src/pages/Route/Vehicle/VehicleList.vue new file mode 100644 index 0000000000..de6aaba5fd --- /dev/null +++ b/src/pages/Route/Vehicle/VehicleList.vue @@ -0,0 +1,224 @@ + + + (warehouses = data)" + auto-load + /> + (companies = data)" + auto-load + /> + (countries = data)" + auto-load + /> + (vehicleStates = data)" + auto-load + /> + (vehicleTypes = data)" + auto-load + /> + + + + + + + {{ $t('globals.inactive') }} + + + + + + + + + + + + + + + + + diff --git a/src/pages/Route/Vehicle/locale/en.yml b/src/pages/Route/Vehicle/locale/en.yml new file mode 100644 index 0000000000..c92022f9d2 --- /dev/null +++ b/src/pages/Route/Vehicle/locale/en.yml @@ -0,0 +1,20 @@ +vehicle: + tradeMark: Trade Mark + numberPlate: Nº Plate + chassis: Chassis + leasing: Leasing + isKmTruckRate: Trailer + delete: Delete Vehicle + supplierCooler: Supplier Cooler + vin: VIN + ppe: Ppe + isActive: Active + nLeasing: Nº Leasing + create: Create Vehicle + amountCooler: Amount cooler + remove: Vehicle removed + search: Search Vehicle + searchInfo: Search by id or number plate + params: + vehicleTypeFk: Type + vehicleStateFk: State diff --git a/src/pages/Route/Vehicle/locale/es.yml b/src/pages/Route/Vehicle/locale/es.yml new file mode 100644 index 0000000000..c878f97ac5 --- /dev/null +++ b/src/pages/Route/Vehicle/locale/es.yml @@ -0,0 +1,20 @@ +vehicle: + tradeMark: Marca + numberPlate: Matrícula + chassis: Nº de bastidor + leasing: Leasing + isKmTruckRate: Trailer + delete: Eliminar vehículo + supplierCooler: Proveedor Frío + vin: VIN + ppe: Nº Inmovilizado + create: Crear vehículo + amountCooler: Importe frío + isActive: Activo + nLeasing: Nº leasing + remove: Vehículo eliminado + search: Buscar Vehículo + searchInfo: Buscar por id o matrícula + params: + vehicleTypeFk: Tipo + vehicleStateFk: Estado diff --git a/src/pages/Shelving/ShelvingList.vue b/src/pages/Shelving/ShelvingList.vue index 8a52118076..4e0c21100e 100644 --- a/src/pages/Shelving/ShelvingList.vue +++ b/src/pages/Shelving/ShelvingList.vue @@ -72,7 +72,7 @@ function navigate(id) { - + {{ $t('shelving.list.newShelving') }} diff --git a/src/pages/Supplier/Card/SupplierAccounts.vue b/src/pages/Supplier/Card/SupplierAccounts.vue index 4a6901d1d1..365eb67a14 100644 --- a/src/pages/Supplier/Card/SupplierAccounts.vue +++ b/src/pages/Supplier/Card/SupplierAccounts.vue @@ -71,7 +71,7 @@ function bankEntityFilter(val, update) { filteredBankEntitiesOptions.value = bankEntitiesOptions.value.filter( (bank) => bank.bic.toLowerCase().startsWith(needle) || - bank.name.toLowerCase().includes(needle) + bank.name.toLowerCase().includes(needle), ); }); } @@ -170,7 +170,7 @@ function bankEntityFilter(val, update) { {{ t( - 'Name of the bank account holder if different from the provider' + 'Name of the bank account holder if different from the provider', ) }} @@ -194,7 +194,7 @@ function bankEntityFilter(val, update) { { icon="add" color="primary" @click="redirectToCreateView()" - shortcut="+" + v-shortcut="'+'" /> {{ t('New address') }} diff --git a/src/pages/Supplier/Card/SupplierAgencyTerm.vue b/src/pages/Supplier/Card/SupplierAgencyTerm.vue index 99b672cc4c..ab21f1f76f 100644 --- a/src/pages/Supplier/Card/SupplierAgencyTerm.vue +++ b/src/pages/Supplier/Card/SupplierAgencyTerm.vue @@ -114,7 +114,7 @@ const redirectToCreateView = () => { icon="add" color="primary" @click="redirectToCreateView()" - shortcut="+" + v-shortcut="'+'" /> {{ t('supplier.agencyTerms.addRow') }} diff --git a/src/pages/Supplier/Card/SupplierContacts.vue b/src/pages/Supplier/Card/SupplierContacts.vue index 6781c8d34a..f96d92ab1d 100644 --- a/src/pages/Supplier/Card/SupplierContacts.vue +++ b/src/pages/Supplier/Card/SupplierContacts.vue @@ -78,7 +78,7 @@ const insertRow = () => { { return $props.id || route.params.id; }); +const problems = ref({}); function ticketFilter(ticket) { return JSON.stringify({ clientFk: ticket.clientFk }); @@ -34,6 +38,11 @@ function ticketFilter(ticket) { + ([problems] = data)" + /> - - - - {{ t('Client inactive') }} - - - {{ t('Client Frozen') }} - - - {{ t('Client has debt') }} - - - {{ t('Client not checked') }} - - - {{ t('This ticket is deleted') }} - + + + diff --git a/src/pages/Ticket/Card/TicketDescriptorMenu.vue b/src/pages/Ticket/Card/TicketDescriptorMenu.vue index 89f7015d71..63e45c8abe 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/Card/TicketNotes.vue b/src/pages/Ticket/Card/TicketNotes.vue index f558b71cc2..feb88bf843 100644 --- a/src/pages/Ticket/Card/TicketNotes.vue +++ b/src/pages/Ticket/Card/TicketNotes.vue @@ -32,7 +32,7 @@ watch( crudModelFilter.where.ticketFk = route.params.id; store.filter = crudModelFilter; await ticketNotesCrudRef.value.reload(); - } + }, ); function handleDelete(row) { ticketNotesCrudRef.value.remove([row]); @@ -105,7 +105,7 @@ async function handleSave() { @@ -118,7 +118,7 @@ watch( route.params.id, - () => tableRef.value.reload() + () => tableRef.value.reload(), ); const columns = computed(() => [ @@ -199,7 +200,7 @@ const changeQuantity = async (sale) => { await updateQuantity(sale); } catch (e) { const { quantity } = tableRef.value.CrudModelRef.originalData.find( - (s) => s.id === sale.id + (s) => s.id === sale.id, ); sale.quantity = quantity; throw e; @@ -503,7 +504,7 @@ async function isSalePrepared(item) { componentProps: { title: t('Item prepared'), message: t( - 'This item is already prepared. Do you want to continue?' + 'This item is already prepared. Do you want to continue?', ), data: item, }, @@ -525,7 +526,7 @@ watch( if (newItemFk) { updateItem(newRow.value); } - } + }, ); @@ -595,7 +596,7 @@ watch( openConfirmationModal( t('Continue anyway?'), t('You are going to delete lines of the ticket'), - removeSales + removeSales, ) " > @@ -679,53 +680,7 @@ watch( :disabled-attr="isTicketEditable" > - - - - {{ t('ticketSale.claim') }}: - {{ row.claim?.claimFk }} - - - - - - {{ t('ticketSale.visible') }}: {{ row.visible || 0 }} - - - - - {{ t('ticketSale.reserved') }} - - - - - {{ t('ticketSale.noVisible') }} - - - - - {{ t('ticketSale.hasComponentLack') }} - - + @@ -856,7 +811,7 @@ watch( color="primary" fab icon="add" - shortcut="+" + v-shortcut="'+'" data-cy="ticketSaleAddToBasketBtn" /> diff --git a/src/pages/Ticket/Card/TicketService.vue b/src/pages/Ticket/Card/TicketService.vue index d045eadee8..6ce69a6aa4 100644 --- a/src/pages/Ticket/Card/TicketService.vue +++ b/src/pages/Ticket/Card/TicketService.vue @@ -40,7 +40,7 @@ watch( async () => { store.filter = crudModelFilter.value; await ticketServiceCrudRef.value.reload(); - } + }, ); onMounted(async () => await getDefaultTaxClass()); @@ -59,7 +59,7 @@ const createRefund = async () => { t('service.createRefundSuccess', { ticketId: refundTicket.id, }), - 'positive' + 'positive', ); router.push({ name: 'TicketSale', params: { id: refundTicket.id } }); }; @@ -225,7 +225,7 @@ async function handleSave() { color="primary" icon="add" @click="ticketServiceCrudRef.insert()" - shortcut="+" + v-shortcut="'+'" /> diff --git a/src/pages/Ticket/Card/TicketSummary.vue b/src/pages/Ticket/Card/TicketSummary.vue index 09a2500064..5df08b8813 100644 --- a/src/pages/Ticket/Card/TicketSummary.vue +++ b/src/pages/Ticket/Card/TicketSummary.vue @@ -20,6 +20,7 @@ import ZoneDescriptorProxy from 'src/pages/Zone/Card/ZoneDescriptorProxy.vue'; import VnSelect from 'src/components/common/VnSelect.vue'; import VnToSummary from 'src/components/ui/VnToSummary.vue'; import TicketDescriptorMenu from './TicketDescriptorMenu.vue'; +import TicketProblems from 'src/components/TicketProblems.vue'; const route = useRoute(); const { notify } = useNotify(); @@ -243,7 +244,7 @@ onMounted(async () => { { - - - {{ t('ticket.summary.claim') }}: - {{ props.row.claim.claimFk }} - - - - - {{ t('ticket.summary.claim') }}: - {{ props.row.claimBeginning.claimFk }} - - - - - {{ t('globals.visible') }}: - {{ props.row.visible }} - - - - - {{ t('ticket.summary.reserved') }} - - - - - {{ t('ticket.summary.itemShortage') }} - - - - - {{ t('ticket.summary.hasComponentLack') }} - - + diff --git a/src/pages/Ticket/Card/TicketTracking.vue b/src/pages/Ticket/Card/TicketTracking.vue index f5ed03b0d6..8bf7fe6b8c 100644 --- a/src/pages/Ticket/Card/TicketTracking.vue +++ b/src/pages/Ticket/Card/TicketTracking.vue @@ -19,7 +19,7 @@ watch( async (val) => { paginateFilter.where.ticketFk = val; paginateRef.value.fetch(); - } + }, ); const paginateFilter = reactive({ @@ -119,7 +119,7 @@ const openCreateModal = () => createTrackingDialogRef.value.show(); color="primary" fab icon="add" - shortcut="+" + v-shortcut="'+'" /> {{ t('tracking.addState') }} diff --git a/src/pages/Ticket/TicketCreate.vue b/src/pages/Ticket/TicketCreate.vue index 4c32448e4e..96d2005472 100644 --- a/src/pages/Ticket/TicketCreate.vue +++ b/src/pages/Ticket/TicketCreate.vue @@ -38,35 +38,43 @@ onBeforeMount(async () => { await onClientSelected(initialFormState); }); +function resetAgenciesSelector(formData) { + agenciesOptions.value = []; + formData.agencyModeId = null; +} + 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; }; const onClientSelected = async (formData) => { + resetAgenciesSelector(formData); await fetchClient(formData); await fetchAddresses(formData); }; const fetchAvailableAgencies = async (formData) => { - const { data } = await getAgencies(formData); - agenciesOptions.value = data; - - const defaultAgency = agenciesOptions.value.find( - (agency) => - agency.agencyModeFk === selectedClient.value.defaultAddress.agencyModeFk - ); - - if (defaultAgency) formData.agencyModeId = defaultAgency.agencyModeFk; + resetAgenciesSelector(formData); + const response= await getAgencies(formData, selectedClient.value); + if (!response) return; + + 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 c8b126d7dc..2245c5c81e 100644 --- a/src/pages/Ticket/TicketCreateDialog.vue +++ b/src/pages/Ticket/TicketCreateDialog.vue @@ -38,35 +38,43 @@ onBeforeMount(async () => { await onClientSelected(initialFormState); }); +function resetAgenciesSelector(formData) { + agenciesOptions.value = []; + if(formData) formData.agencyModeId = null; +} + 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; }; const onClientSelected = async (formData) => { + resetAgenciesSelector(formData); await fetchClient(formData); await fetchAddresses(formData); }; const fetchAvailableAgencies = async (formData) => { - const { data } = await getAgencies(formData); - agenciesOptions.value = data; - - const defaultAgency = agenciesOptions.value.find( - (agency) => - agency.agencyModeFk === selectedClient.value.defaultAddress.agencyModeFk - ); - - if (defaultAgency) formData.agencyModeId = defaultAgency.agencyModeFk; + resetAgenciesSelector(formData); + const response= await getAgencies(formData, selectedClient.value); + if (!response) return; + + 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 8cf1184eb1..8df19c0d9e 100644 --- a/src/pages/Ticket/TicketList.vue +++ b/src/pages/Ticket/TicketList.vue @@ -229,37 +229,46 @@ 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 { data } = await getAgencies(formData); - agenciesOptions.value = data; - - const defaultAgency = agenciesOptions.value.find( - (agency) => - agency.agencyModeFk === selectedClient.value.defaultAddress.agencyModeFk - ); - - if (defaultAgency) formData.agencyModeId = defaultAgency.agencyModeFk; + resetAgenciesSelector(formData); + const response= await getAgencies(formData, selectedClient.value); + if (!response) return; + + const { options, agency } = response + if(options) + agenciesOptions.value = options; + if(agency) + formData.agencyModeId = agency; }; 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; diff --git a/src/pages/Ticket/locale/en.yml b/src/pages/Ticket/locale/en.yml index f11b32c3a5..d4bfd11036 100644 --- a/src/pages/Ticket/locale/en.yml +++ b/src/pages/Ticket/locale/en.yml @@ -208,3 +208,9 @@ ticketList: toLines: Go to lines addressNickname: Address nickname ref: Reference + rounding: Rounding + noVerifiedData: No verified data + purchaseRequest: Purchase request + notVisible: Not visible + clientFrozen: Client frozen + componentLack: Component lack diff --git a/src/pages/Ticket/locale/es.yml b/src/pages/Ticket/locale/es.yml index 945da8367f..ff68461fad 100644 --- a/src/pages/Ticket/locale/es.yml +++ b/src/pages/Ticket/locale/es.yml @@ -213,3 +213,9 @@ ticketList: toLines: Ir a lineas addressNickname: Alias consignatario ref: Referencia + rounding: Redondeo + noVerifiedData: Sin datos comprobados + purchaseRequest: Petición de compra + notVisible: No visible + clientFrozen: Cliente congelado + componentLack: Faltan componentes diff --git a/src/pages/Travel/Card/TravelThermographs.vue b/src/pages/Travel/Card/TravelThermographs.vue index b520166d30..4463378fd8 100644 --- a/src/pages/Travel/Card/TravelThermographs.vue +++ b/src/pages/Travel/Card/TravelThermographs.vue @@ -217,7 +217,7 @@ const removeThermograph = async (id) => { icon="add" color="primary" @click="redirectToThermographForm('create')" - shortcut="+" + v-shortcut="'+'" /> {{ t('Add thermograph') }} diff --git a/src/pages/Travel/ExtraCommunityFilter.vue b/src/pages/Travel/ExtraCommunityFilter.vue index b903aeabff..b22574632f 100644 --- a/src/pages/Travel/ExtraCommunityFilter.vue +++ b/src/pages/Travel/ExtraCommunityFilter.vue @@ -113,7 +113,7 @@ warehouses(); - + -import { nextTick, ref, watch } from 'vue'; +import { nextTick, ref, watch, computed } from 'vue'; import { useI18n } from 'vue-i18n'; import { useRoute, useRouter } from 'vue-router'; +import { useAcl } from 'src/composables/useAcl'; import WorkerCalendarFilter from 'pages/Worker/Card/WorkerCalendarFilter.vue'; import FetchData from 'components/FetchData.vue'; @@ -9,10 +10,17 @@ import WorkerCalendarItem from 'pages/Worker/Card/WorkerCalendarItem.vue'; import RightMenu from 'src/components/common/RightMenu.vue'; import axios from 'axios'; +import VnNotes from 'src/components/ui/VnNotes.vue'; +import { useStateStore } from 'src/stores/useStateStore'; +const stateStore = useStateStore(); const router = useRouter(); const route = useRoute(); const { t } = useI18n(); +const acl = useAcl(); +const canSeeNotes = computed(() => + acl.hasAny([{ model: 'Worker', props: '__get__business', accessType: 'READ' }]), +); const workerIsFreelance = ref(); const WorkerFreelanceRef = ref(); const workerCalendarFilterRef = ref(null); @@ -26,6 +34,10 @@ const contractHolidays = ref(null); const yearHolidays = ref(null); const eventsMap = ref({}); const festiveEventsMap = ref({}); +const saveUrl = ref(); +const body = { + workerFk: route.params.id, +}; const onFetchActiveContract = (data) => { if (!data) return; @@ -67,7 +79,7 @@ const onFetchAbsences = (data) => { name: holidayName, isFestive: true, }, - true + true, ); }); } @@ -146,7 +158,7 @@ watch( async () => { await nextTick(); await activeContractRef.value.fetch(); - } + }, ); watch([year, businessFk], () => refreshData()); @@ -181,6 +193,20 @@ watch([year, businessFk], () => refreshData()); /> + + { + saveUrl = `Businesses/${data.id}`; + } + " + :body="body" + /> + diff --git a/src/pages/Worker/Card/WorkerCalendarFilter.vue b/src/pages/Worker/Card/WorkerCalendarFilter.vue index 67b7df907b..48fc4094b8 100644 --- a/src/pages/Worker/Card/WorkerCalendarFilter.vue +++ b/src/pages/Worker/Card/WorkerCalendarFilter.vue @@ -180,8 +180,6 @@ const yearList = ref(generateYears()); :is-clearable="false" /> - - import { useI18n } from 'vue-i18n'; import { useRoute } from 'vue-router'; -import { ref, computed } from 'vue'; +import { ref, computed, watch } from 'vue'; import FetchData from 'components/FetchData.vue'; import VnRow from 'components/ui/VnRow.vue'; import VnSelect from 'src/components/common/VnSelect.vue'; @@ -19,6 +19,7 @@ const trainsData = ref([]); const machinesData = ref([]); const route = useRoute(); const routeId = computed(() => route.params.id); +const selected = ref([]); const initialData = computed(() => { return { @@ -41,6 +42,21 @@ async function insert() { await axios.post('Operators', initialData.value); crudModelRef.value.reload(); } + +watch( + () => crudModelRef.value?.formData, + (formData) => { + if (formData && formData.length) { + if (JSON.stringify(selected.value) !== JSON.stringify(formData)) { + selected.value = formData; + } + } else if (selected.value.length > 0) { + selected.value = []; + } + }, + { immediate: true, deep: true } +); + @@ -67,6 +83,7 @@ async function insert() { :data-required="{ workerFk: route.params.id }" ref="crudModelRef" search-url="operator" + :selected="selected" auto-load > diff --git a/src/pages/Worker/Card/WorkerPda.vue b/src/pages/Worker/Card/WorkerPda.vue index c1beef40dc..2c2f494a44 100644 --- a/src/pages/Worker/Card/WorkerPda.vue +++ b/src/pages/Worker/Card/WorkerPda.vue @@ -101,7 +101,7 @@ function reloadData() { openConfirmationModal( t(`Remove PDA`), t('Do you want to remove this PDA?'), - () => deallocatePDA(row.deviceProductionFk) + () => deallocatePDA(row.deviceProductionFk), ) " > @@ -114,7 +114,13 @@ function reloadData() { - + { color="primary" flat icon="add" - shortcut="+" + v-shortcut="'+'" style="flex: 0" data-cy="addRelative" /> diff --git a/src/pages/Worker/Card/WorkerSummary.vue b/src/pages/Worker/Card/WorkerSummary.vue index 98bdffe258..992f6ec718 100644 --- a/src/pages/Worker/Card/WorkerSummary.vue +++ b/src/pages/Worker/Card/WorkerSummary.vue @@ -12,7 +12,6 @@ import RoleDescriptorProxy from 'src/pages/Account/Role/Card/RoleDescriptorProxy import DepartmentDescriptorProxy from 'src/pages/Department/Card/DepartmentDescriptorProxy.vue'; import { useAdvancedSummary } from 'src/composables/useAdvancedSummary'; import WorkerDescriptorMenu from './WorkerDescriptorMenu.vue'; -import axios from 'axios'; const route = useRoute(); const { t } = useI18n(); @@ -28,76 +27,6 @@ const entityId = computed(() => $props.id || route.params.id); const basicDataUrl = ref(null); const advancedSummary = ref(); -const filter = { - include: [ - { - relation: 'user', - scope: { - fields: ['name', 'nickname', 'roleFk'], - - include: [ - { - relation: 'role', - scope: { - fields: ['name'], - }, - }, - { - relation: 'emailUser', - scope: { - fields: ['email'], - }, - }, - ], - }, - }, - { - relation: 'department', - scope: { - include: { - relation: 'department', - scope: { - fields: ['name'], - }, - }, - }, - }, - { - relation: 'boss', - }, - { - relation: 'client', - }, - { - relation: 'sip', - }, - { - relation: 'business', - scope: { - include: [ - { - relation: 'department', - scope: { - fields: ['id', 'name'], - }, - }, - { - relation: 'reasonEnd', - scope: { - fields: ['id', 'reason'], - }, - }, - { - relation: 'workerBusinessProfessionalCategory', - scope: { - fields: ['id', 'description'], - }, - }, - ], - }, - }, - ], -}; onBeforeMount(async () => { advancedSummary.value = await useAdvancedSummary('Workers', entityId.value); basicDataUrl.value = `#/worker/${entityId.value}/basic-data`; @@ -107,8 +36,8 @@ onBeforeMount(async () => { diff --git a/src/pages/Worker/Card/WorkerTimeControl.vue b/src/pages/Worker/Card/WorkerTimeControl.vue index d922156335..7def6e94cf 100644 --- a/src/pages/Worker/Card/WorkerTimeControl.vue +++ b/src/pages/Worker/Card/WorkerTimeControl.vue @@ -69,12 +69,12 @@ const acl = useAcl(); const selectedDateYear = computed(() => moment(selectedDate.value).isoWeekYear()); const worker = computed(() => arrayData.store?.data); const canSend = computed(() => - acl.hasAny([{ model: 'WorkerTimeControl', props: 'sendMail', accessType: 'WRITE' }]) + acl.hasAny([{ model: 'WorkerTimeControl', props: 'sendMail', accessType: 'WRITE' }]), ); const canUpdate = computed(() => acl.hasAny([ { model: 'WorkerTimeControl', props: 'updateMailState', accessType: 'WRITE' }, - ]) + ]), ); const isHimself = computed(() => user.value.id === Number(route.params.id)); @@ -100,7 +100,7 @@ const getHeaderFormattedDate = (date) => { }; const formattedWeekTotalHours = computed(() => - secondsToHoursMinutes(weekTotalHours.value) + secondsToHoursMinutes(weekTotalHours.value), ); const onInputChange = async (date) => { @@ -320,7 +320,7 @@ const getFinishTime = () => { today.setHours(0, 0, 0, 0); let todayInWeek = weekDays.value.find( - (day) => day.dated.getTime() === today.getTime() + (day) => day.dated.getTime() === today.getTime(), ); if (todayInWeek && todayInWeek.hours && todayInWeek.hours.length) { @@ -472,7 +472,7 @@ onMounted(async () => { openConfirmationModal( t('Send time control email'), t('Are you sure you want to send it?'), - resendEmail + resendEmail, ) " > @@ -561,7 +561,7 @@ onMounted(async () => { @show-worker-time-form=" showWorkerTimeForm( { id: hour.id, entryCode: hour.direction }, - 'edit' + 'edit', ) " class="hour-chip" @@ -577,7 +577,7 @@ onMounted(async () => { diff --git a/src/pages/Zone/Card/ZoneEvents.vue b/src/pages/Zone/Card/ZoneEvents.vue index a5806bab9f..1e6debd25d 100644 --- a/src/pages/Zone/Card/ZoneEvents.vue +++ b/src/pages/Zone/Card/ZoneEvents.vue @@ -78,13 +78,13 @@ const onZoneEventFormClose = () => { { isNewMode: true, }, - true + true, ) " color="primary" fab icon="add" - shortcut="+" + v-shortcut="'+'" /> {{ t('eventsInclusionForm.addEvent') }} diff --git a/src/pages/Zone/Card/ZoneWarehouses.vue b/src/pages/Zone/Card/ZoneWarehouses.vue index d3b1d7a1c4..f14658dd0b 100644 --- a/src/pages/Zone/Card/ZoneWarehouses.vue +++ b/src/pages/Zone/Card/ZoneWarehouses.vue @@ -49,7 +49,7 @@ watch( store.url = urlPath.value; store.filter.include = 'warehouse'; fetchWarehouses(); - } + }, ); const fetchWarehouses = () => paginateRef.value.fetch(); @@ -84,7 +84,8 @@ const openCreateWarehouseForm = () => createWarehouseDialogRef.value.show(); openConfirmationModal( t('zone.deleteTitle'), t('zone.deleteSubtitle'), - () => deleteWarehouse(row, rows, rowIndex) + () => + deleteWarehouse(row, rows, rowIndex), ) " > @@ -108,7 +109,7 @@ const openCreateWarehouseForm = () => createWarehouseDialogRef.value.show(); icon="add" color="primary" @click="openCreateWarehouseForm()" - shortcut="+" + v-shortcut="'+'" > {{ t('warehouses.add') }} diff --git a/src/pages/Zone/Delivery/ZoneDeliveryList.vue b/src/pages/Zone/Delivery/ZoneDeliveryList.vue index 975cbdb67b..e3ec8cb2d4 100644 --- a/src/pages/Zone/Delivery/ZoneDeliveryList.vue +++ b/src/pages/Zone/Delivery/ZoneDeliveryList.vue @@ -74,7 +74,7 @@ async function remove(row) { - + diff --git a/src/pages/Zone/Upcoming/ZoneUpcomingList.vue b/src/pages/Zone/Upcoming/ZoneUpcomingList.vue index 5a7f0bb4c4..7b5c2ddbc8 100644 --- a/src/pages/Zone/Upcoming/ZoneUpcomingList.vue +++ b/src/pages/Zone/Upcoming/ZoneUpcomingList.vue @@ -74,7 +74,7 @@ async function remove(row) { - + diff --git a/src/router/modules/route.js b/src/router/modules/route.js index 946ad3e15c..835324d20b 100644 --- a/src/router/modules/route.js +++ b/src/router/modules/route.js @@ -160,6 +160,36 @@ const roadmapCard = { ], }; +const vehicleCard = { + path: ':id', + name: 'VehicleCard', + component: () => import('src/pages/Route/Vehicle/Card/VehicleCard.vue'), + redirect: { name: 'VehicleSummary' }, + meta: { + menu: ['VehicleBasicData'], + }, + children: [ + { + name: 'VehicleSummary', + path: 'summary', + meta: { + title: 'summary', + icon: 'view_list', + }, + component: () => import('src/pages/Route/Vehicle/Card/VehicleSummary.vue'), + }, + { + name: 'VehicleBasicData', + path: 'basic-data', + meta: { + title: 'basicData', + icon: 'vn:settings', + }, + component: () => import('src/pages/Route/Vehicle/Card/VehicleBasicData.vue'), + }, + ], +}; + export default { name: 'Route', path: '/route', @@ -174,6 +204,7 @@ export default { 'RouteRoadmap', 'CmrList', 'AgencyList', + 'VehicleList', ], }, component: RouterView, @@ -280,6 +311,27 @@ export default { agencyCard, ], }, + { + path: 'vehicle', + name: 'RouteVehicle', + redirect: { name: 'VehicleList' }, + meta: { + title: 'vehicle', + icon: 'directions_car', + }, + component: () => import('src/pages/Route/Vehicle/VehicleList.vue'), + children: [ + { + path: 'list', + name: 'VehicleList', + meta: { + title: 'vehicleList', + icon: 'directions_car', + }, + }, + vehicleCard, + ], + }, ], }, ], diff --git a/src/router/modules/shelving.js b/src/router/modules/shelving.js index cd8a2bf157..55fb04278c 100644 --- a/src/router/modules/shelving.js +++ b/src/router/modules/shelving.js @@ -44,6 +44,7 @@ const shelvingCard = { path: ':id', component: () => import('pages/Shelving/Card/ShelvingCard.vue'), redirect: { name: 'ShelvingSummary' }, + meta: { menu: ['ShelvingBasicData', 'ShelvingLog'] }, children: [ { name: 'ShelvingSummary', @@ -81,13 +82,10 @@ export default { title: 'shelving', icon: 'vn:inventory', moduleName: 'Shelving', + menu: ['ShelvingList', 'ParkingMain'], }, component: RouterView, redirect: { name: 'ShelvingMain' }, - menus: { - main: ['ShelvingList', 'ParkingMain'], - card: ['ShelvingBasicData', 'ShelvingLog'], - }, children: [ { path: '', diff --git a/test/cypress/integration/client/clientList.spec.js b/test/cypress/integration/client/clientList.spec.js index dcded63b0f..f2e3671baa 100644 --- a/test/cypress/integration/client/clientList.spec.js +++ b/test/cypress/integration/client/clientList.spec.js @@ -62,10 +62,9 @@ describe('Client list', () => { it('Client founded create order', () => { const search = 'Jessica Jones'; cy.searchByLabel('Name', search); - cy.openActionDescriptor('New order'); + cy.clickButtonWith('icon', 'icon-basketadd'); cy.waitForElement('#formModel'); cy.waitForElement('.q-form'); cy.checkValueForm(1, search); - cy.checkValueForm(2, search); }); }); diff --git a/test/cypress/integration/client/clientSms.spec.js b/test/cypress/integration/client/clientSms.spec.js index 731522a5c6..5c82fd9a33 100644 --- a/test/cypress/integration/client/clientSms.spec.js +++ b/test/cypress/integration/client/clientSms.spec.js @@ -7,6 +7,5 @@ describe('Client notes', () => { }); it('Should load layout', () => { cy.get('.q-page').should('be.visible'); - cy.get('.q-page > :nth-child(2) > :nth-child(1)').should('be.visible'); }); }); diff --git a/test/cypress/integration/entry/myEntry.spec.js b/test/cypress/integration/entry/myEntry.spec.js index c254764192..49d75cf392 100644 --- a/test/cypress/integration/entry/myEntry.spec.js +++ b/test/cypress/integration/entry/myEntry.spec.js @@ -8,12 +8,12 @@ describe('EntryMy when is supplier', () => { }, }); }); - // https://redmine.verdnatura.es/issues/8418 - it.skip('should open buyLabel when is supplier', () => { + + it('should open buyLabel when is supplier', () => { cy.get( - '[to="/null/3"] > .q-card > .column > .q-btn > .q-btn__content > .q-icon' + '[to="/null/3"] > .q-card > :nth-child(2) > .q-btn > .q-btn__content > .q-icon' ).click(); - cy.get('.q-card__actions > .q-btn').click(); + cy.dataCy('printLabelsBtn').click(); cy.window().its('open').should('be.called'); }); }); diff --git a/test/cypress/integration/item/itemBarcodes.spec.js b/test/cypress/integration/item/itemBarcodes.spec.js index 4d17fa260e..844768d9e9 100644 --- a/test/cypress/integration/item/itemBarcodes.spec.js +++ b/test/cypress/integration/item/itemBarcodes.spec.js @@ -1,5 +1,5 @@ /// -describe('Item shelving', () => { +describe('ItemBarcodes', () => { beforeEach(() => { cy.viewport(1920, 1080); cy.login('developer'); diff --git a/test/cypress/integration/item/itemTag.spec.js b/test/cypress/integration/item/itemTag.spec.js index 28e0a747fc..10d68d08a6 100644 --- a/test/cypress/integration/item/itemTag.spec.js +++ b/test/cypress/integration/item/itemTag.spec.js @@ -12,28 +12,22 @@ describe('Item tag', () => { cy.get('.q-page-sticky > div').click(); cy.dataCy('Tag_select').eq(7).type('Tallos'); cy.get('.q-menu .q-item').contains('Tallos').click(); - cy.get( - ':nth-child(8) > [label="Value"] > .q-field > .q-field__inner > .q-field__control > .q-field__control-container > [data-cy="Value_input"]' - ).type('1'); + cy.get(':nth-child(8) > [label="Value"]').type('1'); +cy.dataCy('crudModelDefaultSaveBtn').click(); cy.checkNotification("The tag or priority can't be repeated for an item"); }); - // https://redmine.verdnatura.es/issues/8422 - it.skip('should add a new tag', () => { + + it('should add a new tag', () => { cy.get('.q-page').should('be.visible'); cy.get('.q-page-sticky > div').click(); cy.get('.q-page-sticky > div').click(); cy.dataCy('Tag_select').eq(7).click(); - cy.get('.q-menu .q-item').contains('Ancho de la base').click(); - cy.get( - ':nth-child(8) > [label="Value"] > .q-field > .q-field__inner > .q-field__control > .q-field__control-container > [data-cy="Value_input"]' - ).type('50'); + cy.get('.q-menu .q-item').contains('Ancho de la base').type('{enter}'); + cy.get(':nth-child(8) > [label="Value"]').type('50'); cy.dataCy('crudModelDefaultSaveBtn').click(); cy.checkNotification('Data saved'); - cy.get( - '[data-cy="itemTags"] > :nth-child(7) > .justify-center > .q-icon' - ).click(); + cy.dataCy('itemTags').children(':nth-child(8)').find('.justify-center > .q-icon').click(); cy.dataCy('VnConfirm_confirm').click(); cy.checkNotification('Data saved'); }); -}); +}); \ No newline at end of file diff --git a/test/cypress/integration/item/itemType.spec.js b/test/cypress/integration/item/itemType.spec.js index b0a7b0ca90..466a497084 100644 --- a/test/cypress/integration/item/itemType.spec.js +++ b/test/cypress/integration/item/itemType.spec.js @@ -1,5 +1,10 @@ /// describe('Item type', () => { + const workerError = 'employeeNick'; + const worker = 'buyerNick'; + const category = 'Artificial'; + const type = 'Flower'; + beforeEach(() => { cy.viewport(1920, 1080); cy.login('developer'); @@ -8,32 +13,24 @@ describe('Item type', () => { it('should throw an error if the code already exists', () => { cy.dataCy('vnTableCreateBtn').click(); - cy.get( - 'div.fit > .q-field > .q-field__inner > .q-field__control > .q-field__control-container > [data-cy="Code_input"]' - ).type('ALS'); - cy.get( - 'div.fit > .q-field > .q-field__inner > .q-field__control > .q-field__control-container > [data-cy="Name_input"]' - ).type('Alstroemeria'); - cy.dataCy('Worker_select').type('employeeNick'); - cy.get('.q-menu .q-item').contains('employeeNick').click(); - cy.dataCy('ItemCategory_select').type('Artificial'); - cy.get('.q-menu .q-item').contains('Artificial').click(); + cy.dataCy('codeInput').type('ALS'); + cy.dataCy('nameInput').type('Alstroemeria'); + cy.dataCy('vnWorkerSelect').type(workerError); + cy.get('.q-menu .q-item').contains(workerError).click(); + cy.dataCy('itemCategorySelect').type(category); + cy.get('.q-menu .q-item').contains(category).click(); cy.dataCy('FormModelPopup_save').click(); cy.checkNotification('An item type with the same code already exists'); }); it('should create a new type', () => { cy.dataCy('vnTableCreateBtn').click(); - cy.get( - 'div.fit > .q-field > .q-field__inner > .q-field__control > .q-field__control-container > [data-cy="Code_input"]' - ).type('LIL'); - cy.get( - 'div.fit > .q-field > .q-field__inner > .q-field__control > .q-field__control-container > [data-cy="Name_input"]' - ).type('Lilium'); - cy.dataCy('Worker_select').type('buyerNick'); - cy.get('.q-menu .q-item').contains('buyerNick').click(); - cy.dataCy('ItemCategory_select').type('Flower'); - cy.get('.q-menu .q-item').contains('Flower').click(); + cy.dataCy('codeInput').type('LIL'); + cy.dataCy('nameInput').type('Lilium'); + cy.dataCy('vnWorkerSelect').type(worker); + cy.get('.q-menu .q-item').contains(worker).click(); + cy.dataCy('itemCategorySelect').type(type); + cy.get('.q-menu .q-item').contains(type).click(); cy.dataCy('FormModelPopup_save').click(); cy.checkNotification('Data created'); }); diff --git a/test/cypress/integration/route/vehicle/vehicleDescriptor.spec.js b/test/cypress/integration/route/vehicle/vehicleDescriptor.spec.js new file mode 100644 index 0000000000..64b9ca0a00 --- /dev/null +++ b/test/cypress/integration/route/vehicle/vehicleDescriptor.spec.js @@ -0,0 +1,13 @@ +describe('Vehicle', () => { + beforeEach(() => { + cy.viewport(1920, 1080); + cy.login('deliveryAssistant'); + cy.visit(`/#/route/vehicle/7`); + }); + + it('should delete a vehicle', () => { + cy.openActionsDescriptor(); + cy.get('[data-cy="delete"]').click(); + cy.checkNotification('Vehicle removed'); + }); +}); diff --git a/test/cypress/integration/ticket/ticketList.spec.js b/test/cypress/integration/ticket/ticketList.spec.js index 2984a4ee49..593021e6e1 100644 --- a/test/cypress/integration/ticket/ticketList.spec.js +++ b/test/cypress/integration/ticket/ticketList.spec.js @@ -53,4 +53,29 @@ describe('TicketList', () => { cy.checkNotification('Data created'); cy.url().should('match', /\/ticket\/\d+\/summary/); }); + + it('should show the corerct problems', () => { + cy.intercept('GET', '**/api/Tickets/filter*', (req) => { + req.headers['cache-control'] = 'no-cache'; + req.headers['pragma'] = 'no-cache'; + req.headers['expires'] = '0'; + + req.on('response', (res) => { + delete res.headers['if-none-match']; + delete res.headers['if-modified-since']; + }); + }).as('ticket'); + + cy.get('[data-cy="Warehouse_select"]').type('Warehouse Five'); + cy.get('.q-menu .q-item').contains('Warehouse Five').click(); + cy.wait('@ticket').then((interception) => { + const data = interception.response.body[1]; + expect(data.hasComponentLack).to.equal(1); + expect(data.isTooLittle).to.equal(1); + expect(data.hasItemShortage).to.equal(1); + }); + cy.get('.icon-components').should('exist'); + cy.get('.icon-unavailable').should('exist'); + cy.get('.icon-isTooLittle').should('exist'); + }); }); diff --git a/test/cypress/integration/vnComponent/VnSearchBar.spec.js b/test/cypress/integration/vnComponent/VnSearchBar.spec.js index c710d51921..11d9bbe6a8 100644 --- a/test/cypress/integration/vnComponent/VnSearchBar.spec.js +++ b/test/cypress/integration/vnComponent/VnSearchBar.spec.js @@ -1,7 +1,6 @@ /// describe('VnSearchBar', () => { const employeeId = ' #1'; - const salesPersonId = ' #18'; const idGap = '.q-item > .q-item__label'; const vnTableRow = '.q-virtual-scroll__content'; beforeEach(() => { @@ -12,11 +11,10 @@ describe('VnSearchBar', () => { it('should redirect to account summary page', () => { searchAndCheck('1', employeeId); - searchAndCheck('salesPerson', salesPersonId); + searchAndCheck('employee', employeeId); }); it('should stay on the list page if there are several results or none', () => { - cy.typeSearchbar('salesA{enter}'); cy.typeSearchbar('salesA{enter}'); checkTableLength(2); @@ -29,7 +27,6 @@ describe('VnSearchBar', () => { const searchAndCheck = (searchTerm, expectedText) => { cy.clearSearchbar(); cy.typeSearchbar(`${searchTerm}{enter}`); - cy.typeSearchbar(`${searchTerm}{enter}`); cy.get(idGap).should('have.text', expectedText); }; diff --git a/test/cypress/integration/vnComponent/VnShortcut.spec.js b/test/cypress/integration/vnComponent/VnShortcut.spec.js index b49b4e964d..e08c446357 100644 --- a/test/cypress/integration/vnComponent/VnShortcut.spec.js +++ b/test/cypress/integration/vnComponent/VnShortcut.spec.js @@ -28,6 +28,17 @@ describe('VnShortcuts', () => { }); cy.url().should('include', module); + if (['monitor', 'claim'].includes(module)) { + return; + } + cy.waitForElement('.q-page').should('exist'); + cy.dataCy('vnTableCreateBtn').should('exist'); + cy.get('.q-page').trigger('keydown', { + ctrlKey: true, + altKey: true, + key: '+', + }); + cy.get('#formModel').should('exist'); }); } }); diff --git a/test/cypress/integration/wagon/wagonCreate.spec.js b/test/cypress/integration/wagon/wagonCreate.spec.js index 501375d8cd..ce3723e540 100644 --- a/test/cypress/integration/wagon/wagonCreate.spec.js +++ b/test/cypress/integration/wagon/wagonCreate.spec.js @@ -8,16 +8,16 @@ describe('WagonCreate', () => { it('should create and delete a new wagon', () => { cy.dataCy('vnTableCreateBtn').click(); cy.get( - '.grid-create > [label="Label"] > .q-field > .q-field__inner > .q-field__control > .q-field__control-container > [data-cy="Label_input"]' + '.grid-create > [label="Label"] > .q-field > .q-field__inner > .q-field__control > .q-field__control-container > [data-cy="Label_input"]', ).type('1234'); cy.get( - '.grid-create > [label="Plate"] > .q-field > .q-field__inner > .q-field__control > .q-field__control-container > [data-cy="Plate_input"]' + '.grid-create > [label="Plate"] > .q-field > .q-field__inner > .q-field__control > .q-field__control-container > [data-cy="Plate_input"]', ).type('1234ABCD'); cy.get( - '.grid-create > [label="Volume"] > .q-field > .q-field__inner > .q-field__control > .q-field__control-container > [data-cy="Volume_input"]' + '.grid-create > [label="Volume"] > .q-field > .q-field__inner > .q-field__control > .q-field__control-container > [data-cy="Volume_input"]', ).type('100'); cy.dataCy('Type_select').type('{downarrow}{enter}'); - // // Delete wagon type created - cy.get('[to="/null/1"] > .q-card > .column > [title="Remove"]').click(); + + cy.get('[title="Remove"] > .q-btn__content > .q-icon').click(); }); }); diff --git a/test/cypress/integration/wagon/wagonType/wagonTypeCreate.spec.js b/test/cypress/integration/wagon/wagonType/wagonTypeCreate.spec.js index 0ad98e5973..343c1c1271 100644 --- a/test/cypress/integration/wagon/wagonType/wagonTypeCreate.spec.js +++ b/test/cypress/integration/wagon/wagonType/wagonTypeCreate.spec.js @@ -6,14 +6,10 @@ describe('WagonTypeCreate', () => { cy.waitForElement('.q-page', 6000); }); - it('should create a new wagon type', () => { + it('should create a new wagon type and then delete it', () => { cy.get('.q-page-sticky > div > .q-btn').click(); cy.get('input').first().type('Example for testing'); cy.get('button[type="submit"]').click(); - }); - it('delete a wagon type', () => { - cy.get( - '[to="/null/2"] > .q-card > .column > [title="Remove"] > .q-btn__content > .q-icon' - ).click(); + cy.get('[title="Remove"] > .q-btn__content > .q-icon').first().click(); }); }); diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js index 6a436c1eba..2c93fbf844 100755 --- a/test/cypress/support/commands.js +++ b/test/cypress/support/commands.js @@ -289,40 +289,13 @@ Cypress.Commands.add('openActionDescriptor', (opt) => { cy.openActionsDescriptor(); const listItem = '[role="menu"] .q-list .q-item'; cy.contains(listItem, opt).click(); - 1; }); Cypress.Commands.add('openActionsDescriptor', () => { cy.get('[data-cy="descriptor-more-opts"]').click(); }); -Cypress.Commands.add('clickButtonsDescriptor', (id) => { - cy.get(`.actions > .q-card__actions> .q-btn:nth-child(${id})`) - .invoke('removeAttr', 'target') - .click(); -}); - -Cypress.Commands.add('openActionDescriptor', (opt) => { - cy.openActionsDescriptor(); - const listItem = '[role="menu"] .q-list .q-item'; - cy.contains(listItem, opt).click(); - 1; -}); - -Cypress.Commands.add('clickButtonsDescriptor', (id) => { - cy.get(`.actions > .q-card__actions> .q-btn:nth-child(${id})`) - .invoke('removeAttr', 'target') - .click(); -}); - -Cypress.Commands.add('openActionDescriptor', (opt) => { - cy.openActionsDescriptor(); - const listItem = '[role="menu"] .q-list .q-item'; - cy.contains(listItem, opt).click(); - 1; -}); - -Cypress.Commands.add('clickButtonsDescriptor', (id) => { +Cypress.Commands.add('clickButtonDescriptor', (id) => { cy.get(`.actions > .q-card__actions> .q-btn:nth-child(${id})`) .invoke('removeAttr', 'target') .click(); @@ -374,3 +347,21 @@ Cypress.Commands.add('addBtnClick', () => { .and('be.visible') .click(); }); + +Cypress.Commands.add('clickButtonWith', (type, value) => { + switch (type) { + case 'icon': + cy.clickButtonWithIcon(value); + break; + + default: + cy.clickButtonWithText(value); + break; + } +}); +Cypress.Commands.add('clickButtonWithIcon', (iconClass) => { + cy.get(`.q-icon.${iconClass}`).parent().click(); +}); +Cypress.Commands.add('clickButtonWithText', (buttonText) => { + cy.get('.q-btn').contains(buttonText).click(); +});