diff --git a/Jenkinsfile b/Jenkinsfile index 63577dad5..2f11556b5 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -120,6 +120,8 @@ pipeline { def image = docker.build('lilium-dev', '-f docs/Dockerfile.dev docs') sh 'docker login --username $CREDS_USR --password $CREDS_PSW $REGISTRY' + sh "docker-compose ${env.COMPOSE_PARAMS} pull back" + sh "docker-compose ${env.COMPOSE_PARAMS} pull db" sh "docker-compose ${env.COMPOSE_PARAMS} up -d" image.inside("--network ${env.COMPOSE_PROJECT}_default -e CI -e TZ --init") { diff --git a/README.md b/README.md index 262e12e58..d280e29ce 100644 --- a/README.md +++ b/README.md @@ -32,6 +32,18 @@ pnpm run test:front pnpm run test:e2e ``` +### Run e2e parallel + +```bash +pnpm run test:e2e:parallel +``` + +### View e2e parallel report + +```bash +pnpm run test:e2e:summary +``` + ### Build the app for production ```bash diff --git a/docs/Dockerfile.dev b/docs/Dockerfile.dev index 84a4d80bc..3117e2c20 100644 --- a/docs/Dockerfile.dev +++ b/docs/Dockerfile.dev @@ -25,6 +25,8 @@ RUN apt-get update \ libnss3 \ libxss1 \ libxtst6 \ + mesa-vulkan-drivers \ + vulkan-tools \ xauth \ xvfb \ && apt-get clean \ diff --git a/package.json b/package.json index 33b730b9e..076cbbb14 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "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:e2e:parallel": "bash ./test/cypress/cypressParallel.sh", + "test:e2e:parallel": "bash ./test/cypress/run.sh", "test:e2e:summary": "bash ./test/cypress/summary.sh", "test": "echo \"See package.json => scripts for available tests.\" && exit 0", "test:front": "vitest", @@ -56,6 +56,7 @@ "eslint-plugin-cypress": "^4.1.0", "eslint-plugin-vue": "^9.32.0", "husky": "^8.0.0", + "junit-merge": "^2.0.0", "mocha": "^11.1.0", "postcss": "^8.4.23", "prettier": "^3.4.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 168fb9e0d..51fc75469 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -91,6 +91,9 @@ devDependencies: husky: specifier: ^8.0.0 version: 8.0.3 + junit-merge: + specifier: ^2.0.0 + version: 2.0.0 mocha: specifier: ^11.1.0 version: 11.1.0 @@ -4860,6 +4863,10 @@ packages: universalify: 2.0.1 dev: true + /fs-readdir-recursive@1.1.0: + resolution: {integrity: sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==} + dev: true + /fs.realpath@1.0.0: resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} @@ -5640,6 +5647,16 @@ packages: verror: 1.10.0 dev: true + /junit-merge@2.0.0: + resolution: {integrity: sha512-qwENzBWcdHPazNqPO0fKyFIqEyaSKyO0iyBeIU4Y/scjkXYpwTi88P2S/PWecqgMhzG2MOCwXk8QB9ucvXeIPw==} + hasBin: true + dependencies: + commander: 2.20.3 + fs-readdir-recursive: 1.1.0 + mkdirp: 0.5.6 + xmldoc: 1.3.0 + dev: true + /keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} dependencies: @@ -6037,7 +6054,6 @@ packages: hasBin: true dependencies: minimist: 1.2.8 - dev: false /mlly@1.7.4: resolution: {integrity: sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw==} @@ -7253,6 +7269,10 @@ packages: resolution: {integrity: sha512-5f3k2PbGGp+YtKJjOItpg3P99IMD84E4HOvcfleTb5joCHNXYLsR9yWFPOYGgaeMPDubQILTCMdsFb2OMeOjtg==} dev: true + /sax@1.4.1: + resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + dev: true + /scheduler@0.25.0: resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==} dev: true @@ -8689,6 +8709,12 @@ packages: engines: {node: '>=4.0'} dev: true + /xmldoc@1.3.0: + resolution: {integrity: sha512-y7IRWW6PvEnYQZNZFMRLNJw+p3pezM4nKYPfr15g4OOW9i8VpeydycFuipE2297OvZnh3jSb2pxOt9QpkZUVng==} + dependencies: + sax: 1.4.1 + dev: true + /xunit-viewer@10.6.1(@babel/runtime@7.26.9)(@codemirror/autocomplete@6.18.6)(@codemirror/language@6.10.8)(@codemirror/lint@6.8.4)(@codemirror/search@6.5.10)(@codemirror/state@6.5.2)(@codemirror/theme-one-dark@6.1.2)(@codemirror/view@6.36.3)(codemirror@6.0.1)(react-dom@19.0.0)(react@19.0.0): resolution: {integrity: sha512-ZMprLPVhCQJf2KD56tv2hlOjc4T+KnUe1E9DkEBHnuliOq7IOXWJf61pxyBMo/7H83B7Ln0DIeWNMMbx/3I7Jg==} hasBin: true diff --git a/src/components/FilterItemForm.vue b/src/components/FilterItemForm.vue index cacfde1b3..cca8d80c3 100644 --- a/src/components/FilterItemForm.vue +++ b/src/components/FilterItemForm.vue @@ -188,7 +188,7 @@ const selectItem = ({ id }) => { > <template #body-cell-id="{ row }"> <QTd auto-width @click.stop> - <QBtn flat color="blue">{{ row.id }}</QBtn> + <QBtn flat class="link">{{ row.id }}</QBtn> <ItemDescriptorProxy :id="row.id" /> </QTd> </template> diff --git a/src/components/FilterTravelForm.vue b/src/components/FilterTravelForm.vue index c522d0269..4aad327b2 100644 --- a/src/components/FilterTravelForm.vue +++ b/src/components/FilterTravelForm.vue @@ -196,7 +196,7 @@ const selectTravel = ({ id }) => { > <template #body-cell-id="{ row }"> <QTd auto-width @click.stop data-cy="travelFk-travel-form"> - <QBtn flat color="blue">{{ row.id }}</QBtn> + <QBtn flat class="link">{{ row.id }}</QBtn> <TravelDescriptorProxy :id="row.id" /> </QTd> </template> diff --git a/src/components/LeftMenu.vue b/src/components/LeftMenu.vue index 9a9949499..8e83bf579 100644 --- a/src/components/LeftMenu.vue +++ b/src/components/LeftMenu.vue @@ -77,6 +77,7 @@ watch( function findMatches(search, item) { const matches = []; function findRoute(search, item) { + if (!item?.children) return; for (const child of item.children) { if (search?.indexOf(child.name) > -1) { matches.push(child); @@ -92,7 +93,7 @@ function findMatches(search, item) { } function addChildren(module, route, parent) { - const menus = route?.meta?.menu ?? route?.menus?.[props.source]; //backwards compatible + const menus = route?.meta?.menu; if (!menus) return; const matches = findMatches(menus, route); @@ -107,11 +108,7 @@ function getRoutes() { main: getMainRoutes, card: getCardRoutes, }; - try { - handleRoutes[props.source](); - } catch (error) { - throw new Error(`Method is not defined`); - } + handleRoutes[props.source](); } function getMainRoutes() { const modules = Object.assign([], navigation.getModules().value); @@ -122,7 +119,6 @@ function getMainRoutes() { ); if (!moduleDef) continue; item.children = []; - addChildren(item.module, moduleDef, item.children); } @@ -132,21 +128,16 @@ function getMainRoutes() { function getCardRoutes() { const currentRoute = route.matched[1]; const currentModule = toLowerCamel(currentRoute.name); - let moduleDef = routes.find((route) => toLowerCamel(route.name) === currentModule); + let moduleDef; - if (!moduleDef) return; - if (!moduleDef?.menus) moduleDef = betaGetRoutes(); - addChildren(currentModule, moduleDef, items.value); -} - -function betaGetRoutes() { - let menuRoute; let index = route.matched.length - 1; - while (!menuRoute && index > 0) { - if (route.matched[index]?.meta?.menu) menuRoute = route.matched[index]; + while (!moduleDef && index > 0) { + if (route.matched[index]?.meta?.menu) moduleDef = route.matched[index]; index--; } - return menuRoute; + + if (!moduleDef) return; + addChildren(currentModule, moduleDef, items.value); } async function togglePinned(item, event) { diff --git a/src/components/TicketProblems.vue b/src/components/TicketProblems.vue index 255bea9cd..59be95035 100644 --- a/src/components/TicketProblems.vue +++ b/src/components/TicketProblems.vue @@ -28,6 +28,17 @@ defineProps({ row: { type: Object, required: true } }); {{ t('ticketSale.reserved') }} </QTooltip> </QIcon> + <QIcon + v-if="row?.isDeleted" + color="primary" + name="vn:deletedTicket" + size="xs" + data-cy="ticketDeletedIcon" + > + <QTooltip> + {{ t('Ticket deleted') }} + </QTooltip> + </QIcon> <QIcon v-if="row?.hasRisk" name="vn:risk" diff --git a/src/components/VnTable/VnFilter.vue b/src/components/VnTable/VnFilter.vue index e9660e4c2..82d7c772c 100644 --- a/src/components/VnTable/VnFilter.vue +++ b/src/components/VnTable/VnFilter.vue @@ -6,6 +6,7 @@ import VnSelect from 'components/common/VnSelect.vue'; import VnInput from 'components/common/VnInput.vue'; import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputTime from 'components/common/VnInputTime.vue'; +import VnCheckbox from 'components/common/VnCheckbox.vue'; import VnColumn from 'components/VnTable/VnColumn.vue'; const $props = defineProps({ @@ -106,7 +107,7 @@ const components = { }, }, checkbox: { - component: markRaw(QCheckbox), + component: markRaw(VnCheckbox), event: updateEvent, attrs: { class: $props.showTitle ? 'q-py-sm' : 'q-px-md q-py-xs fit', diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue index fee8a169f..6a547d95d 100644 --- a/src/components/VnTable/VnTable.vue +++ b/src/components/VnTable/VnTable.vue @@ -919,12 +919,24 @@ const rowCtrlClickFunction = computed(() => { :row-index="index" > <VnColumn - :column="col" + :column="{ + ...col, + disable: + col?.component === + 'checkbox' + ? true + : false, + }" :row="row" :is-editable="false" v-model="row[col.name]" component-prop="columnField" - :show-label="true" + :show-label=" + col?.component === + 'checkbox' + ? false + : true + " /> </slot> </span> diff --git a/src/components/__tests__/Leftmenu.spec.js b/src/components/__tests__/Leftmenu.spec.js index 4ab8b527f..0bcc587ac 100644 --- a/src/components/__tests__/Leftmenu.spec.js +++ b/src/components/__tests__/Leftmenu.spec.js @@ -15,10 +15,7 @@ vi.mock('src/router/modules', () => ({ meta: { title: 'customers', icon: 'vn:client', - }, - menus: { - main: ['CustomerList', 'CustomerCreate'], - card: ['CustomerBasicData'], + menu: ['CustomerList', 'CustomerCreate'], }, children: [ { @@ -50,14 +47,6 @@ vi.mock('src/router/modules', () => ({ ], }, }, - { - path: 'create', - name: 'CustomerCreate', - meta: { - title: 'createCustomer', - icon: 'vn:addperson', - }, - }, ], }, ], @@ -98,7 +87,7 @@ vi.spyOn(vueRouter, 'useRoute').mockReturnValue({ icon: 'vn:client', moduleName: 'Customer', keyBinding: 'c', - menu: 'customer', + menu: ['customer'], }, }, ], @@ -260,15 +249,6 @@ describe('Leftmenu as main', () => { }); }); - it('should handle a single matched route with a menu', () => { - const route = { - matched: [{ meta: { menu: 'customer' } }], - }; - - const result = vm.betaGetRoutes(); - - expect(result.meta.menu).toEqual(route.matched[0].meta.menu); - }); it('should get routes for main source', () => { vm.props.source = 'main'; vm.getRoutes(); @@ -351,8 +331,9 @@ describe('addChildren', () => { it('should handle routes with no meta menu', () => { const route = { - meta: {}, - menus: {}, + meta: { + menu: [], + }, }; const parent = []; diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue index d6b525dc8..93e3a57f2 100644 --- a/src/components/ui/VnFilterPanel.vue +++ b/src/components/ui/VnFilterPanel.vue @@ -54,7 +54,7 @@ const $props = defineProps({ default: 'table', }, redirect: { - type: Boolean, + type: [String, Boolean], default: true, }, arrayData: { diff --git a/src/components/ui/VnNotes.vue b/src/components/ui/VnNotes.vue index eb0804af0..6ce28254d 100644 --- a/src/components/ui/VnNotes.vue +++ b/src/components/ui/VnNotes.vue @@ -186,7 +186,7 @@ function fetchData([data]) { ref="vnPaginateRef" class="show" v-bind="$attrs" - search-url="notes" + :search-url="false" @on-fetch=" newNote.text = ''; newNote.observationTypeFk = null; diff --git a/src/composables/getColAlign.js b/src/composables/getColAlign.js index a930fd7d8..c1841e134 100644 --- a/src/composables/getColAlign.js +++ b/src/composables/getColAlign.js @@ -9,6 +9,8 @@ export function getColAlign(col) { case 'number': align = 'right'; break; + case 'time': + case 'date': case 'checkbox': align = 'center'; break; diff --git a/src/css/app.scss b/src/css/app.scss index b8b53a929..5befd150b 100644 --- a/src/css/app.scss +++ b/src/css/app.scss @@ -15,6 +15,7 @@ body.body--light { --vn-empty-tag: #acacac; --vn-black-text-color: black; --vn-text-color-contrast: white; + --vn-link-color: #1e90ff; background-color: var(--vn-page-color); @@ -38,6 +39,7 @@ body.body--dark { --vn-empty-tag: #2d2d2d; --vn-black-text-color: black; --vn-text-color-contrast: black; + --vn-link-color: #66bfff; background-color: var(--vn-page-color); @@ -49,7 +51,7 @@ a { } .link { - color: $color-link; + color: var(--vn-link-color); cursor: pointer; &--white { @@ -58,14 +60,14 @@ a { } .tx-color-link { - color: $color-link !important; + color: var(--vn-link-color) !important; } .tx-color-font { - color: $color-link !important; + color: var(--vn-link-color) !important; } .header-link { - color: $color-link !important; + color: var(--vn-link-color) !important; cursor: pointer; border-bottom: solid $primary; border-width: 2px; diff --git a/src/css/quasar.variables.scss b/src/css/quasar.variables.scss index 22c6d2b56..45d18af7e 100644 --- a/src/css/quasar.variables.scss +++ b/src/css/quasar.variables.scss @@ -24,7 +24,6 @@ $alert: $negative; $white: #fff; $dark: #3d3d3d; // custom -$color-link: #66bfff; $color-spacer-light: #a3a3a31f; $color-spacer: #7979794d; $border-thin-light: 1px solid $color-spacer-light; diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml index d7187371e..c1286267c 100644 --- a/src/i18n/locale/en.yml +++ b/src/i18n/locale/en.yml @@ -99,7 +99,6 @@ globals: file: File selectFile: Select a file copyClipboard: Copy on clipboard - salesPerson: SalesPerson send: Send code: Code since: Since @@ -158,7 +157,9 @@ globals: changeState: Change state raid: 'Raid {daysInForward} days' isVies: Vies + department: Department noData: No data available + vehicle: Vehicle pageTitles: logIn: Login addressEdit: Update address @@ -346,7 +347,6 @@ globals: params: description: Description clientFk: Client id - salesPersonFk: Sales person warehouseFk: Warehouse provinceFk: Province stateFk: State @@ -531,6 +531,7 @@ ticket: customerCard: Customer card ticketList: Ticket List newOrder: New Order + ticketClaimed: Claimed ticket boxing: expedition: Expedition created: Created @@ -603,7 +604,6 @@ worker: balance: Balance medical: Medical list: - department: Department schedule: Schedule newWorker: New worker summary: @@ -862,7 +862,6 @@ components: mine: For me hasMinPrice: Minimum price # LatestBuysFilter - salesPersonFk: Buyer supplierFk: Supplier from: From to: To diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml index fc3018f39..681781d11 100644 --- a/src/i18n/locale/es.yml +++ b/src/i18n/locale/es.yml @@ -103,7 +103,6 @@ globals: file: Fichero selectFile: Seleccione un fichero copyClipboard: Copiar en portapapeles - salesPerson: Comercial send: Enviar code: Código since: Desde @@ -163,6 +162,8 @@ globals: raid: 'Redada {daysInForward} días' isVies: Vies noData: Datos no disponibles + department: Departamento + vehicle: Vehículo pageTitles: logIn: Inicio de sesión addressEdit: Modificar consignatario @@ -349,7 +350,6 @@ globals: params: description: Descripción clientFk: Id cliente - salesPersonFk: Comercial warehouseFk: Almacén provinceFk: Provincia stateFk: Estado @@ -531,13 +531,13 @@ ticket: state: Estado shipped: Enviado landed: Entregado - salesPerson: Comercial total: Total card: customerId: ID cliente customerCard: Ficha del cliente ticketList: Listado de tickets newOrder: Nuevo pedido + ticketClaimed: Ticket reclamado boxing: expedition: Expedición created: Creado @@ -622,8 +622,6 @@ invoiceOut: errors: downloadCsvFailed: Error al descargar CSV order: - field: - salesPersonFk: Comercial form: clientFk: Cliente addressFk: Dirección @@ -691,7 +689,6 @@ worker: formation: Formación medical: Mutua list: - department: Departamento schedule: Horario newWorker: Nuevo trabajador summary: @@ -949,7 +946,6 @@ components: hasMinPrice: Precio mínimo wareHouseFk: Almacén # LatestBuysFilter - salesPersonFk: Comprador supplierFk: Proveedor visible: Visible active: Activo diff --git a/src/pages/Account/Alias/Card/AliasSummary.vue b/src/pages/Account/Alias/Card/AliasSummary.vue index b4b9abd25..cfd33ec82 100644 --- a/src/pages/Account/Alias/Card/AliasSummary.vue +++ b/src/pages/Account/Alias/Card/AliasSummary.vue @@ -5,6 +5,7 @@ import { useI18n } from 'vue-i18n'; import CardSummary from 'components/ui/CardSummary.vue'; import VnLv from 'src/components/ui/VnLv.vue'; +import VnTitle from 'src/components/common/VnTitle.vue'; const route = useRoute(); const { t } = useI18n(); @@ -27,13 +28,10 @@ const entityId = computed(() => $props.id || route.params.id); <template #body="{ entity: alias }"> <QCard class="vn-one"> <QCardSection class="q-pa-none"> - <router-link - :to="{ name: 'AliasBasicData', params: { id: entityId } }" - class="header header-link" - > - {{ t('globals.summary.basicData') }} - <QIcon name="open_in_new" /> - </router-link> + <VnTitle + :url="`#/account/alias/${entityId}/basic-data`" + :text="t('globals.summary.basicData')" + /> </QCardSection> <VnLv :label="t('role.id')" :value="alias.id" /> <VnLv :label="t('role.description')" :value="alias.description" /> diff --git a/src/pages/Account/Card/AccountSummary.vue b/src/pages/Account/Card/AccountSummary.vue index f7a16e8c3..2172fec9a 100644 --- a/src/pages/Account/Card/AccountSummary.vue +++ b/src/pages/Account/Card/AccountSummary.vue @@ -5,6 +5,7 @@ import CardSummary from 'components/ui/CardSummary.vue'; import VnLv from 'src/components/ui/VnLv.vue'; import filter from './AccountFilter.js'; import AccountDescriptorMenu from './AccountDescriptorMenu.vue'; +import VnTitle from 'src/components/common/VnTitle.vue'; const $props = defineProps({ id: { type: Number, default: 0 } }); @@ -26,13 +27,10 @@ const entityId = computed(() => $props.id || route.params.id); <template #body="{ entity }"> <QCard class="vn-one"> <QCardSection class="q-pa-none"> - <router-link - :to="{ name: 'AccountBasicData', params: { id: entityId } }" - class="header header-link" - > - {{ $t('globals.pageTitles.basicData') }} - <QIcon name="open_in_new" /> - </router-link> + <VnTitle + :url="`#/account/${entityId}/basic-data`" + :text="$t('globals.pageTitles.basicData')" + /> </QCardSection> <VnLv :label="$t('account.card.nickname')" :value="entity.name" /> <VnLv :label="$t('account.card.role')" :value="entity.role?.name" /> diff --git a/src/pages/Account/Role/Card/RoleSummary.vue b/src/pages/Account/Role/Card/RoleSummary.vue index 410f90b17..baa4afeca 100644 --- a/src/pages/Account/Role/Card/RoleSummary.vue +++ b/src/pages/Account/Role/Card/RoleSummary.vue @@ -4,6 +4,7 @@ import { useRoute } from 'vue-router'; import { useI18n } from 'vue-i18n'; import CardSummary from 'components/ui/CardSummary.vue'; import VnLv from 'src/components/ui/VnLv.vue'; +import VnTitle from 'src/components/common/VnTitle.vue'; const route = useRoute(); const { t } = useI18n(); @@ -29,13 +30,10 @@ const entityId = computed(() => $props.id || route.params.id); <template #body="{ entity }"> <QCard class="vn-one"> <QCardSection class="q-pa-none"> - <a - class="header header-link" - :href="`#/VnUser/${entityId}/basic-data`" - > - {{ t('globals.pageTitles.basicData') }} - <QIcon name="open_in_new" /> - </a> + <VnTitle + :url="`#/account/role/${entityId}/basic-data`" + :text="$t('globals.pageTitles.basicData')" + /> </QCardSection> <VnLv :label="t('role.id')" :value="entity.id" /> <VnLv :label="t('globals.name')" :value="entity.name" /> diff --git a/src/pages/Claim/Card/ClaimDescriptor.vue b/src/pages/Claim/Card/ClaimDescriptor.vue index 4551c58fe..d789b63d3 100644 --- a/src/pages/Claim/Card/ClaimDescriptor.vue +++ b/src/pages/Claim/Card/ClaimDescriptor.vue @@ -5,6 +5,7 @@ import { useI18n } from 'vue-i18n'; import { toDateHourMinSec, toPercentage } from 'src/filters'; import TicketDescriptorProxy from 'pages/Ticket/Card/TicketDescriptorProxy.vue'; import ClaimDescriptorMenu from 'pages/Claim/Card/ClaimDescriptorMenu.vue'; +import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue'; import CardDescriptor from 'components/ui/CardDescriptor.vue'; import VnLv from 'src/components/ui/VnLv.vue'; import VnUserLink from 'src/components/ui/VnUserLink.vue'; @@ -65,12 +66,12 @@ onMounted(async () => { </template> </VnLv> <VnLv :label="t('claim.created')" :value="toDateHourMinSec(entity.created)" /> - <VnLv :label="t('claim.commercial')"> + <VnLv :label="t('globals.department')"> <template #value> - <VnUserLink - :name="entity.client?.salesPersonUser?.name" - :worker-id="entity.client?.salesPersonFk" - /> + <span class="link"> + {{ entity?.client?.department?.name || '-' }} + <DepartmentDescriptorProxy :id="entity?.client?.departmentFk" /> + </span> </template> </VnLv> <VnLv diff --git a/src/pages/Claim/Card/ClaimFilter.js b/src/pages/Claim/Card/ClaimFilter.js index 50cabe228..4f119544c 100644 --- a/src/pages/Claim/Card/ClaimFilter.js +++ b/src/pages/Claim/Card/ClaimFilter.js @@ -14,7 +14,7 @@ export default { relation: 'client', scope: { include: [ - { relation: 'salesPersonUser' }, + { relation: 'department' }, { relation: 'claimsRatio', scope: { diff --git a/src/pages/Claim/Card/ClaimLines.vue b/src/pages/Claim/Card/ClaimLines.vue index dee03b95d..7c948bb2f 100644 --- a/src/pages/Claim/Card/ClaimLines.vue +++ b/src/pages/Claim/Card/ClaimLines.vue @@ -117,7 +117,7 @@ const selected = ref([]); const mana = ref(0); async function fetchMana() { const ticketId = claim.value.ticketFk; - const response = await axios.get(`Tickets/${ticketId}/getSalesPersonMana`); + const response = await axios.get(`Tickets/${ticketId}/getDepartmentMana`); mana.value = response.data; } diff --git a/src/pages/Claim/Card/ClaimSummary.vue b/src/pages/Claim/Card/ClaimSummary.vue index 210b0c982..5d06d5627 100644 --- a/src/pages/Claim/Card/ClaimSummary.vue +++ b/src/pages/Claim/Card/ClaimSummary.vue @@ -19,6 +19,7 @@ import ClaimNotes from 'src/pages/Claim/Card/ClaimNotes.vue'; import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue'; import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; +import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue'; import ClaimDescriptorMenu from './ClaimDescriptorMenu.vue'; const route = useRoute(); @@ -252,13 +253,15 @@ function claimUrl(section) { </VnLv> <VnLv v-if="$route.name != 'ClaimSummary'" - :label="t('globals.salesPerson')" + :label="t('customer.summary.team')" > <template #value> - <VnUserLink - :name="claim.client?.salesPersonUser?.name" - :worker-id="claim.client?.salesPersonFk" - /> + <span class="link"> + {{ claim?.client?.department?.name || '-' }} + <DepartmentDescriptorProxy + :id="claim?.client?.departmentFk" + /> + </span> </template> </VnLv> <VnLv v-if="$route.name != 'ClaimSummary'" :label="t('claim.attendedBy')"> @@ -271,7 +274,7 @@ function claimUrl(section) { </VnLv> <VnLv v-if="$route.name != 'ClaimSummary'" :label="t('claim.customer')"> <template #value> - <span class="link cursor-pointer"> + <span class="link"> {{ claim.client?.name }} <CustomerDescriptorProxy :id="claim.clientFk" /> </span> diff --git a/src/pages/Claim/Card/ClaimSummaryAction.vue b/src/pages/Claim/Card/ClaimSummaryAction.vue index e5273902c..577ac2a65 100644 --- a/src/pages/Claim/Card/ClaimSummaryAction.vue +++ b/src/pages/Claim/Card/ClaimSummaryAction.vue @@ -80,7 +80,7 @@ const columns = [ :right-search="false" :column-search="false" :disable-option="{ card: true, table: true }" - search-url="actions" + :search-url="false" :filter="{ where: { claimFk: $props.id } }" :columns="columns" :limit="0" diff --git a/src/pages/Claim/ClaimFilter.vue b/src/pages/Claim/ClaimFilter.vue index 0fe7fc588..37146865c 100644 --- a/src/pages/Claim/ClaimFilter.vue +++ b/src/pages/Claim/ClaimFilter.vue @@ -44,15 +44,14 @@ const props = defineProps({ is-outlined /> <VnSelect - :label="t('Salesperson')" - v-model="params.salesPersonFk" - url="Workers/activeWithInheritedRole" - :filter="{ where: { role: 'salesPerson' } }" - :use-like="false" - option-filter="firstName" - dense outlined + dense rounded + :label="t('globals.params.departmentFk')" + v-model="params.departmentFk" + option-value="id" + option-label="name" + url="Departments" /> <VnSelect :label="t('claim.attendedBy')" @@ -126,7 +125,6 @@ en: search: Contains clientFk: Customer clientName: Customer - salesPersonFk: Salesperson attenderFk: Attender claimResponsibleFk: Responsible claimStateFk: State @@ -139,7 +137,6 @@ es: search: Contiene clientFk: Cliente clientName: Cliente - salesPersonFk: Comercial attenderFk: Asistente claimResponsibleFk: Responsable claimStateFk: Estado @@ -148,6 +145,5 @@ es: itemFk: Artículo zoneFk: Zona Client Name: Nombre del cliente - Salesperson: Comercial Item: Artículo </i18n> diff --git a/src/pages/Claim/ClaimList.vue b/src/pages/Claim/ClaimList.vue index 41d0c5598..06996c2c1 100644 --- a/src/pages/Claim/ClaimList.vue +++ b/src/pages/Claim/ClaimList.vue @@ -4,6 +4,7 @@ import { useI18n } from 'vue-i18n'; import { toDate } from 'filters/index'; import ClaimFilter from './ClaimFilter.vue'; import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue'; +import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue'; import VnUserLink from 'src/components/ui/VnUserLink.vue'; import ClaimSummary from './Card/ClaimSummary.vue'; import { useSummaryDialog } from 'src/composables/useSummaryDialog'; @@ -48,6 +49,20 @@ const columns = computed(() => [ }, columnClass: 'expand', }, + { + align: 'left', + name: 'departmentFk', + label: t('customer.summary.team'), + component: 'select', + attrs: { + url: 'Departments', + }, + create: true, + columnField: { + component: null, + }, + format: (row, dashIfEmpty) => dashIfEmpty(row.departmentName), + }, { align: 'left', label: t('claim.attendedBy'), @@ -152,6 +167,12 @@ const STATE_COLOR = { <CustomerDescriptorProxy :id="row.clientFk" /> </span> </template> + <template #column-departmentFk="{ row }"> + <span class="link" @click.stop> + {{ row.departmentName || '-' }} + <DepartmentDescriptorProxy :id="row?.departmentFk" /> + </span> + </template> <template #column-attendedBy="{ row }"> <span @click.stop> <VnUserLink :name="row.workerName" :worker-id="row.workerFk" /> diff --git a/src/pages/Customer/Card/CustomerBasicData.vue b/src/pages/Customer/Card/CustomerBasicData.vue index 36ec4763e..9c9d1b50b 100644 --- a/src/pages/Customer/Card/CustomerBasicData.vue +++ b/src/pages/Customer/Card/CustomerBasicData.vue @@ -8,7 +8,6 @@ import FormModel from 'components/FormModel.vue'; import VnRow from 'components/ui/VnRow.vue'; import VnInput from 'src/components/common/VnInput.vue'; import VnSelect from 'src/components/common/VnSelect.vue'; -import VnSelectWorker from 'src/components/common/VnSelectWorker.vue'; import { getDifferences, getUpdatedValues } from 'src/filters'; const route = useRoute(); @@ -37,7 +36,7 @@ const exprBuilder = (param, value) => { function onBeforeSave(formData, originalData) { return getUpdatedValues( Object.keys(getDifferences(formData, originalData)), - formData + formData, ); } </script> @@ -119,16 +118,11 @@ function onBeforeSave(formData, originalData) { /> </VnRow> <VnRow> - <VnSelectWorker - :label="t('customer.summary.salesPerson')" - v-model="data.salesPersonFk" - :params="{ - departmentCodes: ['VT', 'shopping'], - }" - :has-avatar="true" - :rules="validate('client.salesPersonFk')" - :expr-builder="exprBuilder" - emit-value + <VnSelect + :label="t('globals.department')" + v-model="data.departmentFk" + url="Departments" + :fields="['id', 'name']" /> <VnSelect v-model="data.contactChannelFk" @@ -160,7 +154,7 @@ function onBeforeSave(formData, originalData) { <QIcon name="info" class="cursor-pointer"> <QTooltip>{{ t( - 'In case of a company succession, specify the grantor company' + 'In case of a company succession, specify the grantor company', ) }}</QTooltip> </QIcon> diff --git a/src/pages/Customer/Card/CustomerDescriptor.vue b/src/pages/Customer/Card/CustomerDescriptor.vue index e3156dd6d..8978c00f1 100644 --- a/src/pages/Customer/Card/CustomerDescriptor.vue +++ b/src/pages/Customer/Card/CustomerDescriptor.vue @@ -3,14 +3,14 @@ import { onMounted, ref, computed } from 'vue'; import { useRoute } from 'vue-router'; import { useI18n } from 'vue-i18n'; -import { dashIfEmpty, toCurrency, toDate } from 'src/filters'; +import { toCurrency, toDate } from 'src/filters'; import useCardDescription from 'src/composables/useCardDescription'; import CardDescriptor from 'components/ui/CardDescriptor.vue'; import VnLv from 'src/components/ui/VnLv.vue'; -import VnUserLink from 'src/components/ui/VnUserLink.vue'; import CustomerDescriptorMenu from './CustomerDescriptorMenu.vue'; +import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue'; import { useState } from 'src/composables/useState'; const state = useState(); @@ -84,14 +84,10 @@ const debtWarning = computed(() => { :value="toCurrency(entity.debt)" :info="t('customer.summary.riskInfo')" /> - <VnLv :label="t('customer.summary.salesPerson')"> + <VnLv :label="t('globals.department')"> <template #value> - <VnUserLink - v-if="entity.salesPersonUser" - :name="entity.salesPersonUser.name" - :worker-id="entity.salesPersonFk" - /> - <span v-else>{{ dashIfEmpty(entity.salesPersonUser) }}</span> + <span class="link" v-text="entity.department?.name" /> + <DepartmentDescriptorProxy :id="entity.department?.id" /> </template> </VnLv> <VnLv diff --git a/src/pages/Customer/Card/CustomerFileManagement.vue b/src/pages/Customer/Card/CustomerFileManagement.vue index b565db6e7..419719251 100644 --- a/src/pages/Customer/Card/CustomerFileManagement.vue +++ b/src/pages/Customer/Card/CustomerFileManagement.vue @@ -86,12 +86,12 @@ const tableColumnComponents = { }, file: { component: QBtn, - props: () => ({ flat: true, color: 'blue' }), + props: () => ({ flat: true }), event: ({ row }) => downloadFile(row.dmsFk), }, employee: { component: QBtn, - props: () => ({ flat: true, color: 'blue' }), + props: () => ({ flat: true }), event: () => {}, }, created: { @@ -214,8 +214,17 @@ const toCustomerFileManagementCreate = () => { v-bind="tableColumnComponents[props.col.name].props(props)" > <template v-if="props.col.name !== 'original'"> - {{ props.value }} + <span + :class="{ + link: + props.col.name === 'employee' || + props.col.name === 'file', + }" + > + {{ props.value }} + </span> </template> + <WorkerDescriptorProxy :id="props.row.dms.workerFk" v-if="props.col.name === 'employee'" diff --git a/src/pages/Customer/Card/CustomerSummary.vue b/src/pages/Customer/Card/CustomerSummary.vue index c98bf1ffb..7d5d691a3 100644 --- a/src/pages/Customer/Card/CustomerSummary.vue +++ b/src/pages/Customer/Card/CustomerSummary.vue @@ -2,7 +2,6 @@ import { computed, ref } from 'vue'; import { useRoute } from 'vue-router'; import { useI18n } from 'vue-i18n'; -import VnUserLink from 'src/components/ui/VnUserLink.vue'; import { toCurrency, toPercentage, toDate, dashOrCurrency } from 'src/filters'; import CardSummary from 'components/ui/CardSummary.vue'; @@ -13,6 +12,8 @@ import CustomerSummaryTable from 'src/pages/Customer/components/CustomerSummaryT import VnTitle from 'src/components/common/VnTitle.vue'; import VnRow from 'src/components/ui/VnRow.vue'; import CustomerDescriptorMenu from './CustomerDescriptorMenu.vue'; +import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue'; + const route = useRoute(); const { t } = useI18n(); const grafanaUrl = 'https://grafana.verdnatura.es'; @@ -106,16 +107,12 @@ const sumRisk = ({ clientRisks }) => { {{ t('globals.params.email') }} <VnLinkMail email="entity.email"></VnLinkMail> </template ></VnLv> - <VnLv - :label="t('customer.summary.salesPerson')" - :value="entity?.salesPersonUser?.name" - > + <VnLv :label="t('globals.department')"> <template #value> - <VnUserLink - :name="entity.salesPersonUser?.name" - :worker-id="entity.salesPersonFk" - /> </template - ></VnLv> + <span class="link" v-text="entity.department?.name" /> + <DepartmentDescriptorProxy :id="entity?.department?.id" /> + </template> + </VnLv> <VnLv :label="t('customer.summary.contactChannel')" :value="entity?.contactChannel?.name" diff --git a/src/pages/Customer/CustomerCreate.vue b/src/pages/Customer/CustomerCreate.vue deleted file mode 100644 index 79da63283..000000000 --- a/src/pages/Customer/CustomerCreate.vue +++ /dev/null @@ -1,146 +0,0 @@ -<script setup> -import { reactive, ref } from 'vue'; -import { useI18n } from 'vue-i18n'; - -import FetchData from 'components/FetchData.vue'; -import FormModel from 'components/FormModel.vue'; -import VnRow from 'components/ui/VnRow.vue'; -import VnSelect from 'src/components/common/VnSelect.vue'; -import VnLocation from 'src/components/common/VnLocation.vue'; -import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; - -const { t } = useI18n(); - -const initialData = reactive({ - active: true, - isEqualizated: false, -}); - -const workersOptions = ref([]); -const businessTypesOptions = ref([]); - -function handleLocation(data, location) { - const { town, code, provinceFk, countryFk } = location ?? {}; - data.postcode = code; - data.city = town; - data.provinceFk = provinceFk; - data.countryFk = countryFk; -} -</script> - -<template> - <FetchData - @on-fetch="(data) => (workersOptions = data)" - auto-load - url="Workers/search?departmentCodes" - /> - <FetchData - @on-fetch="(data) => (businessTypesOptions = data)" - auto-load - url="BusinessTypes" - /> - <QPage> - <VnSubToolbar /> - <FormModel - :form-initial-data="initialData" - model="client" - url-create="Clients/createWithUser" - > - <template #form="{ data, validate }"> - <VnRow> - <QInput :label="t('Comercial name')" v-model="data.name" /> - <VnSelect - :label="t('Salesperson')" - :options="workersOptions" - hide-selected - option-label="name" - option-value="id" - v-model="data.salesPersonFk" - /> - </VnRow> - <VnRow> - <VnSelect - :label="t('Business type')" - :options="businessTypesOptions" - hide-selected - option-label="description" - option-value="code" - v-model="data.businessTypeFk" - /> - <QInput v-model="data.fi" :label="t('Tax number')" /> - </VnRow> - <VnRow> - <QInput - :label="t('Business name')" - :rules="validate('client.socialName')" - v-model="data.socialName" - /> - </VnRow> - <VnRow> - <QInput - :label="t('Street')" - :rules="validate('client.street')" - v-model="data.street" - /> - </VnRow> - <VnRow> - <VnLocation - :rules="validate('Worker.postcode')" - :acls="[{ model: 'Town', props: '*', accessType: 'WRITE' }]" - v-model="data.location" - @update:model-value="(location) => handleLocation(data, location)" - > - </VnLocation> - </VnRow> - - <VnRow> - <QInput v-model="data.userName" :label="t('Web user')" /> - <QInput - :label="t('Email')" - :rules="validate('client.email')" - clearable - type="email" - v-model="data.email" - > - <template #append> - <QIcon name="info" class="cursor-info"> - <QTooltip max-width="400px">{{ - t('customer.basicData.youCanSaveMultipleEmails') - }}</QTooltip> - </QIcon> - </template> - </QInput> - </VnRow> - <QCheckbox - :label="t('Is equalizated')" - v-model="initialData.isEqualizated" - /> - </template> - </FormModel> - </QPage> -</template> - -<style lang="scss" scoped> -.card { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); - grid-gap: 20px; -} -</style> - -<i18n> -es: - Comercial name: Nombre comercial - Salesperson: Comercial - Business type: Tipo de negocio - Tax number: NIF / CIF - Business name: Razón social - Street: Dirección fiscal - Postcode: Código postal - City: Población - Province: Provincia - Country: País - Web user: Usuario web - Email: Email - Is equalizated: Recargo de equivalencia -</i18n> diff --git a/src/pages/Customer/CustomerFilter.vue b/src/pages/Customer/CustomerFilter.vue index 1c5a08304..2ace6dd02 100644 --- a/src/pages/Customer/CustomerFilter.vue +++ b/src/pages/Customer/CustomerFilter.vue @@ -3,7 +3,6 @@ import { useI18n } from 'vue-i18n'; import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; import VnSelect from 'components/common/VnSelect.vue'; import VnInput from 'src/components/common/VnInput.vue'; -import VnSelectWorker from 'src/components/common/VnSelectWorker.vue'; const { t } = useI18n(); defineProps({ @@ -65,22 +64,15 @@ const exprBuilder = (param, value) => { </QItem> <QItem class="q-mb-sm"> <QItemSection> - <VnSelectWorker - :label="t('Salesperson')" - v-model="params.salesPersonFk" - :params="{ - departmentCodes: ['VT'], - }" - :expr-builder="exprBuilder" - @update:model-value="searchFn()" - emit-value - map-options - use-input - hide-selected - dense + <VnSelect outlined + dense rounded - :input-debounce="0" + :label="t('globals.params.departmentFk')" + v-model="params.departmentFk" + option-value="id" + option-label="name" + url="Departments" /> </QItemSection> </QItem> @@ -164,7 +156,6 @@ en: params: search: Contains fi: FI - salesPersonFk: Salesperson provinceFk: Province isActive: Is active city: City @@ -191,7 +182,6 @@ es: sageTaxTypeFk: Tipo de impuesto Sage sageTransactionTypeFk: Tipo de impuesto Sage payMethodFk: Forma de pago - salesPersonFk: Comercial provinceFk: Provincia city: Ciudad phone: Teléfono @@ -201,7 +191,6 @@ es: name: Nombre postcode: CP FI: NIF - Salesperson: Comercial Province: Provincia City: Ciudad Phone: Teléfono diff --git a/src/pages/Customer/CustomerList.vue b/src/pages/Customer/CustomerList.vue index 0bfca7910..b721a6ad9 100644 --- a/src/pages/Customer/CustomerList.vue +++ b/src/pages/Customer/CustomerList.vue @@ -10,7 +10,6 @@ import CustomerFilter from './CustomerFilter.vue'; import VnTable from 'components/VnTable/VnTable.vue'; import VnLocation from 'src/components/common/VnLocation.vue'; import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue'; -import VnSelectWorker from 'src/components/common/VnSelectWorker.vue'; import VnSection from 'src/components/common/VnSection.vue'; const { t } = useI18n(); @@ -73,30 +72,17 @@ const columns = computed(() => [ }, { align: 'left', - name: 'salesPersonFk', - label: t('customer.extendedList.tableVisibleColumns.salesPersonFk'), + name: 'departmentFk', + label: t('customer.summary.team'), component: 'select', attrs: { - url: 'Workers/activeWithInheritedRole', - fields: ['id', 'name', 'firstName'], - where: { role: 'salesPerson' }, - optionFilter: 'firstName', + url: 'Departments', }, - columnFilter: { - component: 'select', - attrs: { - url: 'Workers/activeWithInheritedRole', - fields: ['id', 'name', 'firstName'], - where: { role: 'salesPerson' }, - optionLabel: 'firstName', - optionValue: 'id', - }, - }, - create: false, + create: true, columnField: { component: null, }, - format: (row, dashIfEmpty) => dashIfEmpty(row.salesPerson), + format: (row, dashIfEmpty) => dashIfEmpty(row.departmentName), }, { align: 'left', @@ -155,6 +141,9 @@ const columns = computed(() => [ inWhere: true, }, columnClass: 'expand', + attrs: { + uppercase: true, + }, }, { align: 'left', @@ -446,36 +435,6 @@ function handleLocation(data, location) { redirect="customer" > <template #more-create-dialog="{ data }"> - <VnSelectWorker - :label="t('customer.summary.salesPerson')" - v-model="data.salesPersonFk" - :params="{ - departmentCodes: ['VT', 'shopping'], - }" - :has-avatar="true" - :id-value="data.salesPersonFk" - emit-value - auto-load - > - <template #prepend> - <VnAvatar - :worker-id="data.salesPersonFk" - color="primary" - :title="title" - /> - </template> - <template #option="scope"> - <QItem v-bind="scope.itemProps"> - <QItemSection> - <QItemLabel>{{ scope.opt?.name }}</QItemLabel> - <QItemLabel caption - >{{ scope.opt?.nickname }}, - {{ scope.opt?.code }}</QItemLabel - > - </QItemSection> - </QItem> - </template> - </VnSelectWorker> <VnLocation :acls="[{ model: 'Province', props: '*', accessType: 'WRITE' }]" v-model="data.location" diff --git a/src/pages/Customer/Defaulter/CustomerDefaulter.vue b/src/pages/Customer/Defaulter/CustomerDefaulter.vue index dc4ac9162..296ad1eb4 100644 --- a/src/pages/Customer/Defaulter/CustomerDefaulter.vue +++ b/src/pages/Customer/Defaulter/CustomerDefaulter.vue @@ -32,28 +32,6 @@ const columns = computed(() => [ }, }, }, - { - align: 'left', - name: 'isWorker', - label: t('Is worker'), - }, - { - align: 'left', - name: 'salesPersonFk', - label: t('Salesperson'), - columnFilter: { - component: 'select', - attrs: { - url: 'Workers/activeWithInheritedRole', - fields: ['id', 'name'], - where: { role: 'salesPerson' }, - useLike: false, - optionValue: 'id', - optionLabel: 'name', - optionFilter: 'firstName', - }, - }, - }, { align: 'left', name: 'departmentFk', @@ -153,6 +131,11 @@ const columns = computed(() => [ label: t('Has recovery'), name: 'hasRecovery', }, + { + align: 'left', + name: 'isWorker', + label: t('customer.params.isWorker'), + }, ]); const viewAddObservation = (rowsSelected) => { @@ -167,7 +150,6 @@ const viewAddObservation = (rowsSelected) => { function exprBuilder(param, value) { switch (param) { - case 'salesPersonFk': case 'creditInsurance': case 'countryFk': return { [`c.${param}`]: value }; @@ -176,7 +158,7 @@ function exprBuilder(param, value) { case 'workerFk': return { [`co.${param}`]: value }; case 'departmentFk': - return { [`wd.${param}`]: value }; + return { [`c.${param}`]: value }; case 'amount': case 'clientFk': return { [`d.${param}`]: value }; @@ -241,12 +223,6 @@ function exprBuilder(param, value) { <template #column-observation="{ row }"> <VnInput type="textarea" v-model="row.observation" readonly dense rows="2" /> </template> - <template #column-salesPersonFk="{ row }"> - <span class="link" @click.stop> - {{ row.salesPersonName }} - <WorkerDescriptorProxy :id="row.salesPersonFk" /> - </span> - </template> <template #column-departmentFk="{ row }"> <span class="link" @click.stop> {{ row.departmentName }} @@ -265,8 +241,6 @@ function exprBuilder(param, value) { es: Add observation: Añadir observación Client: Cliente - Is worker: Es trabajador - Salesperson: Comercial Department: Departamento Country: País P. Method: F. Pago @@ -281,5 +255,5 @@ es: Credit I.: Crédito A. Credit insurance: Crédito asegurado From: Desde - Has recovery: Tiene recobro + Has recovery: Recobro </i18n> diff --git a/src/pages/Customer/Defaulter/CustomerDefaulterFilter.vue b/src/pages/Customer/Defaulter/CustomerDefaulterFilter.vue index ce86c6435..0eab7b7c5 100644 --- a/src/pages/Customer/Defaulter/CustomerDefaulterFilter.vue +++ b/src/pages/Customer/Defaulter/CustomerDefaulterFilter.vue @@ -15,19 +15,12 @@ const props = defineProps({ }, }); -const salespersons = ref(); const countries = ref(); const authors = ref(); const departments = ref(); </script> <template> - <FetchData - :filter="{ where: { role: 'salesPerson' } }" - @on-fetch="(data) => (salespersons = data)" - auto-load - url="Workers/activeWithInheritedRole" - /> <FetchData @on-fetch="(data) => (countries = data)" auto-load url="Countries" /> <FetchData @on-fetch="(data) => (authors = data)" @@ -62,29 +55,6 @@ const departments = ref(); @update:model-value="searchFn()" /> </QItem> - <QItem class="q-mb-sm"> - <QItemSection v-if="salespersons"> - <VnSelect - :input-debounce="0" - :label="t('Salesperson')" - :options="salespersons" - dense - emit-value - hide-selected - map-options - option-label="name" - option-value="id" - outlined - rounded - use-input - v-model="params.salesPersonFk" - @update:model-value="searchFn()" - /> - </QItemSection> - <QItemSection v-else> - <QSkeleton class="full-width" type="QInput" /> - </QItemSection> - </QItem> <QItem class="q-mb-sm"> <QItemSection v-if="departments"> <VnSelect @@ -219,7 +189,6 @@ const departments = ref(); en: params: clientFk: Client - salesPersonFk: Salesperson countryFk: Country paymentMethod: P. Method balance: Balance D. @@ -230,7 +199,6 @@ en: es: params: clientFk: Cliente - salesPersonFk: Comercial countryFk: País paymentMethod: F. Pago balance: Saldo V. @@ -239,7 +207,6 @@ es: credit: Crédito A. defaulterSinced: Desde Client: Cliente - Salesperson: Comercial Departments: Departamentos Country: País P. Method: F. Pago diff --git a/src/pages/Customer/Notifications/CustomerNotifications.vue b/src/pages/Customer/Notifications/CustomerNotifications.vue index ce18739b4..b30ed6f76 100644 --- a/src/pages/Customer/Notifications/CustomerNotifications.vue +++ b/src/pages/Customer/Notifications/CustomerNotifications.vue @@ -69,17 +69,16 @@ const columns = computed(() => [ }, { align: 'left', - label: t('customer.extendedList.tableVisibleColumns.salesPersonFk'), - name: 'salesPersonFk', + name: 'departmentFk', + label: t('customer.summary.team'), component: 'select', attrs: { - url: 'Workers/activeWithInheritedRole', - fields: ['id', 'name'], - where: { role: 'salesPerson' }, - optionFilter: 'firstName', - useLike: false, + url: 'Departments', }, - visible: false, + columnField: { + component: null, + }, + format: (row, dashIfEmpty) => dashIfEmpty(row.departmentName), }, ]); </script> @@ -96,7 +95,7 @@ const columns = computed(() => [ </VnSubToolbar> <VnTable :data-key="dataKey" - url="Clients" + url="Clients/filter" :table="{ 'row-key': 'id', selection: 'multiple', @@ -127,7 +126,6 @@ const columns = computed(() => [ es: Identifier: Identificador Social name: Razón social - Salesperson: Comercial Phone: Teléfono City: Población Email: Email diff --git a/src/pages/Customer/locale/en.yml b/src/pages/Customer/locale/en.yml index b6d495335..6724a5a7b 100644 --- a/src/pages/Customer/locale/en.yml +++ b/src/pages/Customer/locale/en.yml @@ -20,7 +20,7 @@ customer: name: Name contact: Contact mobile: Mobile - salesPerson: Sales person + team: Team contactChannel: Contact channel socialName: Social name fiscalId: Fiscal ID @@ -78,7 +78,6 @@ customer: id: Identifier socialName: Social name fi: Tax number - salesPersonFk: Salesperson creditInsurance: Credit insurance phone: Phone street: Street diff --git a/src/pages/Customer/locale/es.yml b/src/pages/Customer/locale/es.yml index f50d049da..4a266e07a 100644 --- a/src/pages/Customer/locale/es.yml +++ b/src/pages/Customer/locale/es.yml @@ -20,7 +20,7 @@ customer: name: Nombre contact: Contacto mobile: Móvil - salesPerson: Comercial + team: Equipo contactChannel: Canal de contacto socialName: Razón social fiscalId: NIF/CIF @@ -78,7 +78,6 @@ customer: id: Identificador socialName: Razón social fi: NIF / CIF - salesPersonFk: Comercial creditInsurance: Crédito asegurado phone: Teléfono street: Dirección fiscal diff --git a/src/pages/InvoiceOut/Card/InvoiceOutDescriptor.vue b/src/pages/InvoiceOut/Card/InvoiceOutDescriptor.vue index 9b5215986..2402c0bf6 100644 --- a/src/pages/InvoiceOut/Card/InvoiceOutDescriptor.vue +++ b/src/pages/InvoiceOut/Card/InvoiceOutDescriptor.vue @@ -46,6 +46,11 @@ function ticketFilter(invoice) { <InvoiceOutDescriptorMenu :invoice-out-data="entity" :menu-ref="menuRef" /> </template> <template #body="{ entity }"> + <VnLv + v-if="entity.externalRef" + :label="t('invoiceOut.externalRef')" + :value="entity.externalRef" + /> <VnLv :label="t('invoiceOut.card.issued')" :value="toDate(entity.issued)" /> <VnLv :label="t('globals.amount')" :value="toCurrency(entity.amount)" /> <VnLv v-if="entity.client" :label="t('globals.client')"> diff --git a/src/pages/InvoiceOut/InvoiceOutFilter.vue b/src/pages/InvoiceOut/InvoiceOutFilter.vue index 648b8e4e6..99524e0d6 100644 --- a/src/pages/InvoiceOut/InvoiceOutFilter.vue +++ b/src/pages/InvoiceOut/InvoiceOutFilter.vue @@ -7,6 +7,7 @@ import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; import VnInput from 'src/components/common/VnInput.vue'; import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputNumber from 'src/components/common/VnInputNumber.vue'; +import VnSelect from 'src/components/common/VnSelect.vue'; const { t } = useI18n(); const props = defineProps({ @@ -30,7 +31,7 @@ const states = ref(); <QItem> <QItemSection> <VnInput - :label="t('Customer ID')" + :label="t('globals.params.clientFk')" v-model="params.clientFk" is-outlined /> @@ -38,13 +39,17 @@ const states = ref(); </QItem> <QItem> <QItemSection> - <VnInput v-model="params.fi" :label="t('FI')" is-outlined /> + <VnInput + v-model="params.fi" + :label="t('globals.params.fi')" + is-outlined + /> </QItemSection> </QItem> <QItem> <QItemSection> <VnInputNumber - :label="t('Amount')" + :label="t('globals.amount')" v-model="params.amount" is-outlined data-cy="InvoiceOutFilterAmountBtn" @@ -54,7 +59,7 @@ const states = ref(); <QItem> <QItemSection> <QInput - :label="t('Min')" + :label="t('invoiceOut.params.min')" dense lazy-rules outlined @@ -65,7 +70,7 @@ const states = ref(); </QItemSection> <QItemSection> <QInput - :label="t('Max')" + :label="t('invoiceOut.params.max')" dense lazy-rules outlined @@ -78,7 +83,7 @@ const states = ref(); <QItem> <QItemSection> <QCheckbox - :label="t('Has PDF')" + :label="t('invoiceOut.params.hasPdf')" toggle-indeterminate v-model="params.hasPdf" /> @@ -88,14 +93,31 @@ const states = ref(); <QItemSection> <VnInputDate v-model="params.created" - :label="t('Created')" + :label="t('invoiceOut.params.created')" is-outlined /> </QItemSection> </QItem> <QItem> <QItemSection> - <VnInputDate v-model="params.dued" :label="t('Dued')" is-outlined /> + <VnInputDate + v-model="params.dued" + :label="t('invoiceOut.params.dued')" + is-outlined + /> + </QItemSection> + </QItem> + <QItem> + <QItemSection> + <VnSelect + outlined + rounded + :label="t('globals.params.departmentFk')" + v-model="params.departmentFk" + option-value="id" + option-label="name" + url="Departments" + /> </QItemSection> </QItem> </template> diff --git a/src/pages/InvoiceOut/InvoiceOutList.vue b/src/pages/InvoiceOut/InvoiceOutList.vue index 034f416ed..3390ef33a 100644 --- a/src/pages/InvoiceOut/InvoiceOutList.vue +++ b/src/pages/InvoiceOut/InvoiceOutList.vue @@ -8,7 +8,7 @@ import { useSummaryDialog } from 'src/composables/useSummaryDialog'; import { usePrintService } from 'src/composables/usePrintService'; import VnTable from 'src/components/VnTable/VnTable.vue'; import InvoiceOutSummary from './Card/InvoiceOutSummary.vue'; -import { toCurrency, toDate } from 'src/filters/index'; +import { toCurrency, toDate, dashIfEmpty } from 'src/filters/index'; import { QBtn } from 'quasar'; import axios from 'axios'; import InvoiceOutFilter from './InvoiceOutFilter.vue'; @@ -16,6 +16,7 @@ import VnRow from 'src/components/ui/VnRow.vue'; import VnRadio from 'src/components/common/VnRadio.vue'; import VnInput from 'src/components/common/VnInput.vue'; import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue'; +import DepartmentDescriptorProxy from '../Worker/Department/Card/DepartmentDescriptorProxy.vue'; import VnSection from 'src/components/common/VnSection.vue'; const { t } = useI18n(); @@ -54,6 +55,14 @@ const columns = computed(() => [ name: 'id', }, }, + { + align: 'left', + name: 'issued', + label: t('invoiceOut.summary.issued'), + component: 'date', + format: (row) => toDate(row.issued), + columnField: { component: null }, + }, { align: 'left', name: 'ref', @@ -86,6 +95,20 @@ const columns = computed(() => [ component: null, }, }, + { + align: 'left', + name: 'departmentFk', + label: t('customer.summary.team'), + cardVisible: true, + component: 'select', + attrs: { + url: 'Departments', + }, + columnField: { + component: null, + }, + format: (row, dashIfEmpty) => dashIfEmpty(row.departmentName), + }, { align: 'left', name: 'companyFk', @@ -229,6 +252,12 @@ watchEffect(selectedRows); <CustomerDescriptorProxy :id="row.clientFk" /> </span> </template> + <template #column-departmentFk="{ row }"> + <span class="link" @click.stop> + {{ dashIfEmpty(row.departmentName) }} + <DepartmentDescriptorProxy :id="row?.departmentFk" /> + </span> + </template> <template #more-create-dialog="{ data }"> <div class="row q-col-gutter-xs col-span-2"> <div class="col-12"> diff --git a/src/pages/InvoiceOut/InvoiceOutNegativeBases.vue b/src/pages/InvoiceOut/InvoiceOutNegativeBases.vue index b062678a0..432cd07d7 100644 --- a/src/pages/InvoiceOut/InvoiceOutNegativeBases.vue +++ b/src/pages/InvoiceOut/InvoiceOutNegativeBases.vue @@ -8,7 +8,7 @@ import { useInvoiceOutGlobalStore } from 'src/stores/invoiceOutGlobal.js'; import { useArrayData } from 'src/composables/useArrayData'; import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue'; import TicketDescriptorProxy from '../Ticket/Card/TicketDescriptorProxy.vue'; -import WorkerDescriptorProxy from '../Worker/Card/WorkerDescriptorProxy.vue'; +import DepartmentDescriptorProxy from '../Worker/Department/Card/DepartmentDescriptorProxy.vue'; import VnInputDate from 'components/common/VnInputDate.vue'; import InvoiceOutNegativeBasesFilter from './InvoiceOutNegativeBasesFilter.vue'; import RightMenu from 'src/components/common/RightMenu.vue'; @@ -115,18 +115,16 @@ const columns = computed(() => [ }, { align: 'left', - label: t('customer.extendedList.tableVisibleColumns.salesPersonFk'), - name: 'workerName', + name: 'departmentFk', + label: t('customer.summary.team'), component: 'select', attrs: { - url: 'Workers/activeWithInheritedRole', - fields: ['id', 'name'], - where: { role: 'salesPerson' }, + url: 'Departments', }, columnField: { component: null, }, - format: (row, dashIfEmpty) => dashIfEmpty(row.workerName), + format: (row, dashIfEmpty) => dashIfEmpty(row.departmentName), }, ]); @@ -198,10 +196,10 @@ const downloadCSV = async () => { <TicketDescriptorProxy :id="row.ticketFk" /> </span> </template> - <template #column-workerName="{ row }"> + <template #column-departmentFk="{ row }"> <span class="link" @click.stop> - {{ row.workerName }} - <WorkerDescriptorProxy :id="row.comercialId" /> + {{ row.departmentName }} + <DepartmentDescriptorProxy :id="row.departmentFk" /> </span> </template> <template #moreFilterPanel="{ params }"> diff --git a/src/pages/InvoiceOut/InvoiceOutNegativeBasesFilter.vue b/src/pages/InvoiceOut/InvoiceOutNegativeBasesFilter.vue index 579ab8871..b24c8b247 100644 --- a/src/pages/InvoiceOut/InvoiceOutNegativeBasesFilter.vue +++ b/src/pages/InvoiceOut/InvoiceOutNegativeBasesFilter.vue @@ -129,12 +129,15 @@ const props = defineProps({ </QItem> <QItem> <QItemSection> - <VnSelectWorker - :label="t('invoiceOut.negativeBases.comercial')" - v-model="params.workerName" - option-value="name" - is-outlined - @update:model-value="searchFn()" + <VnSelect + outlined + dense + rounded + :label="t('globals.params.departmentFk')" + v-model="params.departmentFk" + option-value="id" + option-label="name" + url="Departments" /> </QItemSection> </QItem> diff --git a/src/pages/InvoiceOut/locale/en.yml b/src/pages/InvoiceOut/locale/en.yml index 17d198351..9d6a4a244 100644 --- a/src/pages/InvoiceOut/locale/en.yml +++ b/src/pages/InvoiceOut/locale/en.yml @@ -1,6 +1,7 @@ invoiceOut: search: Search invoice searchInfo: You can search by invoice reference + externalRef: External Ref. params: id: ID company: Company @@ -12,7 +13,6 @@ invoiceOut: isActive: Active hasToInvoice: Has to invoice hasVerifiedData: Verified data - workerName: Worker isTaxDataChecked: Verified data amount: Amount clientFk: Client @@ -26,6 +26,7 @@ invoiceOut: max: Max hasPdf: Has PDF search: Contains + departmentFk: Department card: issued: Issued customerCard: Customer card diff --git a/src/pages/InvoiceOut/locale/es.yml b/src/pages/InvoiceOut/locale/es.yml index 3df95d6b2..f9448cd9b 100644 --- a/src/pages/InvoiceOut/locale/es.yml +++ b/src/pages/InvoiceOut/locale/es.yml @@ -1,6 +1,7 @@ invoiceOut: search: Buscar factura emitida searchInfo: Puedes buscar por referencia de la factura + externalRef: Ref. externa params: id: ID company: Empresa @@ -12,7 +13,6 @@ invoiceOut: isActive: Activo hasToInvoice: Debe facturar hasVerifiedData: Datos verificados - workerName: Comercial isTaxDataChecked: Datos comprobados amount: Importe clientFk: Cliente @@ -26,6 +26,7 @@ invoiceOut: max: Max hasPdf: Tiene PDF search: Contiene + departmentFk: Departamento card: issued: Fecha emisión customerCard: Ficha del cliente diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index 43fc611d8..ccae98025 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -3,6 +3,7 @@ import { ref, computed, onMounted } from 'vue'; import { useI18n } from 'vue-i18n'; import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; +import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue'; import { useStateStore } from 'stores/useStateStore'; import { toCurrency } from 'filters/index'; import useNotify from 'src/composables/useNotify.js'; @@ -61,6 +62,7 @@ const columns = computed(() => [ columnClass: 'expand', }, { + align: 'left', label: t('item.buyRequest.requester'), name: 'requesterName', component: 'select', @@ -77,6 +79,19 @@ const columns = computed(() => [ }, columnClass: 'shrink', }, + { + align: 'left', + name: 'departmentFk', + label: t('customer.summary.team'), + component: 'select', + attrs: { + url: 'Departments', + }, + columnField: { + component: null, + }, + format: (row, dashIfEmpty) => dashIfEmpty(row.departmentName), + }, { label: t('item.buyRequest.requested'), name: 'quantity', @@ -107,6 +122,7 @@ const columns = computed(() => [ }, columnClass: 'shrink', }, + { label: t('globals.item'), name: 'item', @@ -262,6 +278,12 @@ const onDenyAccept = (_, responseData) => { <WorkerDescriptorProxy :id="row.requesterFk" /> </span> </template> + <template #column-departmentFk="{ row }"> + <span class="link" @click.stop> + {{ row.departmentName }} + <DepartmentDescriptorProxy :id="row.departmentFk" /> + </span> + </template> <template #column-item="{ row }"> <span> diff --git a/src/pages/Item/ItemRequestFilter.vue b/src/pages/Item/ItemRequestFilter.vue index c2a63ddd9..a29203df3 100644 --- a/src/pages/Item/ItemRequestFilter.vue +++ b/src/pages/Item/ItemRequestFilter.vue @@ -221,7 +221,7 @@ en: attenderFk: Atender clientFk: Client id warehouseFk: Warehouse - requesterFk: Salesperson + requesterFk: Requester from: From to: To mine: For me @@ -239,7 +239,7 @@ es: attenderFk: Comprador clientFk: Id cliente warehouseFk: Almacén - requesterFk: Comercial + requesterFk: Solicitante from: Desde to: Hasta mine: Para mi diff --git a/src/pages/Item/ItemType/Card/ItemTypeSummary.vue b/src/pages/Item/ItemType/Card/ItemTypeSummary.vue index 3b63c4b63..ba294e144 100644 --- a/src/pages/Item/ItemType/Card/ItemTypeSummary.vue +++ b/src/pages/Item/ItemType/Card/ItemTypeSummary.vue @@ -7,6 +7,7 @@ import filter from './ItemTypeFilter.js'; import CardSummary from 'components/ui/CardSummary.vue'; import VnLv from 'src/components/ui/VnLv.vue'; import VnToSummary from 'src/components/ui/VnToSummary.vue'; +import VnTitle from 'src/components/common/VnTitle.vue'; onUpdated(() => summaryRef.value.fetch()); @@ -62,13 +63,10 @@ async function setItemTypeData(data) { </template> <template #body> <QCard class="vn-one"> - <router-link - :to="{ name: 'ItemTypeBasicData', params: { id: entityId } }" - class="header header-link" - > - {{ t('globals.summary.basicData') }} - <QIcon name="open_in_new" /> - </router-link> + <VnTitle + :url="`#/item/item-type/${entityId}/basic-data`" + :text="$t('globals.summary.basicData')" + /> <VnLv :label="t('itemType.summary.id')" :value="itemType.id" /> <VnLv :label="t('itemType.shared.code')" :value="itemType.code" /> <VnLv :label="t('itemType.shared.name')" :value="itemType.name" /> diff --git a/src/pages/Item/locale/en.yml b/src/pages/Item/locale/en.yml index 9d27fc96e..ff8df26d4 100644 --- a/src/pages/Item/locale/en.yml +++ b/src/pages/Item/locale/en.yml @@ -84,7 +84,7 @@ item: attenderFk: Atender clientFk: Client id warehouseFk: Warehouse - requesterFk: Salesperson + requesterFk: Requester from: From to: To mine: For me diff --git a/src/pages/Item/locale/es.yml b/src/pages/Item/locale/es.yml index 935f5160b..7b768d0cb 100644 --- a/src/pages/Item/locale/es.yml +++ b/src/pages/Item/locale/es.yml @@ -93,7 +93,7 @@ item: attenderFk: Comprador clientFk: Id cliente warehouseFk: Almacén - requesterFk: Comercial + requesterFk: Solicitante from: Desde to: Hasta mine: Para mi diff --git a/src/pages/Monitor/MonitorClients.vue b/src/pages/Monitor/MonitorClients.vue index c1958cdcb..278b0b26f 100644 --- a/src/pages/Monitor/MonitorClients.vue +++ b/src/pages/Monitor/MonitorClients.vue @@ -31,7 +31,7 @@ function exprBuilder(param, value) { switch (param) { case 'clientFk': return { [`c.id`]: value }; - case 'salesPersonFk': + case 'departmentFk': return { [`c.${param}`]: value }; } } @@ -62,25 +62,17 @@ const columns = computed(() => [ columnFilter: false, }, { - label: t('salesClientsTable.salesPerson'), - name: 'salesPersonFk', - field: 'salesPerson', align: 'left', + name: 'departmentFk', + label: t('customer.summary.team'), + component: 'select', + attrs: { + url: 'Departments', + }, columnField: { component: null, }, - optionFilter: 'firstName', - columnFilter: { - component: 'select', - attrs: { - url: 'Workers/activeWithInheritedRole', - fields: ['id', 'name'], - sortBy: 'nickname ASC', - where: { role: 'salesPerson' }, - useLike: false, - }, - }, - columnClass: 'no-padding', + format: (row, dashIfEmpty) => dashIfEmpty(row.departmentName), }, { label: t('salesClientsTable.client'), @@ -128,9 +120,9 @@ const columns = computed(() => [ <VnInputDate v-model="to" :label="$t('globals.to')" dense /> </VnRow> </template> - <template #column-salesPersonFk="{ row }"> - <span class="link" :title="row.salesPerson" v-text="row.salesPerson" /> - <WorkerDescriptorProxy :id="row.salesPersonFk" dense /> + <template #column-departmentFk="{ row }"> + <span class="link" :title="row.department" v-text="row.department" /> + <WorkerDescriptorProxy :id="row.departmentFk" dense /> </template> <template #column-clientFk="{ row }"> <span class="link" :title="row.clientName" v-text="row.clientName" /> diff --git a/src/pages/Monitor/MonitorOrders.vue b/src/pages/Monitor/MonitorOrders.vue index 873f8abb4..2679f7224 100644 --- a/src/pages/Monitor/MonitorOrders.vue +++ b/src/pages/Monitor/MonitorOrders.vue @@ -1,9 +1,9 @@ <script setup> import { ref, computed } from 'vue'; import { useI18n } from 'vue-i18n'; -import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue'; import VnTable from 'components/VnTable/VnTable.vue'; +import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue'; import { toDateFormat, toDateTimeFormat } from 'src/filters/date.js'; import { toCurrency } from 'src/filters'; @@ -20,8 +20,8 @@ function exprBuilder(param, value) { switch (param) { case 'clientFk': return { [`c.id`]: value }; - case 'salesPersonFk': - return { [`c.salesPersonFk`]: value }; + case 'departmentFk': + return { [`c.departmentFk`]: value }; } } @@ -63,20 +63,18 @@ const columns = computed(() => [ columnFilter: false, }, { - label: t('salesClientsTable.salesPerson'), - name: 'salesPersonFk', align: 'left', - optionFilter: 'firstName', - columnFilter: { - component: 'select', - attrs: { - url: 'Workers/activeWithInheritedRole', - fields: ['id', 'name'], - sortBy: 'nickname ASC', - where: { role: 'salesPerson' }, - useLike: false, - }, + name: 'departmentFk', + label: t('customer.summary.team'), + component: 'select', + attrs: { + url: 'Departments', }, + create: true, + columnField: { + component: null, + }, + format: (row, dashIfEmpty) => dashIfEmpty(row.departmentName), }, { label: t('salesOrdersTable.import'), @@ -184,11 +182,10 @@ const openTab = (id) => <CustomerDescriptorProxy :id="row.clientFk" /> </QTd> </template> - - <template #column-salesPersonFk="{ row }"> + <template #column-departmentFk="{ row }"> <QTd @click.stop> - <span class="link" v-text="row.salesPerson" /> - <WorkerDescriptorProxy :id="row.salesPersonFk" dense /> + <span class="link" v-text="row.departmentName" /> + <DepartmentDescriptorProxy :id="row.departmentFk" dense /> </QTd> </template> </VnTable> diff --git a/src/pages/Monitor/Ticket/MonitorTicketFilter.vue b/src/pages/Monitor/Ticket/MonitorTicketFilter.vue index 48710d696..447dd35b8 100644 --- a/src/pages/Monitor/Ticket/MonitorTicketFilter.vue +++ b/src/pages/Monitor/Ticket/MonitorTicketFilter.vue @@ -9,7 +9,6 @@ import VnInput from 'src/components/common/VnInput.vue'; import VnInputNumber from 'src/components/common/VnInputNumber.vue'; import FetchData from 'src/components/FetchData.vue'; import { dateRange } from 'src/filters'; -import VnSelectWorker from 'src/components/common/VnSelectWorker.vue'; defineProps({ dataKey: { type: String, required: true } }); const { t, te } = useI18n(); @@ -113,16 +112,16 @@ const getLocale = (label) => { </QItem> <QItem> <QItemSection> - <VnSelectWorker + <VnSelect outlined dense rounded - :label="t('globals.params.salesPersonFk')" - v-model="params.salesPersonFk" - :params="{ departmentCodes: ['VT'] }" - :no-one="true" - > - </VnSelectWorker> + :label="t('globals.params.departmentFk')" + v-model="params.departmentFk" + option-value="id" + option-label="name" + url="Departments" + /> </QItemSection> </QItem> <QItem> diff --git a/src/pages/Monitor/Ticket/MonitorTickets.vue b/src/pages/Monitor/Ticket/MonitorTickets.vue index 782175cd6..03d751595 100644 --- a/src/pages/Monitor/Ticket/MonitorTickets.vue +++ b/src/pages/Monitor/Ticket/MonitorTickets.vue @@ -2,7 +2,7 @@ import { ref, computed, onMounted } from 'vue'; import { useI18n } from 'vue-i18n'; import FetchData from 'components/FetchData.vue'; -import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; +import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue'; import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue'; import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue'; import InvoiceOutDescriptorProxy from 'src/pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue'; @@ -49,8 +49,8 @@ function exprBuilder(param, value) { switch (param) { case 'stateFk': return { 'ts.stateFk': value }; - case 'salesPersonFk': - return { 'c.salesPersonFk': !value ? null : value }; + case 'departmentFk': + return { 'c.departmentFk': !value ? null : value }; case 'provinceFk': return { 'a.provinceFk': value }; case 'theoreticalHour': @@ -108,19 +108,18 @@ const columns = computed(() => [ }, }, { - label: t('salesClientsTable.salesPerson'), - name: 'salesPersonFk', - field: 'userName', align: 'left', - columnFilter: { - component: 'select', - attrs: { - url: 'Workers/search?departmentCodes=["VT"]', - fields: ['id', 'name', 'nickname', 'code'], - sortBy: 'nickname ASC', - optionLabel: 'nickname', - }, + name: 'departmentFk', + label: t('customer.summary.team'), + component: 'select', + attrs: { + url: 'Departments', }, + create: true, + columnField: { + component: null, + }, + format: (row, dashIfEmpty) => dashIfEmpty(row.departmentName), }, { label: t('salesClientsTable.date'), @@ -437,10 +436,10 @@ const openTab = (id) => useOpenURL(`#/ticket/${id}/sale`); <CustomerDescriptorProxy :id="row.clientFk" /> </div> </template> - <template #column-salesPersonFk="{ row }"> - <div @click.stop :title="row.userName"> - <span class="link" v-text="dashIfEmpty(row.userName)" /> - <WorkerDescriptorProxy :id="row.salesPersonFk" /> + <template #column-departmentFk="{ row }"> + <div @click.stop :title="row.departmentName"> + <span class="link" v-text="dashIfEmpty(row.departmentName)" /> + <DepartmentDescriptorProxy :id="row.departmentFk" /> </div> </template> <template #column-shippedDate="{ row }"> diff --git a/src/pages/Monitor/locale/en.yml b/src/pages/Monitor/locale/en.yml index c049a5e53..a9ce36ffd 100644 --- a/src/pages/Monitor/locale/en.yml +++ b/src/pages/Monitor/locale/en.yml @@ -7,7 +7,6 @@ salesClientsTable: to: To date: Date hour: Hour - salesPerson: Salesperson client: Client salesOrdersTable: delete: Delete diff --git a/src/pages/Monitor/locale/es.yml b/src/pages/Monitor/locale/es.yml index a02d7f36f..6086eda6b 100644 --- a/src/pages/Monitor/locale/es.yml +++ b/src/pages/Monitor/locale/es.yml @@ -7,7 +7,6 @@ salesClientsTable: to: Hasta date: Fecha hour: Hora - salesPerson: Comercial client: Cliente salesOrdersTable: delete: Eliminar diff --git a/src/pages/Order/Card/OrderBasicData.vue b/src/pages/Order/Card/OrderBasicData.vue index 9c02d7494..73b8b6fc8 100644 --- a/src/pages/Order/Card/OrderBasicData.vue +++ b/src/pages/Order/Card/OrderBasicData.vue @@ -64,17 +64,7 @@ const orderFilter = { { relation: 'client', scope: { - fields: [ - 'salesPersonFk', - 'name', - 'isActive', - 'isFreezed', - 'isTaxDataChecked', - ], - include: { - relation: 'salesPersonUser', - scope: { fields: ['id', 'name'] }, - }, + fields: ['name', 'isActive', 'isFreezed', 'isTaxDataChecked'], }, }, ], @@ -167,7 +157,7 @@ const onClientChange = async (clientId) => { !data.isConfirmed && agencyList?.length && agencyList.some( - (agency) => agency.agencyModeFk === data.agency_id + (agency) => agency.agencyModeFk === data.agency_id, ) ? data.agencyModeFk : null diff --git a/src/pages/Order/Card/OrderDescriptor.vue b/src/pages/Order/Card/OrderDescriptor.vue index 0d18864dc..f34549c1e 100644 --- a/src/pages/Order/Card/OrderDescriptor.vue +++ b/src/pages/Order/Card/OrderDescriptor.vue @@ -8,7 +8,7 @@ import filter from './OrderFilter.js'; import CardDescriptor from 'components/ui/CardDescriptor.vue'; import VnLv from 'src/components/ui/VnLv.vue'; import FetchData from 'components/FetchData.vue'; -import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; +import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue'; const DEFAULT_ITEMS = 0; @@ -66,11 +66,11 @@ const total = ref(0); :label="t('globals.state')" :value="getConfirmationValue(entity.isConfirmed)" /> - <VnLv :label="t('order.field.salesPersonFk')"> + <VnLv :label="t('customer.summary.team')"> <template #value> <span class="link"> - {{ entity?.client?.salesPersonUser?.name || '-' }} - <WorkerDescriptorProxy :id="entity?.client?.salesPersonFk" /> + {{ entity?.client?.department?.name || '-' }} + <DepartmentDescriptorProxy :id="entity?.client?.departmentFk" /> </span> </template> </VnLv> diff --git a/src/pages/Order/Card/OrderFilter.js b/src/pages/Order/Card/OrderFilter.js index 3e521b92c..d45578529 100644 --- a/src/pages/Order/Card/OrderFilter.js +++ b/src/pages/Order/Card/OrderFilter.js @@ -10,14 +10,14 @@ export default { relation: 'client', scope: { fields: [ - 'salesPersonFk', + 'departmentFk', 'name', 'isActive', 'isFreezed', 'isTaxDataChecked', ], include: { - relation: 'salesPersonUser', + relation: 'department', scope: { fields: ['id', 'name'] }, }, }, diff --git a/src/pages/Order/Card/OrderFilter.vue b/src/pages/Order/Card/OrderFilter.vue index c387be241..42578423f 100644 --- a/src/pages/Order/Card/OrderFilter.vue +++ b/src/pages/Order/Card/OrderFilter.vue @@ -6,7 +6,6 @@ import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; import VnSelect from 'components/common/VnSelect.vue'; import VnInputDate from 'components/common/VnInputDate.vue'; import VnInput from 'components/common/VnInput.vue'; -import VnSelectWorker from 'src/components/common/VnSelectWorker.vue'; const { t } = useI18n(); const props = defineProps({ @@ -62,15 +61,15 @@ const sourceList = ref([]); outlined rounded /> - <VnSelectWorker - :label="t('globals.salesPerson')" - v-model="params.workerFk" - :params="{ - departmentCodes: ['VT'], - }" - dense + <VnSelect outlined + dense rounded + :label="t('globals.params.departmentFk')" + v-model="params.departmentFk" + option-value="id" + option-label="name" + url="Departments" /> <VnInputDate v-model="params.from" @@ -125,7 +124,6 @@ en: search: Includes clientFk: Client agencyModeFk: Agency - salesPersonFk: Sales Person from: From to: To orderFk: Order @@ -136,7 +134,6 @@ en: showEmpty: Show Empty customerId: Customer ID agency: Agency - salesPerson: Sales Person fromLanded: From Landed toLanded: To Landed orderId: Order ID @@ -149,7 +146,6 @@ es: search: Búsqueda clientFk: Cliente agencyModeFk: Agencia - salesPersonFk: Comercial from: Desde to: Hasta orderFk: Cesta @@ -160,7 +156,6 @@ es: showEmpty: Mostrar vacías customerId: ID Cliente agency: Agencia - salesPerson: Comercial fromLanded: Desde F. entrega toLanded: Hasta F. entrega orderId: ID Cesta diff --git a/src/pages/Order/OrderList.vue b/src/pages/Order/OrderList.vue index 2a1997f21..d75390d96 100644 --- a/src/pages/Order/OrderList.vue +++ b/src/pages/Order/OrderList.vue @@ -10,12 +10,12 @@ import axios from 'axios'; import OrderSummary from 'pages/Order/Card/OrderSummary.vue'; import OrderFilter from './Card/OrderFilter.vue'; import CustomerDescriptorProxy from '../Customer/Card/CustomerDescriptorProxy.vue'; -import WorkerDescriptorProxy from '../Worker/Card/WorkerDescriptorProxy.vue'; import VnTable from 'src/components/VnTable/VnTable.vue'; import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnSelect from 'src/components/common/VnSelect.vue'; import VnSection from 'src/components/common/VnSection.vue'; +import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue'; import { getAddresses } from '../Customer/composables/getAddresses'; const { t } = useI18n(); @@ -59,22 +59,17 @@ const columns = computed(() => [ }, { align: 'left', - name: 'salesPersonFk', - label: t('module.salesPerson'), - columnFilter: { - component: 'select', - inWhere: true, - attrs: { - url: 'Workers/activeWithInheritedRole', - fields: ['id', 'name'], - where: { role: 'salesPerson' }, - useLike: false, - optionValue: 'id', - optionLabel: 'name', - optionFilter: 'firstName', - }, + name: 'departmentFk', + label: t('customer.summary.team'), + component: 'select', + attrs: { + url: 'Departments', }, - format: (row) => row?.name, + create: true, + columnField: { + component: null, + }, + format: (row, dashIfEmpty) => dashIfEmpty(row.departmentName), }, { align: 'center', @@ -264,10 +259,10 @@ const isDefaultAddress = (opt, data) => { <CustomerDescriptorProxy :id="row?.clientFk" /> </span> </template> - <template #column-salesPersonFk="{ row }"> + <template #column-departmentFk="{ row }"> <span class="link" @click.stop> - {{ row?.name }} - <WorkerDescriptorProxy :id="row?.salesPersonFk" /> + {{ row?.departmentName }} + <DepartmentDescriptorProxy :id="row?.departmentFk" /> </span> </template> <template #column-landed="{ row }"> diff --git a/src/pages/Order/locale/en.yml b/src/pages/Order/locale/en.yml index 14e41c559..877a3c380 100644 --- a/src/pages/Order/locale/en.yml +++ b/src/pages/Order/locale/en.yml @@ -8,7 +8,6 @@ module: hour: Hour agency: Agency total: Total - salesPerson: Sales Person address: Address cerateOrder: Create order lines: @@ -22,8 +21,6 @@ lines: params: tagGroups: Tags order: - field: - salesPersonFk: Sales Person form: clientFk: Client addressFk: Address diff --git a/src/pages/Order/locale/es.yml b/src/pages/Order/locale/es.yml index 44e243ad1..f7528ec28 100644 --- a/src/pages/Order/locale/es.yml +++ b/src/pages/Order/locale/es.yml @@ -8,7 +8,6 @@ module: hour: Hora agency: Agencia total: Total - salesPerson: Comercial address: Dirección cerateOrder: Crear cesta lines: @@ -22,8 +21,6 @@ lines: params: tagGroups: Tags order: - field: - salesPersonFk: Comercial form: clientFk: Cliente addressFk: Dirección diff --git a/src/pages/Route/Agency/AgencyList.vue b/src/pages/Route/Agency/AgencyList.vue index 5c2904bf3..c01dd272c 100644 --- a/src/pages/Route/Agency/AgencyList.vue +++ b/src/pages/Route/Agency/AgencyList.vue @@ -2,10 +2,13 @@ import { computed } from 'vue'; import { useRouter } from 'vue-router'; import { useI18n } from 'vue-i18n'; +import { useSummaryDialog } from 'src/composables/useSummaryDialog'; import VnTable from 'components/VnTable/VnTable.vue'; import VnSection from 'src/components/common/VnSection.vue'; +import AgencySummary from 'pages/Route/Agency/Card/AgencySummary.vue'; const { t } = useI18n(); +const { viewSummary } = useSummaryDialog(); const router = useRouter(); const dataKey = 'AgencyList'; function navigate(id) { @@ -40,16 +43,22 @@ const columns = computed(() => [ }, { align: 'left', - label: t('isOwn'), + label: t('agency.isOwn'), name: 'isOwn', component: 'checkbox', + columnFilter: { + inWhere: true, + }, cardVisible: true, }, { align: 'left', - label: t('isAnyVolumeAllowed'), + label: t('agency.isAnyVolumeAllowed'), name: 'isAnyVolumeAllowed', component: 'checkbox', + columnFilter: { + inWhere: true, + }, cardVisible: true, }, { @@ -58,9 +67,10 @@ const columns = computed(() => [ name: 'tableActions', actions: [ { - title: t('Client ticket list'), + title: t('globals.pageTitles.summary'), icon: 'preview', - action: (row) => navigate(row.id), + action: (row) => viewSummary(row?.id, AgencySummary), + isPrimary: true, }, ], }, @@ -82,7 +92,7 @@ const columns = computed(() => [ <VnTable :data-key :columns="columns" - is-editable="false" + :is-editable="false" :right-search="false" :use-model="true" redirect="route/agency" @@ -103,11 +113,3 @@ const columns = computed(() => [ justify-content: center; } </style> -<i18n> - es: - isOwn: Tiene propietario - isAnyVolumeAllowed: Permite cualquier volumen - en: - isOwn: Has owner - isAnyVolumeAllowed: Allows any volume -</i18n> diff --git a/src/pages/Route/Agency/Card/AgencyBasicData.vue b/src/pages/Route/Agency/Card/AgencyBasicData.vue index 4270b136c..4f8f17163 100644 --- a/src/pages/Route/Agency/Card/AgencyBasicData.vue +++ b/src/pages/Route/Agency/Card/AgencyBasicData.vue @@ -21,7 +21,7 @@ const warehouses = ref([]); @on-fetch="(data) => (warehouses = data)" auto-load /> - <FormModel :update-url="`Agencies/${routeId}`" model="Agency" auto-load> + <FormModel :url-update="`Agencies/${routeId}`" model="Agency" auto-load> <template #form="{ data }"> <VnRow> <VnInput v-model="data.name" :label="t('globals.name')" /> diff --git a/src/pages/Route/Agency/Card/AgencyDescriptorProxy.vue b/src/pages/Route/Agency/Card/AgencyDescriptorProxy.vue new file mode 100644 index 000000000..e5c1249b2 --- /dev/null +++ b/src/pages/Route/Agency/Card/AgencyDescriptorProxy.vue @@ -0,0 +1,20 @@ +<script setup> +import AgencyDescriptor from 'pages/Route/Agency/Card/AgencyDescriptor.vue'; +import AgencySummary from './AgencySummary.vue'; + +const $props = defineProps({ + id: { + type: Number, + required: true, + }, + summary: { + type: Object, + default: null, + }, +}); +</script> +<template> + <QPopupProxy> + <AgencyDescriptor v-if="$props.id" :id="$props.id" :summary="AgencySummary" /> + </QPopupProxy> +</template> diff --git a/src/pages/Route/Agency/Card/AgencySummary.vue b/src/pages/Route/Agency/Card/AgencySummary.vue index 71a6d1066..ab274939a 100644 --- a/src/pages/Route/Agency/Card/AgencySummary.vue +++ b/src/pages/Route/Agency/Card/AgencySummary.vue @@ -6,29 +6,31 @@ import { useI18n } from 'vue-i18n'; import CardSummary from 'components/ui/CardSummary.vue'; import VnLv from 'components/ui/VnLv.vue'; import VnTitle from 'src/components/common/VnTitle.vue'; +import VnCheckbox from 'components/common/VnCheckbox.vue'; +const route = useRoute(); const $props = defineProps({ id: { type: Number, default: 0 } }); const { t } = useI18n(); -const entityId = computed(() => $props.id || useRoute().params.id); +const entityId = computed(() => $props.id || route.params.id); </script> <template> <div class="q-pa-md"> - <CardSummary :url="`Agencies/${entityId}`" data-key="Agency"> + <CardSummary :url="`Agencies/${entityId}`" data-key="Agency" module-name="Agency"> <template #header="{ entity: agency }">{{ agency.name }}</template> <template #body="{ entity: agency }"> <QCard class="vn-one"> <VnTitle - :url="`#/agency/${entityId}/basic-data`" + :url="`#/${route.meta.moduleName.toLowerCase()}/agency/${entityId}/basic-data`" :text="t('globals.pageTitles.basicData')" /> <VnLv :label="t('globals.name')" :value="agency.name" /> - <QCheckbox + <VnCheckbox :label="t('agency.isOwn')" v-model="agency.isOwn" :disable="true" /> - <QCheckbox + <VnCheckbox :label="t('agency.isAnyVolumeAllowed')" v-model="agency.isAnyVolumeAllowed" :disable="true" diff --git a/src/pages/Route/Agency/Card/AgencyWorkcenter.vue b/src/pages/Route/Agency/Card/AgencyWorkcenter.vue index 9a9213868..d33c9f753 100644 --- a/src/pages/Route/Agency/Card/AgencyWorkcenter.vue +++ b/src/pages/Route/Agency/Card/AgencyWorkcenter.vue @@ -80,6 +80,7 @@ async function deleteWorCenter(id) { color="primary" round flat + data-cy="removeWorkCenterBtn" /> </QItemSection> </QItem> diff --git a/src/pages/Route/Agency/locale/en.yml b/src/pages/Route/Agency/locale/en.yml index 93f8b4aaa..78a687f2e 100644 --- a/src/pages/Route/Agency/locale/en.yml +++ b/src/pages/Route/Agency/locale/en.yml @@ -1,11 +1,12 @@ agency: search: Search agency - searchInfo: You can search by name + searchInfo: You can search by name and by id isOwn: Own isAnyVolumeAllowed: Any volume allowed + removeItem: Agency removed successfully notification: - removeItemError: Error removing agency - removeItem: WorkCenter removed successfully + removeItemError: Error removing work center + removeItem: Work center removed successfully pageTitles: agency: Agency searchBar: diff --git a/src/pages/Route/Agency/locale/es.yml b/src/pages/Route/Agency/locale/es.yml index 1efed0e9c..b6237a9f7 100644 --- a/src/pages/Route/Agency/locale/es.yml +++ b/src/pages/Route/Agency/locale/es.yml @@ -1,15 +1,14 @@ agency: search: Buscar agencia - searchInfo: Puedes buscar por nombre + searchInfo: Puedes buscar por nombre y por id isOwn: Propio isAnyVolumeAllowed: Cualquier volumen removeItem: Agencia eliminada correctamente notification: - removeItemError: Error al eliminar la agencia + removeItemError: Error al eliminar la el centro de trabajo removeItem: Centro de trabajo eliminado correctamente pageTitles: agency: Agencia searchBar: info: Puedes buscar por nombre o id label: Buscar agencia... - diff --git a/src/pages/Route/Card/RouteDescriptorProxy.vue b/src/pages/Route/Card/RouteDescriptorProxy.vue index 1ff39a51e..7553469f3 100644 --- a/src/pages/Route/Card/RouteDescriptorProxy.vue +++ b/src/pages/Route/Card/RouteDescriptorProxy.vue @@ -7,6 +7,10 @@ const $props = defineProps({ type: Number, required: true, }, + summary: { + type: Object, + default: null, + }, }); </script> <template> diff --git a/src/pages/Route/Card/RouteFilter.vue b/src/pages/Route/Card/RouteFilter.vue index 21858102b..cb5158517 100644 --- a/src/pages/Route/Card/RouteFilter.vue +++ b/src/pages/Route/Card/RouteFilter.vue @@ -25,7 +25,7 @@ const emit = defineEmits(['search']); > <template #tags="{ tag, formatFn }"> <div class="q-gutter-x-xs"> - <strong>{{ t(`params.${tag.label}`) }}: </strong> + <strong>{{ t(`route.params.${tag.label}`) }}: </strong> <span>{{ formatFn(tag.value) }}</span> </div> </template> @@ -33,6 +33,7 @@ const emit = defineEmits(['search']); <QItem class="q-my-sm"> <QItemSection> <VnSelectWorker + :label="t('globals.worker')" v-model="params.workerFk" dense outlined @@ -44,7 +45,7 @@ const emit = defineEmits(['search']); <QItem class="q-my-sm"> <QItemSection> <VnSelect - :label="t('Agency')" + :label="t('globals.agency')" v-model="params.agencyModeFk" url="AgencyModes/isActive" sort-by="name ASC" @@ -61,7 +62,7 @@ const emit = defineEmits(['search']); <QItemSection> <VnInputDate v-model="params.from" - :label="t('From')" + :label="t('globals.from')" is-outlined :disable="Boolean(params.scopeDays)" @update:model-value="params.scopeDays = null" @@ -72,7 +73,7 @@ const emit = defineEmits(['search']); <QItemSection> <VnInputDate v-model="params.to" - :label="t('To')" + :label="t('globals.to')" is-outlined :disable="Boolean(params.scopeDays)" @update:model-value="params.scopeDays = null" @@ -84,7 +85,7 @@ const emit = defineEmits(['search']); <VnInput v-model="params.scopeDays" type="number" - :label="t('Days Onward')" + :label="t('globals.daysOnward')" is-outlined clearable :disable="Boolean(params.from || params.to)" @@ -98,7 +99,7 @@ const emit = defineEmits(['search']); <QItem class="q-my-sm"> <QItemSection> <VnSelect - :label="t('Vehicle')" + :label="t('globals.vehicle')" v-model="params.vehicleFk" url="Vehicles/active" sort-by="numberPlate ASC" @@ -120,7 +121,7 @@ const emit = defineEmits(['search']); <QItem class="q-my-sm"> <QItemSection> <VnSelect - :label="t('Warehouse')" + :label="t('globals.warehouse')" v-model="params.warehouseFk" url="Warehouses" option-value="id" @@ -136,7 +137,7 @@ const emit = defineEmits(['search']); <QItemSection> <VnInput v-model="params.description" - :label="t('Description')" + :label="t('globals.description')" is-outlined clearable /> @@ -146,7 +147,7 @@ const emit = defineEmits(['search']); <QItemSection> <QCheckbox v-model="params.isOk" - :label="t('Served')" + :label="t('route.filter.Served')" toggle-indeterminate /> </QItemSection> @@ -154,38 +155,3 @@ const emit = defineEmits(['search']); </template> </VnFilterPanel> </template> - -<i18n> -en: - params: - warehouseFk: Warehouse - description: Description - m3: m³ - scopeDays: Days Onward - vehicleFk: Vehicle - agencyModeFk: Agency - workerFk: Worker - from: From - to: To - Served: Served -es: - params: - warehouseFk: Almacén - description: Descripción - m3: m³ - scopeDays: Días en adelante - vehicleFk: Vehículo - agencyModeFk: Agencia - workerFk: Trabajador - from: Desde - to: Hasta - Warehouse: Almacén - Description: Descripción - Vehicle: Vehículo - Agency: Agencia - Worker: Trabajador - From: Desde - To: Hasta - Served: Servida - Days Onward: Días en adelante -</i18n> diff --git a/src/pages/Route/Card/RouteSummary.vue b/src/pages/Route/Card/RouteSummary.vue index 3051972b2..f68628095 100644 --- a/src/pages/Route/Card/RouteSummary.vue +++ b/src/pages/Route/Card/RouteSummary.vue @@ -135,7 +135,7 @@ const ticketColumns = ref([ <template #body="{ entity }"> <QCard class="vn-max"> <VnTitle - :url="`#/route/${entityId}/basic-data`" + :url="`#/${route.meta.moduleName.toLowerCase()}/${entityId}/basic-data`" :text="t('globals.pageTitles.basicData')" /> </QCard> @@ -168,7 +168,7 @@ const ticketColumns = ref([ <VnLv :label="t('route.summary.volume')" :value="`${dashIfEmpty(entity?.route?.m3)} / ${dashIfEmpty( - entity?.route?.vehicle?.m3 + entity?.route?.vehicle?.m3, )} m³`" /> <VnLv @@ -221,7 +221,7 @@ const ticketColumns = ref([ <template #body-cell-city="{ value, row }"> <QTd auto-width> <span - class="link cursor-pointer" + class="link" @click="openBuscaman(entity?.route?.vehicleFk, [row])" > {{ value }} @@ -230,7 +230,7 @@ const ticketColumns = ref([ </template> <template #body-cell-client="{ value, row }"> <QTd auto-width> - <span class="link cursor-pointer"> + <span class="link"> {{ value }} <CustomerDescriptorProxy :id="row?.clientFk" /> </span> @@ -238,7 +238,7 @@ const ticketColumns = ref([ </template> <template #body-cell-ticket="{ value, row }"> <QTd auto-width class="text-center"> - <span class="link cursor-pointer"> + <span class="link"> {{ value }} <TicketDescriptorProxy :id="row?.id" /> </span> diff --git a/src/pages/Route/Cmr/CmrList.vue b/src/pages/Route/Cmr/CmrList.vue index b3eaf3b48..d0683e481 100644 --- a/src/pages/Route/Cmr/CmrList.vue +++ b/src/pages/Route/Cmr/CmrList.vue @@ -2,28 +2,38 @@ import { onBeforeMount, onMounted, computed, ref } from 'vue'; import { useI18n } from 'vue-i18n'; import { Notify } from 'quasar'; +import { useRoute } from 'vue-router'; import { useSession } from 'src/composables/useSession'; import { toDateHourMin } from 'filters/index'; import { useStateStore } from 'src/stores/useStateStore'; -import axios from 'axios'; import TicketDescriptorProxy from 'pages/Ticket/Card/TicketDescriptorProxy.vue'; import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue'; import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; import VnTable from 'components/VnTable/VnTable.vue'; +import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; +const route = useRoute(); const { t } = useI18n(); const { getTokenMultimedia } = useSession(); const token = getTokenMultimedia(); const state = useStateStore(); -const warehouses = ref([]); const selectedRows = ref([]); +const dataKey = 'CmrList'; +const shipped = Date.vnNew(); +shipped.setHours(0, 0, 0, 0); +shipped.setDate(shipped.getDate() - 1); +const userParams = { + shipped: null, +}; + + const columns = computed(() => [ { align: 'left', name: 'cmrFk', - label: t('route.cmr.list.cmrFk'), + label: t('route.cmr.params.cmrFk'), chip: { condition: () => true, }, @@ -32,62 +42,67 @@ const columns = computed(() => [ { align: 'center', name: 'hasCmrDms', - label: t('route.cmr.list.hasCmrDms'), + label: t('route.cmr.params.hasCmrDms'), component: 'checkbox', cardVisible: true, }, { align: 'left', - label: t('route.cmr.list.ticketFk'), + label: t('route.cmr.params.ticketFk'), name: 'ticketFk', }, { align: 'left', - label: t('route.cmr.list.routeFk'), + label: t('route.cmr.params.routeFk'), name: 'routeFk', }, { align: 'left', - label: t('route.cmr.list.clientFk'), + label: t('route.cmr.params.clientFk'), name: 'clientFk', }, { align: 'right', - label: t('route.cmr.list.country'), + label: t('route.cmr.params.countryFk'), name: 'countryFk', - cardVisible: true, + component: 'select', attrs: { url: 'countries', fields: ['id', 'name'], - optionLabel: 'name', - optionValue: 'id', }, columnFilter: { - inWhere: true, - component: 'select', + name: 'countryFk', + attrs: { + url: 'countries', + fields: ['id', 'name'], + }, }, format: ({ countryName }) => countryName, }, { align: 'right', - label: t('route.cmr.list.shipped'), + label: t('route.cmr.params.shipped'), name: 'shipped', cardVisible: true, - columnFilter: { - component: 'date', - inWhere: true, - }, + component: 'date', format: ({ shipped }) => toDateHourMin(shipped), }, { align: 'right', + label: t('route.cmr.params.warehouseFk'), name: 'warehouseFk', - label: t('globals.warehouse'), - columnFilter: { - component: 'select', - }, + component: 'select', attrs: { - options: warehouses.value, + url: 'warehouses', + fields: ['id', 'name'], + }, + columnFilter: { + inWhere: true, + name: 'warehouseFk', + attrs: { + url: 'warehouses', + fields: ['id', 'name'], + }, }, format: ({ warehouseName }) => warehouseName, }, @@ -96,7 +111,7 @@ const columns = computed(() => [ name: 'tableActions', actions: [ { - title: t('Ver cmr'), + title: t('route.cmr.params.viewCmr'), icon: 'visibility', isPrimary: true, action: (row) => window.open(getCmrUrl(row?.cmrFk), '_blank'), @@ -105,13 +120,17 @@ const columns = computed(() => [ }, ]); -onBeforeMount(async () => { - const { data } = await axios.get('Warehouses'); - warehouses.value = data; +onBeforeMount(() => { + initializeFromQuery(); }); onMounted(() => (state.rightDrawer = true)); +const initializeFromQuery = () => { + const query = route.query.table ? JSON.parse(route.query.table) : {}; + shipped.value = query.shipped || shipped.toISOString(); + Object.assign(userParams, { shipped }); +}; function getApiUrl() { return new URL(window.location).origin; } @@ -133,6 +152,11 @@ function downloadPdfs() { } </script> <template> + <VnSearchbar + :data-key + :label="t('route.cmr.search')" + :info="t('route.cmr.searchInfo')" + /> <VnSubToolbar> <template #st-actions> <QBtn @@ -142,16 +166,16 @@ function downloadPdfs() { :disable="!selectedRows?.length" @click="downloadPdfs" > - <QTooltip>{{ t('route.cmr.list.downloadCmrs') }}</QTooltip> + <QTooltip>{{ t('route.cmr.params.downloadCmrs') }}</QTooltip> </QBtn> </template> </VnSubToolbar> <VnTable ref="tableRef" - data-key="CmrList" + :data-key url="Cmrs/filter" :columns="columns" - :right-search="true" + :user-params="userParams" default-mode="table" v-model:selected="selectedRows" table-height="85vh" diff --git a/src/pages/Route/Roadmap/RoadmapBasicData.vue b/src/pages/Route/Roadmap/RoadmapBasicData.vue index a9e6059c3..3e9b8df6c 100644 --- a/src/pages/Route/Roadmap/RoadmapBasicData.vue +++ b/src/pages/Route/Roadmap/RoadmapBasicData.vue @@ -17,7 +17,7 @@ const onSave = (data, response) => { </script> <template> <FormModel - :update-url="`Roadmaps/${$route.params?.id}`" + :url-update="`Roadmaps/${$route.params?.id}`" :url="`Roadmaps/${$route.params?.id}`" observe-form-changes model="Roadmap" diff --git a/src/pages/Route/Roadmap/RoadmapDescriptor.vue b/src/pages/Route/Roadmap/RoadmapDescriptor.vue index baa864a15..198bcf8c7 100644 --- a/src/pages/Route/Roadmap/RoadmapDescriptor.vue +++ b/src/pages/Route/Roadmap/RoadmapDescriptor.vue @@ -15,6 +15,10 @@ const $props = defineProps({ required: false, default: null, }, + summary: { + type: Object, + default: null, + }, }); const route = useRoute(); @@ -26,7 +30,12 @@ const entityId = computed(() => { </script> <template> - <CardDescriptor :url="`Roadmaps/${entityId}`" :filter="filter" data-key="Roadmap"> + <CardDescriptor + :url="`Roadmaps/${entityId}`" + :filter="filter" + data-key="Roadmap" + :summary="summary" + > <template #body="{ entity }"> <VnLv :label="t('Roadmap')" :value="entity?.name" /> <VnLv :label="t('ETD')" :value="toDateHourMin(entity?.etd)" /> diff --git a/src/pages/Route/RouteAutonomous.vue b/src/pages/Route/RouteAutonomous.vue index 3047cdf86..15db2a55f 100644 --- a/src/pages/Route/RouteAutonomous.vue +++ b/src/pages/Route/RouteAutonomous.vue @@ -13,6 +13,7 @@ import RouteSummary from 'pages/Route/Card/RouteSummary.vue'; import RouteDescriptorProxy from 'pages/Route/Card/RouteDescriptorProxy.vue'; import InvoiceInDescriptorProxy from 'pages/InvoiceIn/Card/InvoiceInDescriptorProxy.vue'; import SupplierDescriptorProxy from 'pages/Supplier/Card/SupplierDescriptorProxy.vue'; +import AgencyDescriptorProxy from 'pages/Route/Agency/Card/AgencyDescriptorProxy.vue'; import VnSearchbar from 'components/ui/VnSearchbar.vue'; import VnDms from 'components/common/VnDms.vue'; import VnTable from 'components/VnTable/VnTable.vue'; @@ -236,10 +237,16 @@ onUnmounted(() => (stateStore.rightDrawer = false)); selection: 'multiple', }" > - <template #column-id="{ row }"> + <template #column-agencyModeName="{ row }"> <span class="link" @click.stop> - {{ row.routeFk }} - <RouteDescriptorProxy :id="row.route.id" /> + {{ row?.agencyModeName }} + <AgencyDescriptorProxy :id="row?.agencyModeFk" v-if="row?.agencyModeFk" /> + </span> + </template> + <template #column-agencyAgreement="{ row }"> + <span class="link" @click.stop> + {{ row?.agencyAgreement }} + <AgencyDescriptorProxy :id="row?.agencyFk" v-if="row?.agencyFk" /> </span> </template> <template #column-invoiceInFk="{ row }"> diff --git a/src/pages/Route/RouteExtendedList.vue b/src/pages/Route/RouteExtendedList.vue index a7e192765..fb19323c9 100644 --- a/src/pages/Route/RouteExtendedList.vue +++ b/src/pages/Route/RouteExtendedList.vue @@ -38,7 +38,7 @@ const routeFilter = { }; const columns = computed(() => [ { - align: 'center', + align: 'right', name: 'id', label: 'Id', chip: { @@ -46,11 +46,11 @@ const columns = computed(() => [ }, isId: true, columnFilter: false, + width: '25px', }, { - align: 'center', name: 'workerFk', - label: t('route.Worker'), + label: t('globals.worker'), create: true, component: 'select', attrs: { @@ -71,9 +71,8 @@ const columns = computed(() => [ format: (row, dashIfEmpty) => dashIfEmpty(row.workerUserName), }, { - align: 'center', name: 'agencyModeFk', - label: t('route.Agency'), + label: t('globals.agency'), isTitle: true, cardVisible: true, create: true, @@ -90,9 +89,8 @@ const columns = computed(() => [ format: (row, dashIfEmpty) => dashIfEmpty(row.agencyName), }, { - align: 'center', name: 'vehicleFk', - label: t('route.Vehicle'), + label: t('globals.vehicle'), cardVisible: true, create: true, component: 'select', @@ -111,9 +109,8 @@ const columns = computed(() => [ format: (row, dashIfEmpty) => dashIfEmpty(row.vehiclePlateNumber), }, { - align: 'center', name: 'dated', - label: t('route.Date'), + label: t('globals.date'), columnFilter: false, cardVisible: true, create: true, @@ -122,9 +119,8 @@ const columns = computed(() => [ dated === '0000-00-00' ? dashIfEmpty(null) : toDate(dated), }, { - align: 'center', name: 'from', - label: t('route.From'), + label: t('globals.from'), visible: false, cardVisible: true, create: true, @@ -132,9 +128,8 @@ const columns = computed(() => [ format: ({ from }) => toDate(from), }, { - align: 'center', name: 'to', - label: t('route.To'), + label: t('globals.to'), visible: false, cardVisible: true, create: true, @@ -142,30 +137,31 @@ const columns = computed(() => [ format: ({ date }) => toDate(date), }, { - align: 'center', + align: 'right', name: 'm3', label: 'm3', cardVisible: true, columnClass: 'shrink', + width: '50px', }, { - align: 'center', name: 'started', label: t('route.hourStarted'), component: 'time', columnFilter: false, format: ({ started }) => toHour(started), + width: '50px', }, { - align: 'center', name: 'finished', label: t('route.hourFinished'), component: 'time', columnFilter: false, format: ({ finished }) => toHour(finished), + width: '50px', }, { - align: 'center', + align: 'right', name: 'kmStart', label: t('route.KmStart'), columnClass: 'shrink', @@ -173,7 +169,7 @@ const columns = computed(() => [ visible: false, }, { - align: 'center', + align: 'right', name: 'kmEnd', label: t('route.KmEnd'), columnClass: 'shrink', @@ -181,16 +177,15 @@ const columns = computed(() => [ visible: false, }, { - align: 'center', + align: 'left', name: 'description', - label: t('route.Description'), + label: t('globals.description'), isTitle: true, create: true, component: 'input', field: 'description', }, { - align: 'center', name: 'isOk', label: t('route.Served'), component: 'checkbox', @@ -202,7 +197,7 @@ const columns = computed(() => [ name: 'tableActions', actions: [ { - title: t('route.Add tickets'), + title: t('route.addTicket'), icon: 'vn:ticketAdd', action: (row) => openTicketsDialog(row?.id), isPrimary: true, @@ -214,7 +209,7 @@ const columns = computed(() => [ isPrimary: true, }, { - title: t('route.Route summary'), + title: t('route.routeSummary'), icon: 'arrow_forward', action: (row) => navigate(row?.id), isPrimary: true, @@ -276,11 +271,13 @@ const openTicketsDialog = (id) => { <QDialog v-model="confirmationDialog"> <QCard style="min-width: 350px"> <QCardSection> - <p class="text-h6 q-ma-none">{{ t('route.Select the starting date') }}</p> + <p class="text-h6 q-ma-none"> + {{ t('route.extendedList.selectStartingDate') }} + </p> </QCardSection> <QCardSection class="q-pt-none"> <VnInputDate - :label="t('route.Starting date')" + :label="t('route.extendedList.startingDate')" v-model="startingDate" autofocus /> @@ -288,7 +285,7 @@ const openTicketsDialog = (id) => { <QCardActions align="right"> <QBtn flat - :label="t('route.Cancel')" + :label="t('globals.cancel')" v-close-popup class="text-primary" /> @@ -339,7 +336,7 @@ const openTicketsDialog = (id) => { :disable="!selectedRows?.length" @click="confirmationDialog = true" > - <QTooltip>{{ t('route.Clone Selected Routes') }}</QTooltip> + <QTooltip>{{ t('route.extendedList.cloneSelectedRoutes') }}</QTooltip> </QBtn> <QBtn icon="cloud_download" @@ -348,7 +345,9 @@ const openTicketsDialog = (id) => { :disable="!selectedRows?.length" @click="showRouteReport" > - <QTooltip>{{ t('route.Download selected routes as PDF') }}</QTooltip> + <QTooltip>{{ + t('route.extendedList.downloadSelectedRoutes') + }}</QTooltip> </QBtn> <QBtn icon="check" @@ -357,7 +356,7 @@ const openTicketsDialog = (id) => { :disable="!selectedRows?.length" @click="markAsServed()" > - <QTooltip>{{ t('route.Mark as served') }}</QTooltip> + <QTooltip>{{ t('route.extendedList.markServed') }}</QTooltip> </QBtn> </template> </VnTable> diff --git a/src/pages/Route/RouteList.vue b/src/pages/Route/RouteList.vue index 5723e2f0d..7fc1027ea 100644 --- a/src/pages/Route/RouteList.vue +++ b/src/pages/Route/RouteList.vue @@ -7,8 +7,11 @@ import RouteSummary from 'pages/Route/Card/RouteSummary.vue'; import RouteFilter from 'pages/Route/Card/RouteFilter.vue'; import VnTable from 'components/VnTable/VnTable.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; +import AgencyDescriptorProxy from 'src/pages/Route/Agency/Card/AgencyDescriptorProxy.vue'; +import VehicleDescriptorProxy from 'src/pages/Route/Vehicle/Card/VehicleDescriptorProxy.vue'; import VnSection from 'src/components/common/VnSection.vue'; import VnSelectWorker from 'src/components/common/VnSelectWorker.vue'; +import RouteTickets from './RouteTickets.vue'; const { t } = useI18n(); const { viewSummary } = useSummaryDialog(); @@ -24,6 +27,12 @@ const routeFilter = { }, ], }; + +function redirectToTickets(id) { + const url = `#/route/${id}/tickets`; + window.open(url, '_blank'); +} + const columns = computed(() => [ { align: 'right', @@ -34,25 +43,29 @@ const columns = computed(() => [ condition: () => true, }, columnFilter: false, + width: '25px', }, { - align: 'left', name: 'workerFk', - label: t('route.Worker'), + label: t('gloabls.worker'), component: markRaw(VnSelectWorker), create: true, - cardVisible: true, format: (row, dashIfEmpty) => dashIfEmpty(row.travelRef), columnFilter: false, + width: '100px', }, { - align: 'left', - name: 'agencyName', - label: t('route.Agency'), + name: 'workerFk', + label: t('globals.worker'), + visible: false, cardVisible: true, }, { - label: t('route.Agency'), + name: 'agencyName', + label: t('globals.agency'), + }, + { + label: t('globals.Agency'), name: 'agencyModeFk', component: 'select', attrs: { @@ -68,14 +81,18 @@ const columns = computed(() => [ visible: false, }, { - align: 'left', - name: 'vehiclePlateNumber', - label: t('route.Vehicle'), + name: 'agencyName', + label: t('globals.agency'), + visible: false, cardVisible: true, }, + { + name: 'vehiclePlateNumber', + label: t('globals.vehicle'), + }, { name: 'vehicleFk', - label: t('route.Vehicle'), + label: t('globals.Vehicle'), cardVisible: true, component: 'select', attrs: { @@ -93,25 +110,27 @@ const columns = computed(() => [ visible: false, }, { - align: 'left', + align: 'center', name: 'started', label: t('route.hourStarted'), cardVisible: true, columnFilter: false, - format: (row) => toHour(row.started), + format: ({ started }) => toHour(started), + width: '50px', }, { - align: 'left', + align: 'center', name: 'finished', label: t('route.hourFinished'), cardVisible: true, columnFilter: false, - format: (row) => toHour(row.started), + format: ({ finished }) => toHour(finished), + width: '50px', }, { align: 'left', name: 'description', - label: t('route.Description'), + label: t('globals.description'), cardVisible: true, isTitle: true, create: true, @@ -119,7 +138,6 @@ const columns = computed(() => [ columnFilter: false, }, { - align: 'left', name: 'isOk', label: t('route.Served'), component: 'checkbox', @@ -130,6 +148,12 @@ const columns = computed(() => [ align: 'right', name: 'tableActions', actions: [ + { + title: t('globals.pageTitles.tickets'), + icon: 'vn:ticket', + action: (row) => redirectToTickets(row?.id), + isPrimary: true, + }, { title: t('components.smartCard.viewSummary'), icon: 'preview', @@ -155,6 +179,7 @@ const columns = computed(() => [ </template> <template #body> <VnTable + :with-filters="false" :data-key :columns="columns" ref="tableRef" @@ -174,6 +199,24 @@ const columns = computed(() => [ <WorkerDescriptorProxy :id="row?.workerFk" v-if="row?.workerFk" /> </span> </template> + <template #column-agencyName="{ row }"> + <span class="link" @click.stop> + {{ row?.agencyName }} + <AgencyDescriptorProxy + :id="row?.agencyModeFk" + v-if="row?.agencyModeFk" + /> + </span> + </template> + <template #column-vehiclePlateNumber="{ row }"> + <span class="link" @click.stop> + {{ row?.vehiclePlateNumber }} + <VehicleDescriptorProxy + :id="row?.vehicleFk" + v-if="row?.vehicleFk" + /> + </span> + </template> </VnTable> </template> </VnSection> diff --git a/src/pages/Route/RouteRoadmap.vue b/src/pages/Route/RouteRoadmap.vue index 23b1b1d5b..c981b86a5 100644 --- a/src/pages/Route/RouteRoadmap.vue +++ b/src/pages/Route/RouteRoadmap.vue @@ -2,13 +2,11 @@ import { useI18n } from 'vue-i18n'; import { computed, ref } from 'vue'; import { dashIfEmpty } from 'src/filters'; -import { toDate, toDateHourMin } from 'filters/index'; +import { toDate, toDateHourMin, toCurrency } from 'filters/index'; import { useQuasar } from 'quasar'; import { useSummaryDialog } from 'composables/useSummaryDialog'; -import toCurrency from 'filters/toCurrency'; import axios from 'axios'; -import VnSearchbar from 'components/ui/VnSearchbar.vue'; import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; import VnTable from 'components/VnTable/VnTable.vue'; import RoadmapSummary from 'pages/Route/Roadmap/RoadmapSummary.vue'; @@ -17,6 +15,8 @@ import VnInputDate from 'components/common/VnInputDate.vue'; import VnInputTime from 'src/components/common/VnInputTime.vue'; import VnSection from 'src/components/common/VnSection.vue'; import RoadmapFilter from './Roadmap/RoadmapFilter.vue'; +import VehicleDescriptorProxy from 'src/pages/Route/Vehicle/Card/VehicleDescriptorProxy.vue'; +import SupplierDescriptorProxy from 'src/pages/Supplier/Card/SupplierDescriptorProxy.vue'; const { viewSummary } = useSummaryDialog(); const { t } = useI18n(); @@ -33,7 +33,7 @@ const columns = computed(() => [ { align: 'left', name: 'name', - label: t('Roadmap'), + label: t('route.roadmap.roadmap'), create: true, cardVisible: true, columnFilter: { @@ -41,9 +41,9 @@ const columns = computed(() => [ }, }, { - align: 'left', + align: 'center', name: 'etd', - label: t('ETD'), + label: t('route.roadmap.etd'), component: 'date', columnFilter: { inWhere: true, @@ -54,7 +54,7 @@ const columns = computed(() => [ { align: 'left', name: 'supplierFk', - label: t('Carrier'), + label: t('route.roadmap.carrier'), component: 'select', attrs: { url: 'suppliers', @@ -65,21 +65,21 @@ const columns = computed(() => [ }, { name: 'tractorPlate', - label: t('Plate'), + label: t('route.roadmap.vehicle'), field: (row) => row.tractorPlate, sortable: true, align: 'left', }, { name: 'price', - label: t('Price'), - field: (row) => toCurrency(row.price), + label: t('route.roadmap.price'), + format: ({ price }) => toCurrency(price), sortable: true, - align: 'left', + align: 'right', }, { name: 'observations', - label: t('Observations'), + label: t('route.roadmap.observations'), field: (row) => dashIfEmpty(row.observations), sortable: true, align: 'left', @@ -89,7 +89,7 @@ const columns = computed(() => [ name: 'tableActions', actions: [ { - title: t('Ver cmr'), + title: t('route.roadmap.seeCmr'), icon: 'preview', isPrimary: true, action: (row) => viewSummary(row?.id, RoadmapSummary), @@ -124,8 +124,8 @@ function confirmRemove() { .dialog({ component: VnConfirm, componentProps: { - title: t('Selected roadmaps will be removed'), - message: t('Are you sure you want to continue?'), + title: t('route.roadmap.selectedRoadmapsRemoved'), + message: t('route.roadmap.areYouSure'), promise: removeSelection, }, }) @@ -157,15 +157,24 @@ function exprBuilder(param, value) { <QCard style="min-width: 350px"> <QCardSection> <p class="text-h6 q-ma-none"> - {{ t('Select the estimated date of departure (ETD)') }} + {{ t('route.roadmap.selectEtd') }} </p> </QCardSection> <QCardSection class="q-pt-none"> - <VnInputDate :label="t('ETD')" v-model="etdDate" autofocus /> + <VnInputDate + :label="t('route.roadmap.etd')" + v-model="etdDate" + autofocus + /> </QCardSection> <QCardActions align="right"> - <QBtn flat :label="t('Cancel')" v-close-popup class="text-primary" /> + <QBtn + flat + :label="t('globals.cancel')" + v-close-popup + class="text-primary" + /> <QBtn color="primary" v-close-popup @click="cloneSelection"> {{ t('globals.clone') }} </QBtn> @@ -181,7 +190,7 @@ function exprBuilder(param, value) { :disable="!selectedRows?.length" @click="isCloneDialogOpen = true" > - <QTooltip>{{ t('Clone Selected Routes') }}</QTooltip> + <QTooltip>{{ t('route.roadmap.cloneSelected') }}</QTooltip> </QBtn> <QBtn icon="delete" @@ -190,7 +199,7 @@ function exprBuilder(param, value) { :disable="!selectedRows?.length" @click="confirmRemove" > - <QTooltip>{{ t('Delete roadmap(s)') }}</QTooltip> + <QTooltip>{{ t('route.roadmap.deleteRoadmap') }}</QTooltip> </QBtn> </template> </VnSubToolbar> @@ -222,7 +231,7 @@ function exprBuilder(param, value) { redirect="route/roadmap" :create="{ urlCreate: 'Roadmaps', - title: t('Create routemap'), + title: t('route.roadmap.createRoadmap'), onDataSaved: ({ id }) => tableRef.redirect(id), formInitialData: {}, }" @@ -232,7 +241,10 @@ function exprBuilder(param, value) { {{ toDateHourMin(row.etd) }} </template> <template #column-supplierFk="{ row }"> - {{ row.supplierFk }} + <span class="link" @click.stop> + {{ row.driverName }} + <SupplierDescriptorProxy :id="row.supplierFk" /> + </span> </template> <template #more-create-dialog="{ data }"> <VnInputDate v-model="data.etd" /> @@ -251,21 +263,3 @@ function exprBuilder(param, value) { gap: 12px; } </style> -<i18n> -es: - Create routemap: Crear troncal - Search roadmaps: Buscar troncales - You can search by roadmap reference: Puedes buscar por referencia del troncal - Delete roadmap(s): Eliminar troncal(es) - Selected roadmaps will be removed: Los troncales seleccionadas serán eliminados - Are you sure you want to continue?: ¿Seguro que quieres continuar? - The date can't be empty: La fecha no puede estar vacía - Clone Selected Routes: Clonar rutas seleccionadas - Create roadmap: Crear troncal - Roadmap: Troncal - Carrier: Transportista - Plate: Matrícula - Price: Precio - Observations: Observaciones - Select the estimated date of departure (ETD): Selecciona la fecha estimada de salida -</i18n> diff --git a/src/pages/Route/RouteTickets.vue b/src/pages/Route/RouteTickets.vue index adc7dfdaa..b17fb543f 100644 --- a/src/pages/Route/RouteTickets.vue +++ b/src/pages/Route/RouteTickets.vue @@ -37,9 +37,9 @@ const columns = computed(() => [ align: 'left', }, { - name: 'city', - label: t('City'), - field: (row) => row?.city, + name: 'client', + label: t('Client'), + field: (row) => row?.nickname, sortable: false, align: 'left', }, @@ -51,9 +51,9 @@ const columns = computed(() => [ align: 'center', }, { - name: 'client', - label: t('Client'), - field: (row) => row?.nickname, + name: 'city', + label: t('City'), + field: (row) => row?.city, sortable: false, align: 'left', }, diff --git a/src/pages/Route/Vehicle/Card/VehicleDescriptor.vue b/src/pages/Route/Vehicle/Card/VehicleDescriptor.vue index d9a2434ab..ad2ae61e4 100644 --- a/src/pages/Route/Vehicle/Card/VehicleDescriptor.vue +++ b/src/pages/Route/Vehicle/Card/VehicleDescriptor.vue @@ -1,17 +1,29 @@ <script setup> +import { computed } from 'vue'; +import { useRoute } from 'vue-router'; import VnLv from 'src/components/ui/VnLv.vue'; import CardDescriptor from 'components/ui/CardDescriptor.vue'; import axios from 'axios'; import useNotify from 'src/composables/useNotify.js'; const { notify } = useNotify(); + +const props = defineProps({ + id: { + type: Number, + required: false, + default: null, + }, +}); + +const route = useRoute(); +const entityId = computed(() => props.id || route.params.id); </script> <template> <CardDescriptor - :url="`Vehicles/${$route.params.id}`" + :url="`Vehicles/${entityId}`" data-key="Vehicle" title="numberPlate" - :to-module="{ name: 'VehicleList' }" > <template #menu="{ entity }"> <QItem diff --git a/src/pages/Route/Vehicle/Card/VehicleDescriptorProxy.vue b/src/pages/Route/Vehicle/Card/VehicleDescriptorProxy.vue new file mode 100644 index 000000000..cc0943cb8 --- /dev/null +++ b/src/pages/Route/Vehicle/Card/VehicleDescriptorProxy.vue @@ -0,0 +1,20 @@ +<script setup> +import VehicleDescriptor from 'pages/Route/Vehicle/Card/VehicleDescriptor.vue'; +import VehicleSummary from './VehicleSummary.vue'; + +const $props = defineProps({ + id: { + type: Number, + required: true, + }, + summary: { + type: Object, + default: null, + }, +}); +</script> +<template> + <QPopupProxy> + <VehicleDescriptor v-if="$props.id" :id="$props.id" :summary="VehicleSummary" /> + </QPopupProxy> +</template> diff --git a/src/pages/Route/Vehicle/Card/VehicleSummary.vue b/src/pages/Route/Vehicle/Card/VehicleSummary.vue index e4b0a9497..13d4bbc9d 100644 --- a/src/pages/Route/Vehicle/Card/VehicleSummary.vue +++ b/src/pages/Route/Vehicle/Card/VehicleSummary.vue @@ -13,12 +13,13 @@ const props = defineProps({ id: { type: [Number, String], default: null } }); const route = useRoute(); const entityId = computed(() => props.id || +route.params.id); +const baseLink = `#/${route.meta.moduleName.toLowerCase()}/vehicle/${entityId.value}`; const links = { - 'basic-data': `#/vehicle/${entityId.value}/basic-data`, - notes: `#/vehicle/${entityId.value}/notes`, - dms: `#/vehicle/${entityId.value}/dms`, - 'invoice-in': `#/vehicle/${entityId.value}/invoice-in`, - events: `#/vehicle/${entityId.value}/events`, + 'basic-data': `${baseLink}/basic-data`, + notes: `${baseLink}/notes`, + dms: `${baseLink}/dms`, + 'invoice-in': `${baseLink}/invoice-in`, + events: `${baseLink}/events`, }; </script> <template> @@ -54,7 +55,10 @@ const links = { <template #value> <span class="link"> {{ entity.supplier?.name }} - <SupplierDescriptorProxy :id="entity.supplierFk" /> + <SupplierDescriptorProxy + v-if="entity.supplierFk" + :id="entity.supplierFk" + /> </span> </template> </VnLv> @@ -63,6 +67,7 @@ const links = { <span class="link"> {{ entity.supplierCooler?.name }} <SupplierDescriptorProxy + v-if="entity.supplierCoolerFk" :id="entity.supplierCoolerFk" /> </span> diff --git a/src/pages/Route/Vehicle/VehicleList.vue b/src/pages/Route/Vehicle/VehicleList.vue index e5b945010..a79cc2e35 100644 --- a/src/pages/Route/Vehicle/VehicleList.vue +++ b/src/pages/Route/Vehicle/VehicleList.vue @@ -116,6 +116,7 @@ const columns = computed(() => [ title: t('components.smartCard.openSummary'), icon: 'preview', action: (row) => viewSummary(row.id, VehicleSummary), + isPrimary: true, }, ], }, diff --git a/src/pages/Route/locale/en.yml b/src/pages/Route/locale/en.yml index cc445f412..283b61855 100644 --- a/src/pages/Route/locale/en.yml +++ b/src/pages/Route/locale/en.yml @@ -1,48 +1,79 @@ route: + filter: + Served: Served + extendedList: + selectStartingDate: Select the starting date + startingDate: Starting date + cloneSelectedRoutes: Clone selected routes + downloadSelectedRoutes: Download selected routes as PDF + markServed: Mark as served roadmap: + roadmap: Roadmap + carrier: Carrier + vehicle: Vehicle + price: Price + observations: Observations + etd: ETD + dateCantEmpty: The date can't be empty + createRoadmap: Create roadmap + deleteRoadmap: Delete roadmap(s) + cloneSelected: Clone selected routes + selectedRoadmapsRemoved: Selected roadmaps will be removed + areYouSure: Are you sure you want to continue? + selectEtd: Select the estimated date of departure (ETD) search: Search roadmap searchInfo: You can search by roadmap reference params: + warehouseFk: Warehouse + description: Description + m3: m³ + scopeDays: Days Onward + vehicleFk: Vehicle + agencyModeFk: Agency + workerFk: Worker + from: From + to: To + isOk: Served etd: ETD tractorPlate: Plate price: Price observations: Observations - id: ID + id: Id name: Name cmrFk: CMR id hasCmrDms: Attached in gestdoc ticketFk: Ticketd id routeFk: Route id + clientFk: Client id + countryFk: Country shipped: Shipped agencyAgreement: Agency agreement agencyModeName: Agency route + isOwn: Own + isAnyVolumeallowed: Any volume allowed Worker: Worker Agency: Agency Vehicle: Vehicle Description: Description hourStarted: H.Start hourFinished: H.End - dated: Dated - From: From - To: To + createRoute: Create route Date: Date KmStart: Km start KmEnd: Km end Served: Served - Clone Selected Routes: Clone selected routes - Select the starting date: Select the starting date - Stating date: Starting date - Cancel: Cancel - Mark as served: Mark as served - Download selected routes as PDF: Download selected routes as PDF - Add ticket: Add ticket - Summary: Summary + addTicket: Add ticket + routeSummary: Go to summary Route is closed: Route is closed Route is not served: Route is not served search: Search route searchInfo: You can search by route reference + dated: Dated + preview: Preview cmr: - list: + search: Search Cmr + searchInfo: You can search Cmr by Id + params: results: results cmrFk: CMR id hasCmrDms: Attached in gestdoc @@ -50,8 +81,10 @@ route: 'false': 'No' ticketFk: Ticketd id routeFk: Route id - country: Country + countryFk: Country clientFk: Client id + warehouseFk: Warehouse shipped: Preparation date viewCmr: View CMR - downloadCmrs: Download CMRs \ No newline at end of file + downloadCmrs: Download CMRs + search: General search diff --git a/src/pages/Route/locale/es.yml b/src/pages/Route/locale/es.yml index 51d43774a..2785ded31 100644 --- a/src/pages/Route/locale/es.yml +++ b/src/pages/Route/locale/es.yml @@ -1,21 +1,57 @@ route: + filter: + Served: Servida + extendedList: + selectStartingDate: Seleccione la fecha de inicio + statingDate: Fecha de inicio + cloneSelectedRoutes: Clonar rutas seleccionadas + downloadSelectedRoutes: Descargar rutas seleccionadas como PDF + markServed: Marcar como servidas roadmap: + roadmap: Troncal + carrier: Transportista + vehicle: Vehículo + price: Precio + observations: Observaciones + etd: ETD + dateCantEmpty: La fecha no puede estar vacía + createRoadmap: Crear troncal + deleteRoadmap: Eliminar troncal(es) + cloneSelected: Clonar rutas seleccionadas + selectedRoadmapsRemoved: Los troncales seleccionadas serán eliminados + areYouSure: ¿Seguro que quieres continuar? + selectEtd: Selecciona la fecha estimada de salida search: Buscar troncales searchInfo: Puedes buscar por referencia del troncal params: - agencyModeName: Agencia Ruta - agencyAgreement: Agencia Acuerdo - id: Id - name: Troncal + warehouseFk: Almacén + description: Descripción + m3: m³ + scopeDays: Días adelante + vehicleFk: Vehículo + agencyModeFk: Agencia + workerFk: Trabajador + from: Desde + to: Hasta + isOk: Servida etd: ETD tractorPlate: Matrícula price: Precio observations: Observaciones + id: Id + name: Troncal cmrFk: Id CMR hasCmrDms: Gestdoc + search: Búsqueda general ticketFk: Id ticket - routeFK: Id ruta + routeFk: Id ruta + clientFk: Id cliente + countryFk: Pais shipped: Fecha preparación + agencyModeName: Agencia Ruta + agencyAgreement: Agencia Acuerdo + isOwn: Propio + isAnyVolumeAllowed: Cualquier volumen Worker: Trabajador Agency: Agencia Vehicle: Vehículo @@ -23,25 +59,18 @@ route: hourStarted: H.Inicio hourFinished: H.Fin createRoute: Crear ruta - From: Desde - To: Hasta Date: Fecha KmStart: Km inicio KmEnd: Km fin Served: Servida - Clone Selected Routes: Clonar rutas seleccionadas - Select the starting date: Seleccione la fecha de inicio - Stating date: Fecha de inicio - Cancel: Cancelar - Mark as served: Marcar como servidas - Download selected routes as PDF: Descargar rutas seleccionadas como PDF - Add ticket: Añadir tickets - preview: Vista previa - Summary: Resumen + addTicket: Añadir tickets + routeSummary: Ir a vista previa Route is closed: La ruta está cerrada Route is not served: La ruta no está servida search: Buscar rutas searchInfo: Puedes buscar por referencia de la ruta + dated: Fecha + preview: Vista previa cmr: list: results: resultados @@ -55,4 +84,4 @@ route: clientFk: Id cliente shipped: Fecha preparación viewCmr: Ver CMR - downloadCmrs: Descargar CMRs \ No newline at end of file + downloadCmrs: Descargar CMRs diff --git a/src/pages/Shelving/Card/ShelvingFilter.vue b/src/pages/Shelving/Card/ShelvingFilter.vue index 56cf4f58c..88d716046 100644 --- a/src/pages/Shelving/Card/ShelvingFilter.vue +++ b/src/pages/Shelving/Card/ShelvingFilter.vue @@ -2,6 +2,7 @@ import { useI18n } from 'vue-i18n'; import VnFilterPanel from 'components/ui/VnFilterPanel.vue'; import VnSelect from 'src/components/common/VnSelect.vue'; +import VnSelectWorker from 'src/components/common/VnSelectWorker.vue'; const { t } = useI18n(); const props = defineProps({ @@ -46,19 +47,7 @@ const emit = defineEmits(['search']); </QItem> <QItem class="q-mb-sm"> <QItemSection> - <VnSelect - dense - outlined - rounded - :label="t('params.userFk')" - v-model="params.userFk" - url="Workers/activeWithInheritedRole" - option-value="id" - option-label="firstName" - :where="{ role: 'salesPerson' }" - sort-by="firstName ASC" - :use-like="false" - /> + <VnSelectWorker v-model="params.userFk" outlined rounded /> </QItemSection> </QItem> <QItem class="q-mb-md"> diff --git a/src/pages/Shelving/Card/ShelvingSummary.vue b/src/pages/Shelving/Card/ShelvingSummary.vue index f89ff4d78..4a6669624 100644 --- a/src/pages/Shelving/Card/ShelvingSummary.vue +++ b/src/pages/Shelving/Card/ShelvingSummary.vue @@ -6,6 +6,7 @@ import VnLv from 'components/ui/VnLv.vue'; import VnUserLink from 'components/ui/VnUserLink.vue'; import filter from './ShelvingFilter.js'; import ShelvingDescriptorMenu from './ShelvingDescriptorMenu.vue'; +import VnTitle from 'src/components/common/VnTitle.vue'; const $props = defineProps({ id: { @@ -38,13 +39,10 @@ const entityId = computed(() => $props.id || route.params.id); </template> <template #body="{ entity }"> <QCard class="vn-one"> - <RouterLink - class="header header-link" - :to="{ name: 'ShelvingBasicData', params: { id: entityId } }" - > - {{ $t('globals.pageTitles.basicData') }} - <QIcon name="open_in_new" /> - </RouterLink> + <VnTitle + :url="`#/shelving/${entityId}/basic-data`" + :text="$t('globals.pageTitles.basicData')" + /> <VnLv :label="$t('globals.code')" :value="entity.code" /> <VnLv :label="$t('shelving.list.parking')" diff --git a/src/pages/Shelving/Parking/Card/ParkingSummary.vue b/src/pages/Shelving/Parking/Card/ParkingSummary.vue index 7188ebeb6..1365c71ca 100644 --- a/src/pages/Shelving/Parking/Card/ParkingSummary.vue +++ b/src/pages/Shelving/Parking/Card/ParkingSummary.vue @@ -4,6 +4,7 @@ import { useRoute } from 'vue-router'; import { useI18n } from 'vue-i18n'; import CardSummary from 'components/ui/CardSummary.vue'; import VnLv from 'components/ui/VnLv.vue'; +import VnTitle from 'src/components/common/VnTitle.vue'; const $props = defineProps({ id: { @@ -28,13 +29,10 @@ const filter = { <template #body="{ entity }"> <QCard class="vn-one"> <QCardSection class="q-pa-none"> - <a - class="header header-link" - :href="`#/parking/${entityId}/basic-data`" - > - {{ t('globals.pageTitles.basicData') }} - <QIcon name="open_in_new" /> - </a> + <VnTitle + :url="`#/shelving/parking/${entityId}/basic-data`" + :text="$t('globals.pageTitles.basicData')" + /> </QCardSection> <VnLv :label="t('globals.code')" :value="entity.code" /> <VnLv diff --git a/src/pages/Supplier/Card/SupplierConsumption.vue b/src/pages/Supplier/Card/SupplierConsumption.vue index 718de95dd..259561f4c 100644 --- a/src/pages/Supplier/Card/SupplierConsumption.vue +++ b/src/pages/Supplier/Card/SupplierConsumption.vue @@ -203,7 +203,7 @@ onMounted(async () => { </QTr> <QTr v-for="(buy, index) in row.buys" :key="index"> <QTd no-hover> - <QBtn flat color="blue" dense no-caps>{{ buy.itemName }}</QBtn> + <QBtn flat class="link" dense no-caps>{{ buy.itemName }}</QBtn> <ItemDescriptorProxy :id="buy.itemFk" /> </QTd> diff --git a/src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue b/src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue index ef2eb75d6..3c2fe95e5 100644 --- a/src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue +++ b/src/pages/Ticket/Card/BasicData/TicketBasicDataView.vue @@ -44,8 +44,8 @@ const getPriceDifference = async () => { shipped: ticket.value.shipped, }; const { data } = await axios.post( - `tickets/${ticket.value.id}/priceDifference`, - params + `tickets/${formData.value.id}/priceDifference`, + params, ); ticket.value.sale = data; }; @@ -71,8 +71,8 @@ const submit = async () => { }; const { data } = await axios.post( - `tickets/${ticket.value.id}/componentUpdate`, - params + `tickets/${formData.value.id}/componentUpdate`, + params, ); if (!data) return; @@ -99,7 +99,7 @@ const onNextStep = async () => { openConfirmationModal( t('basicData.negativesConfirmTitle'), t('basicData.negativesConfirmMessage'), - submitWithNegatives + submitWithNegatives, ); else submit(); } diff --git a/src/pages/Ticket/Card/TicketDescriptor.vue b/src/pages/Ticket/Card/TicketDescriptor.vue index 1e585592f..743f2188c 100644 --- a/src/pages/Ticket/Card/TicketDescriptor.vue +++ b/src/pages/Ticket/Card/TicketDescriptor.vue @@ -3,14 +3,15 @@ import { ref, computed } from 'vue'; import { useRoute } from 'vue-router'; import { useI18n } from 'vue-i18n'; import CustomerDescriptorProxy from 'pages/Customer/Card/CustomerDescriptorProxy.vue'; +import DepartmentDescriptorProxy from 'pages/Worker/Department/Card/DepartmentDescriptorProxy.vue'; import CardDescriptor from 'components/ui/CardDescriptor.vue'; import TicketDescriptorMenu from './TicketDescriptorMenu.vue'; import VnLv from 'src/components/ui/VnLv.vue'; -import VnUserLink from 'src/components/ui/VnUserLink.vue'; import { toDateTimeFormat } from 'src/filters/date'; import filter from './TicketFilter.js'; import FetchData from 'src/components/FetchData.vue'; import TicketProblems from 'src/components/TicketProblems.vue'; +import axios from 'axios'; const $props = defineProps({ id: { @@ -31,23 +32,37 @@ const entityId = computed(() => { return $props.id || route.params.id; }); const problems = ref({}); +const originalTicket = ref(); function ticketFilter(ticket) { return JSON.stringify({ clientFk: ticket.clientFk }); } +async function getClaims() { + const userFilter = { where: { refundTicketFk: entityId.value } }; + const { data } = await axios.get(`TicketRefunds`, { + params: { filter: JSON.stringify(userFilter) }, + }); + if (!data) return; + originalTicket.value = data[0]?.originalTicketFk; +} +async function getProblems() { + const { data } = await axios.get(`Tickets/${entityId.value}/getTicketProblems`); + if (!data) return; + problems.value = data[0]; +} +function getInfo() { + getClaims(); + getProblems(); +} </script> <template> - <FetchData - :url="`Tickets/${entityId}/getTicketProblems`" - auto-load - @on-fetch="(data) => ([problems] = data)" - /> <CardDescriptor :url="`Tickets/${entityId}`" :filter="filter" data-key="Ticket" :summary="$props.summary" + @on-fetch="getInfo" width="lg-width" > <template #menu="{ entity }"> @@ -73,12 +88,12 @@ function ticketFilter(ticket) { </QBadge> </template> </VnLv> - <VnLv :label="t('globals.salesPerson')"> + <VnLv :label="t('customer.summary.team')"> <template #value> - <VnUserLink - :name="entity.client?.salesPersonUser?.name" - :worker-id="entity.client?.salesPersonFk" - /> + <span class="link"> + {{ entity?.client?.department?.name || '-' }} + <DepartmentDescriptorProxy :id="entity?.client?.departmentFk" /> + </span> </template> </VnLv> <VnLv @@ -95,7 +110,7 @@ function ticketFilter(ticket) { </template> <template #icons="{ entity }"> <QCardActions class="q-gutter-x-xs"> - <TicketProblems :row="{ ...entity?.client, ...problems }" /> + <TicketProblems :row="{ ...entity?.client, ...problems, ...entity }" /> </QCardActions> </template> <template #actions="{ entity }"> @@ -129,6 +144,15 @@ function ticketFilter(ticket) { > <QTooltip>{{ t('ticket.card.newOrder') }}</QTooltip> </QBtn> + <QBtn + v-if="originalTicket" + size="md" + icon="vn:claims" + color="primary" + :to="{ name: 'TicketCard', params: { id: originalTicket } }" + > + <QTooltip>{{ t('ticket.card.ticketClaimed') }}</QTooltip> + </QBtn> </QCardActions> </template> </CardDescriptor> diff --git a/src/pages/Ticket/Card/TicketEditMana.vue b/src/pages/Ticket/Card/TicketEditMana.vue index ff40a6592..266c82ccd 100644 --- a/src/pages/Ticket/Card/TicketEditMana.vue +++ b/src/pages/Ticket/Card/TicketEditMana.vue @@ -33,7 +33,7 @@ const save = (sale = $props.sale) => { }; const getMana = async () => { - const { data } = await axios.get(`Tickets/${route.params.id}/getSalesPersonMana`); + const { data } = await axios.get(`Tickets/${route.params.id}/getDepartmentMana`); mana.value = data; await getUsesMana(); }; diff --git a/src/pages/Ticket/Card/TicketFilter.js b/src/pages/Ticket/Card/TicketFilter.js index 7846f1658..daa204a7a 100644 --- a/src/pages/Ticket/Card/TicketFilter.js +++ b/src/pages/Ticket/Card/TicketFilter.js @@ -12,7 +12,7 @@ export default { fields: [ 'id', 'name', - 'salesPersonFk', + 'departmentFk', 'phone', 'mobile', 'email', @@ -29,7 +29,7 @@ export default { fields: ['id', 'lang'], }, }, - { relation: 'salesPersonUser' }, + { relation: 'department' }, ], }, }, diff --git a/src/pages/Ticket/Card/TicketSale.vue b/src/pages/Ticket/Card/TicketSale.vue index e69d489c0..2fb305cc3 100644 --- a/src/pages/Ticket/Card/TicketSale.vue +++ b/src/pages/Ticket/Card/TicketSale.vue @@ -174,22 +174,26 @@ const getSaleTotal = (sale) => { return price - discount; }; -const getRowUpdateInputEvents = (sale) => ({ - 'keyup.enter': () => { - changeQuantity(sale); - }, - blur: () => { - changeQuantity(sale); - }, -}); +const getRowUpdateInputEvents = (sale) => { + return { + 'keyup.enter': () => { + changeQuantity(sale); + }, + blur: () => { + changeQuantity(sale); + }, + }; +}; const resetChanges = async () => { arrayData.fetch({ append: false }); tableRef.value.reload(); + selectedRows.value = []; }; const changeQuantity = async (sale) => { if (!sale.itemFk || sale.quantity == null || sale?.originalQuantity === sale.quantity) return; + else sale.originalQuantity = sale.quantity; if (!sale.id) return addSale(sale); if (await isSalePrepared(sale)) { @@ -235,7 +239,7 @@ const addSale = async (sale) => { notify('globals.dataSaved', 'positive'); sale.isNew = false; - arrayData.fetch({}); + resetChanges(); }; const changeConcept = async (sale) => { if (await isSalePrepared(sale)) { @@ -258,6 +262,18 @@ const DEFAULT_EDIT = { oldQuantity: null, }; const edit = ref({ ...DEFAULT_EDIT }); +const usesMana = ref(null); + +const getUsesMana = async () => { + const { data } = await axios.get('Sales/usesMana'); + usesMana.value = data; +}; + +const getMana = async () => { + const { data } = await axios.get(`Tickets/${route.params.id}/getDepartmentMana`); + mana.value = data; + await getUsesMana(); +}; const selectedValidSales = computed(() => { if (!sales.value) return; @@ -310,7 +326,7 @@ const changeDiscount = async (sale) => { } }; -const updateDiscounts = async (sales, newDiscount = null) => { +const updateDiscounts = async (sales, newDiscount) => { const salesTracking = await fetchSalesTracking(); const someSaleIsPrepared = salesTracking.some((sale) => @@ -320,12 +336,11 @@ const updateDiscounts = async (sales, newDiscount = null) => { else updateDiscount(sales, newDiscount); }; -const updateDiscount = async (sales, newDiscount = null) => { - const saleIds = sales.map((sale) => sale.id); - const _newDiscount = newDiscount || edit.value.discount; +const updateDiscount = async (sales, newDiscount = 0) => { + const salesIds = sales.map(({ id }) => id); const params = { - salesIds: saleIds, - newDiscount: _newDiscount, + salesIds, + newDiscount, manaCode: manaCode.value, }; await axios.post(`Tickets/${route.params.id}/updateDiscount`, params); @@ -474,7 +489,7 @@ const endNewRow = (row) => { }; async function confirmUpdate(cb) { - await quasar + quasar .dialog({ component: VnConfirm, componentProps: { @@ -664,6 +679,7 @@ watch( selection: 'multiple', }" :right-search="false" + :search-url="false" :column-search="false" :disable-option="{ card: true }" auto-load @@ -703,7 +719,7 @@ watch( </template> <template #column-image="{ row }"> <div class="image-wrapper"> - <VnImg :id="parseInt(row?.item?.id)" class="rounded" /> + <VnImg v-if="row.item" :id="parseInt(row?.item?.id)" class="rounded" /> </div> </template> <template #column-visible="{ row }"> @@ -751,7 +767,7 @@ watch( {{ row?.item?.subName.toUpperCase() }} </div> </div> - <FetchedTags :item="row.item" :max-length="6" /> + <FetchedTags v-if="row.item" :item="row.item" :max-length="6" /> <QPopupProxy v-if="row.id && isTicketEditable"> <VnInput v-model="row.concept" diff --git a/src/pages/Ticket/Card/TicketSaleMoreActions.vue b/src/pages/Ticket/Card/TicketSaleMoreActions.vue index 840b62507..773b0807f 100644 --- a/src/pages/Ticket/Card/TicketSaleMoreActions.vue +++ b/src/pages/Ticket/Card/TicketSaleMoreActions.vue @@ -62,8 +62,6 @@ const isClaimable = computed(() => { } return false; }); -const hasReserves = computed(() => props.sales.some((sale) => sale.reserved == true)); - const sendSms = async (params) => { await axios.post(`Tickets/${ticket.value.id}/sendSms`, params); notify(t('SMS sent'), 'positive'); @@ -144,14 +142,6 @@ const onCreateClaimAccepted = async () => { push({ name: 'ClaimBasicData', params: { id: data.id } }); }; -const setReserved = async (reserved) => { - const params = { ticketId: ticket.value.id, sales: props.sales, reserved: reserved }; - await axios.post(`Sales/reserve`, params); - props.sales.forEach((sale) => { - sale.reserved = reserved; - }); -}; - const createRefund = async (withWarehouse) => { if (!props.ticket) return; @@ -252,18 +242,6 @@ const createRefund = async (withWarehouse) => { <QItemLabel>{{ t('Mark as reserved') }}</QItemLabel> </QItemSection> </QItem> - <QItem - v-if="isTicketEditable && hasReserves" - clickable - v-close-popup - v-ripple - @click="setReserved(false)" - data-cy="unmarkAsReservedItem" - > - <QItemSection> - <QItemLabel>{{ t('Unmark as reserved') }}</QItemLabel> - </QItemSection> - </QItem> <QItem clickable v-ripple data-cy="ticketSaleRefundItem"> <QItemSection> <QItemLabel>{{ t('Refund') }}</QItemLabel> diff --git a/src/pages/Ticket/Card/TicketSummary.vue b/src/pages/Ticket/Card/TicketSummary.vue index 5838efa88..a19ab3779 100644 --- a/src/pages/Ticket/Card/TicketSummary.vue +++ b/src/pages/Ticket/Card/TicketSummary.vue @@ -13,9 +13,9 @@ import VnLinkPhone from 'src/components/ui/VnLinkPhone.vue'; import { getUrl } from 'src/composables/getUrl'; import useNotify from 'src/composables/useNotify.js'; import { useArrayData } from 'composables/useArrayData'; -import VnUserLink from 'src/components/ui/VnUserLink.vue'; import VnTitle from 'src/components/common/VnTitle.vue'; import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue'; +import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue'; 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'; @@ -152,12 +152,14 @@ onMounted(async () => { </QBadge> </template> </VnLv> - <VnLv :label="t('globals.salesPerson')"> + <VnLv :label="t('customer.summary.team')"> <template #value> - <VnUserLink - :name="entity.client?.salesPersonUser?.name" - :worker-id="entity.client?.salesPersonFk" - /> + <span class="link"> + {{ entity?.client?.department?.name || '-' }} + <DepartmentDescriptorProxy + :id="entity?.client?.departmentFk" + /> + </span> </template> </VnLv> <VnLv :label="t('globals.agency')" :value="entity.agencyMode?.name" /> diff --git a/src/pages/Ticket/Negative/TicketLackTable.vue b/src/pages/Ticket/Negative/TicketLackTable.vue index 176e8f7ad..c9c2a7cad 100644 --- a/src/pages/Ticket/Negative/TicketLackTable.vue +++ b/src/pages/Ticket/Negative/TicketLackTable.vue @@ -225,7 +225,7 @@ function onBuysFetched(data) { /> </div> <div class="flex column left" style="align-items: flex-start"> - <QBtn flat class="link text-blue"> + <QBtn flat class="link"> {{ item?.longName ?? item.name }} <ItemDescriptorProxy :id="entityId" /> <FetchedTags class="q-ml-md" :item="item" :columns="7" /> diff --git a/src/pages/Ticket/TicketAdvance.vue b/src/pages/Ticket/TicketAdvance.vue index 94b4623aa..bf3593acd 100644 --- a/src/pages/Ticket/TicketAdvance.vue +++ b/src/pages/Ticket/TicketAdvance.vue @@ -259,7 +259,7 @@ const moveTicketsAdvance = async () => { destinationId: ticket.id, originShipped: ticket.futureShipped, destinationShipped: ticket.shipped, - salesPersonFk: ticket.workerFk, + departmentFk: ticket.departmentFk, }); } const params = { tickets: ticketsToMove }; @@ -285,7 +285,7 @@ const progressAdd = () => { t('advanceTickets.moveTicketSuccess', { ticketsNumber: progressLength.value - splitErrors.value.length, }), - 'positive' + 'positive', ); } }; @@ -345,16 +345,16 @@ watch( originElRef.value.setAttribute('colspan', '9'); destinationElRef.value.textContent = `${t( - 'advanceTickets.destination' + 'advanceTickets.destination', )} ${toDateFormat(vnTableRef.value.params.dateToAdvance)}`; originElRef.value.textContent = `${t('advanceTickets.origin')} ${toDateFormat( - vnTableRef.value.params.dateFuture + vnTableRef.value.params.dateFuture, )}`; newRow.append(destinationElRef.value, originElRef.value); head.insertBefore(newRow, firstRow); }, - { once: true, inmmediate: true } + { once: true, inmmediate: true }, ); watch( @@ -362,14 +362,14 @@ watch( () => { if (originElRef.value && destinationElRef.value) { destinationElRef.value.textContent = `${t( - 'advanceTickets.destination' + 'advanceTickets.destination', )} ${toDateFormat(vnTableRef.value.params.dateToAdvance)}`; originElRef.value.textContent = `${t('advanceTickets.origin')} ${toDateFormat( - vnTableRef.value.params.dateFuture + vnTableRef.value.params.dateFuture, )}`; } }, - { deep: true } + { deep: true }, ); </script> <template> @@ -405,7 +405,7 @@ watch( t(`advanceTickets.advanceTitleSubtitle`, { selectedTickets: selectedTickets.length, }), - moveTicketsAdvance + moveTicketsAdvance, ) " > @@ -423,7 +423,7 @@ watch( t(`advanceTickets.advanceWithoutNegativeSubtitle`, { selectedTickets: selectedTickets.length, }), - splitTickets + splitTickets, ) " > diff --git a/src/pages/Ticket/TicketFilter.vue b/src/pages/Ticket/TicketFilter.vue index bdf75c826..f959157f6 100644 --- a/src/pages/Ticket/TicketFilter.vue +++ b/src/pages/Ticket/TicketFilter.vue @@ -97,15 +97,15 @@ const getGroupedStates = (data) => { </QItem> <QItem> <QItemSection> - <VnSelectWorker - :label="t('globals.salesPerson')" - v-model="params.salesPersonFk" - :params="{ - departmentCodes: ['VT'], - }" - dense + <VnSelect outlined + dense rounded + :label="t('globals.params.departmentFk')" + v-model="params.departmentFk" + option-value="id" + option-label="name" + url="Departments" /> </QItemSection> </QItem> @@ -308,7 +308,6 @@ en: from: From shipped: Shipped to: To - salesPersonFk: Salesperson stateFk: State groupedStates: Grouped State refFk: Invoice Ref. @@ -336,7 +335,6 @@ es: from: Desde shipped: F. envío to: Hasta - salesPersonFk: Comercial stateFk: Estado groupedStates: Estado agrupado refFk: Ref. Factura @@ -355,7 +353,6 @@ es: Order ID: ID Pedido From: Desde To: Hasta - Salesperson: Comercial State: Estado Invoice Ref.: Ref. Factura My team: Mi equipo diff --git a/src/pages/Ticket/TicketFuture.vue b/src/pages/Ticket/TicketFuture.vue index 92911cd25..588379ed9 100644 --- a/src/pages/Ticket/TicketFuture.vue +++ b/src/pages/Ticket/TicketFuture.vue @@ -160,7 +160,7 @@ const moveTicketsFuture = async () => { destinationId: ticket.futureId, originShipped: ticket.shipped, destinationShipped: ticket.futureShipped, - salesPersonFk: ticket.salesPersonFk, + departmentFk: ticket.departmentFk, }; }); diff --git a/src/pages/Ticket/TicketList.vue b/src/pages/Ticket/TicketList.vue index dfaabc848..c603246d1 100644 --- a/src/pages/Ticket/TicketList.vue +++ b/src/pages/Ticket/TicketList.vue @@ -17,6 +17,7 @@ import TicketFilter from './TicketFilter.vue'; import VnInput from 'src/components/common/VnInput.vue'; import FetchData from 'src/components/FetchData.vue'; import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue'; +import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue'; import ZoneDescriptorProxy from 'src/pages/Zone/Card/ZoneDescriptorProxy.vue'; import { toTimeFormat } from 'src/filters/date'; import InvoiceOutDescriptorProxy from 'src/pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue'; @@ -99,22 +100,16 @@ const columns = computed(() => [ }, { align: 'left', - label: t('ticketList.salesPerson'), - name: 'salesPersonFk', + name: 'departmentFk', + label: t('customer.summary.team'), component: 'select', attrs: { - url: 'Workers/activeWithInheritedRole', - fields: ['id', 'name'], - where: { role: 'salesPerson' }, - optionFilter: 'firstName', - useLike: false, + url: 'Departments', }, columnField: { component: null, }, - columnClass: 'expand', - cardVisible: true, - format: (row, dashIfEmpty) => dashIfEmpty(row.salesPerson), + format: (row, dashIfEmpty) => dashIfEmpty(row.departmentName), }, { align: 'left', @@ -519,10 +514,10 @@ function setReference(data) { <template #column-statusIcons="{ row }"> <TicketProblems :row="row" /> </template> - <template #column-salesPersonFk="{ row }"> + <template #column-departmentFk="{ row }"> <span class="link" @click.stop> - {{ dashIfEmpty(row.userName) }} - <CustomerDescriptorProxy :id="row.salesPersonFk" /> + {{ dashIfEmpty(row.departmentName) }} + <DepartmentDescriptorProxy :id="row.departmentFk" /> </span> </template> <template #column-shippedDate="{ row }"> diff --git a/src/pages/Ticket/TicketWeekly.vue b/src/pages/Ticket/TicketWeekly.vue index 0e18fe028..d6493550b 100644 --- a/src/pages/Ticket/TicketWeekly.vue +++ b/src/pages/Ticket/TicketWeekly.vue @@ -5,7 +5,7 @@ import VnSelect from 'src/components/common/VnSelect.vue'; import VnSelectCache from 'src/components/common/VnSelectCache.vue'; import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue'; import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue'; -import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; +import DepartmentDescriptorProxy from 'src/pages/Worker/Department/Card/DepartmentDescriptorProxy.vue'; import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; import { useStateStore } from 'stores/useStateStore'; import { useVnConfirm } from 'composables/useVnConfirm'; @@ -112,23 +112,17 @@ const columns = computed(() => [ }, { align: 'left', - name: 'id', - label: t('weeklyTickets.salesperson'), - columnFilter: { - component: 'select', - alias: 'u', - attrs: { - url: 'Workers/activeWithInheritedRole', - fields: ['id', 'name'], - where: { role: 'salesperson' }, - }, - inWhere: true, + name: 'departmentFk', + label: t('customer.summary.team'), + component: 'select', + attrs: { + url: 'Departments', }, + create: true, columnField: { component: null, }, - cardVisible: true, - format: (row) => row.userName, + format: (row, dashIfEmpty) => dashIfEmpty(row.departmentName), }, { align: 'right', @@ -142,9 +136,9 @@ const columns = computed(() => [ openConfirmationModal( t('You are going to delete this weekly ticket'), t( - 'This ticket will be removed from weekly tickets! Continue anyway?' + 'This ticket will be removed from weekly tickets! Continue anyway?', ), - () => deleteWeekly(row.ticketFk) + () => deleteWeekly(row.ticketFk), ), isPrimary: true, }, @@ -219,10 +213,10 @@ onMounted(async () => { <CustomerDescriptorProxy :id="row.clientFk" /> </span> </template> - <template #column-id="{ row }"> + <template #column-departmentFk="{ row }"> <span class="link" @click.stop> - {{ row.userName }} - <WorkerDescriptorProxy :id="row.workerFk" /> + {{ row.departmentName }} + <DepartmentDescriptorProxy :id="row.departmentFk" /> </span> </template> </VnTable> diff --git a/src/pages/Ticket/locale/en.yml b/src/pages/Ticket/locale/en.yml index cdbb22d9b..9eb8ce8cb 100644 --- a/src/pages/Ticket/locale/en.yml +++ b/src/pages/Ticket/locale/en.yml @@ -136,7 +136,6 @@ purchaseRequest: weeklyTickets: id: Ticket ID shipment: Shipment - salesperson: Salesperson search: Search weekly tickets searchInfo: Search weekly tickets by id or client id ticketSaleTracking: @@ -172,7 +171,7 @@ tracking: addState: Add state package: package: Package - added: Added + added: D. Added addPackage: Add package removePackage: Remove package ticketList: @@ -181,7 +180,6 @@ ticketList: state: State shipped: Shipped zone: Zone - salesPerson: Sales person totalWithVat: Total with VAT summary: Summary client: Customer diff --git a/src/pages/Ticket/locale/es.yml b/src/pages/Ticket/locale/es.yml index 75d3c6a2b..62013d9c5 100644 --- a/src/pages/Ticket/locale/es.yml +++ b/src/pages/Ticket/locale/es.yml @@ -58,7 +58,6 @@ basicData: weeklyTickets: id: ID Ticket shipment: Salida - salesperson: Comercial search: Buscar por tickets programados searchInfo: Buscar tickets programados por el identificador o el identificador del cliente advanceTickets: @@ -162,7 +161,7 @@ expedition: removeExpedition: Eliminar expedición package: package: Embalaje - added: Añadido + added: F. Creacion addPackage: Añadir embalaje removePackage: Quitar embalaje ticketSaleTracking: @@ -186,7 +185,6 @@ ticketList: state: Estado shipped: F. Envío zone: Zona - salesPerson: Comercial totalWithVat: Total con IVA summary: Resumen client: Cliente diff --git a/src/pages/Worker/Card/WorkerDescriptor.vue b/src/pages/Worker/Card/WorkerDescriptor.vue index 0e946f1dd..6e3a5e83f 100644 --- a/src/pages/Worker/Card/WorkerDescriptor.vue +++ b/src/pages/Worker/Card/WorkerDescriptor.vue @@ -23,6 +23,10 @@ const $props = defineProps({ required: false, default: 'Worker', }, + summary: { + type: Object, + default: null, + }, }); const image = ref(null); @@ -51,6 +55,7 @@ const handlePhotoUpdated = (evt = false) => { <CardDescriptor ref="cardDescriptorRef" :data-key="dataKey" + :summary="$props.summary" url="Workers/summary" :filter="{ where: { id: entityId } }" title="user.nickname" @@ -116,7 +121,7 @@ const handlePhotoUpdated = (evt = false) => { :value="entity.user?.emailUser?.email" copy /> - <VnLv :label="t('worker.list.department')"> + <VnLv :label="t('globals.department')"> <template #value> <span class="link" v-text="entity.department?.department?.name" /> <DepartmentDescriptorProxy :id="entity.department?.department?.id" /> diff --git a/src/pages/Worker/Card/WorkerSummary.vue b/src/pages/Worker/Card/WorkerSummary.vue index 78c5dfd82..c486a00c2 100644 --- a/src/pages/Worker/Card/WorkerSummary.vue +++ b/src/pages/Worker/Card/WorkerSummary.vue @@ -50,7 +50,7 @@ onBeforeMount(async () => { <QCard class="vn-one"> <VnTitle :url="basicDataUrl" :text="t('globals.summary.basicData')" /> <VnLv :label="t('globals.name')" :value="worker.user?.nickname" /> - <VnLv :label="t('worker.list.department')"> + <VnLv :label="t('globals.department')"> <template #value> <span class="link" v-text="worker.department?.department?.name" /> <DepartmentDescriptorProxy diff --git a/src/pages/Zone/Card/ZoneLocationsTree.vue b/src/pages/Zone/Card/ZoneLocationsTree.vue index 083436440..d5d7d52b6 100644 --- a/src/pages/Zone/Card/ZoneLocationsTree.vue +++ b/src/pages/Zone/Card/ZoneLocationsTree.vue @@ -73,6 +73,7 @@ const onNodeExpanded = async (nodeKeysArray) => { const response = await axios.get(`Zones/${route.params.id}/getLeaves`, { params, }); + response.data = JSON.parse(response.data); if (response.data) { node.childs = response.data.map((n) => { if (n.sons > 0) n.childs = [{}]; @@ -126,14 +127,17 @@ watch( async (val) => { if (!val) return; // // Se triggerea cuando se actualiza el store.data, el cual es el resultado del fetch de la searchbar + val = JSON.parse(val); if (!nodes.value[0]) nodes.value = [defaultNode]; nodes.value[0].childs = [...val]; const fetchedNodeKeys = val.flatMap(getNodeIds); state.set('Tree', [...fetchedNodeKeys]); expanded.value = [null, ...fetchedNodeKeys]; + const fetchs = []; for (let n of state.get('Tree')) { - await fetchNodeLeaves(n); + fetchs.push(fetchNodeLeaves(n)); } + await Promise.all(fetchs); previousExpandedNodes.value = new Set(expanded.value); }, { immediate: true }, diff --git a/src/router/modules/customer.js b/src/router/modules/customer.js index 67b00b161..a33ed6be5 100644 --- a/src/router/modules/customer.js +++ b/src/router/modules/customer.js @@ -4,8 +4,8 @@ const customerCard = { name: 'CustomerCard', path: ':id', component: () => import('src/pages/Customer/Card/CustomerCard.vue'), - redirect: { name: 'CustomerSummary' }, - meta: { + redirect: { name: 'CustomerSummary' }, + meta: { menu: [ 'CustomerBasicData', 'CustomerFiscalData', @@ -40,8 +40,7 @@ const customerCard = { title: 'basicData', icon: 'vn:settings', }, - component: () => - import('src/pages/Customer/Card/CustomerBasicData.vue'), + component: () => import('src/pages/Customer/Card/CustomerBasicData.vue'), }, { path: 'fiscal-data', @@ -50,8 +49,7 @@ const customerCard = { title: 'fiscalData', icon: 'vn:dfiscales', }, - component: () => - import('src/pages/Customer/Card/CustomerFiscalData.vue'), + component: () => import('src/pages/Customer/Card/CustomerFiscalData.vue'), }, { path: 'billing-data', @@ -60,8 +58,7 @@ const customerCard = { title: 'billingData', icon: 'vn:payment', }, - component: () => - import('src/pages/Customer/Card/CustomerBillingData.vue'), + component: () => import('src/pages/Customer/Card/CustomerBillingData.vue'), }, { path: 'address', @@ -85,9 +82,7 @@ const customerCard = { title: 'address-create', }, component: () => - import( - 'src/pages/Customer/components/CustomerAddressCreate.vue' - ), + import('src/pages/Customer/components/CustomerAddressCreate.vue'), }, { path: ':addressId', @@ -125,8 +120,7 @@ const customerCard = { title: 'credits', icon: 'vn:credit', }, - component: () => - import('src/pages/Customer/Card/CustomerCredits.vue'), + component: () => import('src/pages/Customer/Card/CustomerCredits.vue'), }, { path: 'greuges', @@ -135,8 +129,7 @@ const customerCard = { title: 'greuges', icon: 'vn:greuge', }, - component: () => - import('src/pages/Customer/Card/CustomerGreuges.vue'), + component: () => import('src/pages/Customer/Card/CustomerGreuges.vue'), }, { path: 'balance', @@ -145,8 +138,7 @@ const customerCard = { title: 'balance', icon: 'balance', }, - component: () => - import('src/pages/Customer/Card/CustomerBalance.vue'), + component: () => import('src/pages/Customer/Card/CustomerBalance.vue'), }, { path: 'recoveries', @@ -155,8 +147,7 @@ const customerCard = { title: 'recoveries', icon: 'vn:recovery', }, - component: () => - import('src/pages/Customer/Card/CustomerRecoveries.vue'), + component: () => import('src/pages/Customer/Card/CustomerRecoveries.vue'), }, { path: 'web-access', @@ -165,8 +156,7 @@ const customerCard = { title: 'webAccess', icon: 'vn:web', }, - component: () => - import('src/pages/Customer/Card/CustomerWebAccess.vue'), + component: () => import('src/pages/Customer/Card/CustomerWebAccess.vue'), }, { path: 'log', @@ -247,9 +237,7 @@ const customerCard = { title: 'creditOpinion', }, component: () => - import( - 'src/pages/Customer/Card/CustomerCreditOpinion.vue' - ), + import('src/pages/Customer/Card/CustomerCreditOpinion.vue'), }, ], }, @@ -319,9 +307,7 @@ const customerCard = { title: 'samples', }, component: () => - import( - 'src/pages/Customer/Card/CustomerSamples.vue' - ), + import('src/pages/Customer/Card/CustomerSamples.vue'), }, { path: 'create', @@ -376,9 +362,7 @@ const customerCard = { title: 'fileManagement', }, component: () => - import( - 'src/pages/Customer/Card/CustomerFileManagement.vue' - ), + import('src/pages/Customer/Card/CustomerFileManagement.vue'), }, { path: 'file-management', @@ -420,8 +404,7 @@ const customerCard = { meta: { title: 'unpaid', }, - component: () => - import('src/pages/Customer/Card/CustomerUnpaid.vue'), + component: () => import('src/pages/Customer/Card/CustomerUnpaid.vue'), }, ], }, @@ -429,7 +412,7 @@ const customerCard = { }; export default { - name: 'Customer', + name: 'Customer', path: '/customer', meta: { title: 'customers', @@ -469,15 +452,6 @@ export default { customerCard, ], }, - { - path: 'create', - name: 'CustomerCreate', - meta: { - title: 'customerCreate', - icon: 'add', - }, - component: () => import('src/pages/Customer/CustomerCreate.vue'), - }, { path: 'payments', name: 'CustomerPayments', diff --git a/src/router/modules/route.js b/src/router/modules/route.js index 835324d20..c84795a98 100644 --- a/src/router/modules/route.js +++ b/src/router/modules/route.js @@ -220,7 +220,6 @@ export default { path: '', name: 'RouteIndexMain', redirect: { name: 'RouteList' }, - component: () => import('src/pages/Route/RouteList.vue'), children: [ { name: 'RouteList', @@ -229,6 +228,7 @@ export default { title: 'list', icon: 'view_list', }, + component: () => import('src/pages/Route/RouteList.vue'), }, routeCard, ], @@ -268,7 +268,6 @@ export default { title: 'RouteRoadmap', icon: 'vn:troncales', }, - component: () => import('src/pages/Route/RouteRoadmap.vue'), children: [ { name: 'RoadmapList', @@ -277,6 +276,7 @@ export default { title: 'list', icon: 'view_list', }, + component: () => import('src/pages/Route/RouteRoadmap.vue'), }, roadmapCard, ], @@ -298,7 +298,6 @@ export default { title: 'agency', icon: 'garage_home', }, - component: () => import('src/pages/Route/Agency/AgencyList.vue'), children: [ { name: 'AgencyList', @@ -307,6 +306,8 @@ export default { title: 'list', icon: 'view_list', }, + component: () => + import('src/pages/Route/Agency/AgencyList.vue'), }, agencyCard, ], @@ -319,7 +320,6 @@ export default { title: 'vehicle', icon: 'directions_car', }, - component: () => import('src/pages/Route/Vehicle/VehicleList.vue'), children: [ { path: 'list', @@ -328,6 +328,8 @@ export default { title: 'vehicleList', icon: 'directions_car', }, + component: () => + import('src/pages/Route/Vehicle/VehicleList.vue'), }, vehicleCard, ], diff --git a/test/cypress/integration/claim/claimAction.spec.js b/test/cypress/integration/claim/claimAction.spec.js index b0a16a2ad..8f406ad2f 100644 --- a/test/cypress/integration/claim/claimAction.spec.js +++ b/test/cypress/integration/claim/claimAction.spec.js @@ -1,5 +1,5 @@ /// <reference types="cypress" /> -describe('ClaimAction', () => { +describe.skip('ClaimAction', () => { const claimId = 1; const firstRow = 'tbody > :nth-child(1)'; @@ -15,12 +15,14 @@ describe('ClaimAction', () => { cy.get('[title="Import claim"]').click(); }); - it('should change destination', () => { + // https://redmine.verdnatura.es/issues/8756 + xit('should change destination', () => { const rowData = [true, null, null, 'Bueno']; cy.fillRow(firstRow, rowData); }); - it('should change destination from other button', () => { + // https://redmine.verdnatura.es/issues/8756 + xit('should change destination from other button', () => { const rowData = [true]; cy.fillRow(firstRow, rowData); @@ -33,7 +35,8 @@ describe('ClaimAction', () => { cy.get('[title="Regularize"]').click(); }); - it('should remove the line', () => { + // https://redmine.verdnatura.es/issues/8756 + xit('should remove the line', () => { cy.fillRow(firstRow, [true]); cy.removeCard(); cy.clickConfirm(); diff --git a/test/cypress/integration/claim/claimPhoto.spec.js b/test/cypress/integration/claim/claimPhoto.spec.js index b84b4f958..ac0460029 100755 --- a/test/cypress/integration/claim/claimPhoto.spec.js +++ b/test/cypress/integration/claim/claimPhoto.spec.js @@ -1,5 +1,5 @@ /// <reference types="cypress" /> -describe('ClaimPhoto', () => { +describe.skip('ClaimPhoto', () => { const carrouselClose = '.q-dialog__inner > .q-toolbar > .q-btn > .q-btn__content > .q-icon'; beforeEach(() => { diff --git a/test/cypress/integration/client/clientBalance.spec.js b/test/cypress/integration/client/clientBalance.spec.js index 4579efaa6..fff6a5e04 100644 --- a/test/cypress/integration/client/clientBalance.spec.js +++ b/test/cypress/integration/client/clientBalance.spec.js @@ -1,17 +1,13 @@ /// <reference types="cypress" /> -describe.skip('Client balance', () => { +describe('Client balance', () => { beforeEach(() => { - cy.viewport(1280, 720); cy.login('developer'); cy.visit('#/customer/1101/balance'); }); - it('Should load layout', () => { - cy.get('.q-page').should('be.visible'); - }); it('Should create a mandate', () => { cy.get('.q-page-sticky > div > .q-btn').click(); - cy.dataCy('paymentBank').type({ arroyDown }); - cy.dataCy('paymentAmount').type('100'); + cy.selectOption('[data-cy="paymentBank"]', 2); + cy.dataCy('paymentAmount_input').clear().type('100'); cy.saveCard(); }); }); diff --git a/test/cypress/integration/client/clientList.spec.js b/test/cypress/integration/client/clientList.spec.js index b29ad74af..467c6c37d 100644 --- a/test/cypress/integration/client/clientList.spec.js +++ b/test/cypress/integration/client/clientList.spec.js @@ -1,5 +1,5 @@ /// <reference types="cypress" /> -describe('Client list', () => { +describe.skip('Client list', () => { beforeEach(() => { cy.login('developer'); cy.visit('/#/customer/list', { @@ -25,7 +25,7 @@ describe('Client list', () => { 'Web user': { val: `user_test_${randomInt}` }, Street: { val: `C/ STREET ${randomInt}` }, Email: { val: `user.test${randomInt}@cypress.com` }, - 'Sales person': { val: 'salesPerson', type: 'select' }, + Team: { val: 'Informatica', type: 'select' }, Location: { val: '46000', type: 'select' }, 'Business type': { val: 'others', type: 'select' }, }; diff --git a/test/cypress/integration/invoiceOut/invoiceOutList.spec.js b/test/cypress/integration/invoiceOut/invoiceOutList.spec.js index d3a84d226..b8b42fa4b 100644 --- a/test/cypress/integration/invoiceOut/invoiceOutList.spec.js +++ b/test/cypress/integration/invoiceOut/invoiceOutList.spec.js @@ -13,7 +13,6 @@ describe('InvoiceOut list', () => { ':nth-child(1) > .text-right > [data-cy="tableAction-0"] > .q-btn__content > .q-icon'; beforeEach(() => { - cy.viewport(1920, 1080); cy.login('developer'); cy.visit(`/#/invoice-out/list`); cy.typeSearchbar('{enter}'); @@ -41,7 +40,7 @@ describe('InvoiceOut list', () => { }); it('should filter the results by client ID, then check the first result is correct', () => { - cy.dataCy('Customer ID_input').type('1103'); + cy.dataCy('Client id_input').type('1103'); cy.get(filterBtn).click(); cy.get(firstRowDescriptor).click(); cy.get('.q-item > .q-item__label').should('include.text', '1103'); diff --git a/test/cypress/integration/invoiceOut/invoiceOutNegativeBases.spec.js b/test/cypress/integration/invoiceOut/invoiceOutNegativeBases.spec.js index 0039f6240..9c6eef2ed 100644 --- a/test/cypress/integration/invoiceOut/invoiceOutNegativeBases.spec.js +++ b/test/cypress/integration/invoiceOut/invoiceOutNegativeBases.spec.js @@ -16,9 +16,9 @@ describe('InvoiceOut negative bases', () => { cy.get(getDescriptors('ticketFk')).click(); cy.get('.descriptor').should('be.visible'); cy.get('.q-item > .q-item__label').should('include.text', '23'); - cy.get(getDescriptors('workerName')).click(); + cy.get(getDescriptors('departmentFk')).click(); cy.get('.descriptor').should('be.visible'); - cy.get('.q-item > .q-item__label').should('include.text', '18'); + cy.get('.q-item > .q-item__label').should('include.text', '155'); }); it('should filter and download as CSV', () => { diff --git a/test/cypress/integration/outLogin/login.spec.js b/test/cypress/integration/outLogin/login.spec.js index 2bd5a8c3b..22e30dd8e 100755 --- a/test/cypress/integration/outLogin/login.spec.js +++ b/test/cypress/integration/outLogin/login.spec.js @@ -12,7 +12,7 @@ describe('Login', () => { cy.get('button[type="submit"]').click(); cy.get('.q-notification__message').should( 'have.text', - 'Invalid username or password' + 'Invalid username or password', ); }); @@ -23,7 +23,7 @@ describe('Login', () => { cy.get('button[type="submit"]').click(); cy.get('.q-notification__message').should( 'have.text', - 'Invalid username or password' + 'Invalid username or password', ); }); @@ -33,7 +33,7 @@ describe('Login', () => { cy.get('button[type="submit"]').click(); cy.get('.q-notification__message').should( 'have.text', - 'You have successfully logged in' + 'You have successfully logged in', ); cy.url().should('contain', '/dashboard'); }); @@ -44,7 +44,7 @@ describe('Login', () => { cy.get('button[type="submit"]').click(); cy.get('.q-notification__message').should( 'have.text', - 'You have successfully logged in' + 'You have successfully logged in', ); cy.url().should('contain', '/dashboard'); cy.get('#user').click(); @@ -61,14 +61,4 @@ describe('Login', () => { cy.get('button[type="submit"]').click(); cy.url().should('contain', '/dashboard'); }); - - // ticket creation is not yet implemented, use this test once it is - // it(`should get redirected to ticket creation after login since salesPerson can do it`, () => { - // cy.visit('/#/ticket/create', { failOnStatusCode: false }); - // cy.url().should('contain', '/#/login?redirect=/ticket/create'); - // cy.get('input[aria-label="Username"]').type('salesPerson'); - // cy.get('input[aria-label="Password"]').type('nightmare'); - // cy.get('button[type="submit"]').click(); - // cy.url().should('contain', '/#/ticket/create'); - // }) }); diff --git a/test/cypress/integration/outLogin/logout.spec.js b/test/cypress/integration/outLogin/logout.spec.js index 373f0cc93..b3583e4d3 100644 --- a/test/cypress/integration/outLogin/logout.spec.js +++ b/test/cypress/integration/outLogin/logout.spec.js @@ -24,12 +24,21 @@ describe('Logout', () => { }, }, statusMessage: 'AUTHORIZATION_REQUIRED', - }); + }).as('badRequest'); }); it('when token not exists', () => { + const exceptionHandler = (err) => { + if (err.code === 'AUTHORIZATION_REQUIRED') return; + }; + Cypress.on('uncaught:exception', exceptionHandler); + cy.get('.q-list').first().should('be.visible').click(); + cy.wait('@badRequest'); + cy.checkNotification('Authorization Required'); + + Cypress.off('uncaught:exception', exceptionHandler); }); }); }); diff --git a/test/cypress/integration/route/agency/agencyWorkCenter.spec.js b/test/cypress/integration/route/agency/agencyWorkCenter.spec.js index 5679ceba1..22a1a0143 100644 --- a/test/cypress/integration/route/agency/agencyWorkCenter.spec.js +++ b/test/cypress/integration/route/agency/agencyWorkCenter.spec.js @@ -1,27 +1,34 @@ describe.skip('AgencyWorkCenter', () => { + const selectors = { + workCenter: 'workCenter_select', + popupSave: 'FormModelPopup_save', + popupCancel: 'FormModelPopup_cancel', + remove: 'removeWorkCenterBtn', + }; + + const messages = { + dataCreated: 'Data created', + alreadyAssigned: 'This workCenter is already assigned to this agency', + removed: 'WorkCenter removed successfully', + }; + beforeEach(() => { cy.viewport(1920, 1080); cy.login('developer'); cy.visit(`/#/route/agency/11/workCenter`); }); - const createButton = '.q-page-sticky > div > .q-btn > .q-btn__content > .q-icon'; - const workCenterCombobox = 'input[role="combobox"]'; - it('check workCenter crud', () => { - // create - cy.get(createButton).click(); - cy.get(workCenterCombobox).type('workCenterOne{enter}'); + it('Should add work center, check already assigned and remove work center', () => { + cy.addBtnClick(); + cy.selectOption('[data-cy="workCenter_select"]', 'workCenterOne'); + cy.dataCy(selectors.popupSave).click(); cy.checkNotification('Data created'); - - // expect error when duplicate - cy.get(createButton).click(); - cy.selectOption(workCenterCombobox, 'workCenterOne'); - cy.get('[data-cy="FormModelPopup_save"]').click(); - cy.checkNotification('This workCenter is already assigned to this agency'); - cy.get('[data-cy="FormModelPopup_cancel"]').click(); - - // delete - cy.get('.q-item__section--side > .q-btn > .q-btn__content > .q-icon').click(); - cy.checkNotification('WorkCenter removed successfully'); + cy.addBtnClick(); + cy.selectOption('[data-cy="workCenter_select"]', 'workCenterOne'); + cy.dataCy(selectors.popupSave).click(); + cy.checkNotification(messages.alreadyAssigned); + cy.dataCy(selectors.popupCancel).click(); + cy.dataCy(selectors.remove).click(); + cy.checkNotification(messages.removed); }); }); diff --git a/test/cypress/integration/route/roadMap/roadmapList.spec.js b/test/cypress/integration/route/roadMap/roadmapList.spec.js index 6d46b2cf6..35c0c2b02 100644 --- a/test/cypress/integration/route/roadMap/roadmapList.spec.js +++ b/test/cypress/integration/route/roadMap/roadmapList.spec.js @@ -1,12 +1,74 @@ describe('RoadMap', () => { + const getSelector = (colField) => + `tr:last-child > [data-col-field="${colField}"] > .no-padding`; + + const selectors = { + roadmap: getSelector('name'), + id: getSelector('id'), + etd: getSelector('etd'), + summaryHeader: '.summaryHeader > :nth-child(2)', + summaryGoToSummaryBtn: '.summaryHeader > a > .q-icon', + summaryBtn: 'tableAction-0', + inputRoadmap: 'Roadmap_input', + checkbox: '.q-virtual-scroll__content tr:last-child .q-checkbox', + cloneFormBtn: '.q-card__actions > .q-btn--standard', + cloneBtn: '#subToolbar > :nth-child(3)', + deleteBtn: ':nth-child(4) > .q-btn__content', + confirmBtn: 'VnConfirm_confirm', + inputEtd: 'ETD_inputDate', + }; + + const data = { + roadmap: 'TEST-ROADMAP', + etd: '01/01/2025', + }; + + const dataCreated = 'Data created'; + const summaryUrl = '/summary'; + beforeEach(() => { + cy.viewport(1920, 1080); cy.login('developer'); cy.visit(`/#/route/roadmap`); + cy.typeSearchbar('{enter}'); }); + + it('Should list roadmaps', () => { + cy.get('.q-table') + .children() + .should('be.visible') + .should('have.length.greaterThan', 0); + }); + it('Route list create roadmap and redirect', () => { cy.addBtnClick(); - cy.get('input[name="name"]').type('roadMapTestOne{enter}'); - cy.get('.q-notification__message').should('have.text', 'Data created'); - cy.url().should('include', '/summary'); + cy.dataCy(selectors.inputRoadmap).type(`${data.roadmap}{enter}`); + cy.checkNotification(dataCreated); + cy.url().should('include', summaryUrl); + }); + + it('open summary', () => { + cy.dataCy(selectors.summaryBtn).last().click(); + cy.get(selectors.summaryHeader).should('contain', data.roadmap); + cy.get(selectors.summaryGoToSummaryBtn).click(); + cy.get(selectors.summaryHeader).should('contain', data.roadmap); + }); + + it('Should clone selected roadmap with new ETD', () => { + cy.get(selectors.checkbox).click(); + cy.get(selectors.cloneBtn).click(); + cy.dataCy(selectors.inputEtd).click().type(`${data.etd}{enter}`); + cy.get(selectors.cloneFormBtn).click(); + cy.get(selectors.etd).should('contain', data.etd); + }); + + it('Should delete selected roadmap', () => { + cy.get(selectors.id).then(($el) => { + cy.get(selectors.checkbox).click(); + cy.get(selectors.deleteBtn).click(); + cy.dataCy(selectors.confirmBtn).click(); + cy.typeSearchbar('{enter}'); + cy.get(selectors.id).should('not.have.text', $el.text); + }); }); }); diff --git a/test/cypress/integration/route/routeAutonomous.spec.js b/test/cypress/integration/route/routeAutonomous.spec.js index acf82bd95..08fd7d7ea 100644 --- a/test/cypress/integration/route/routeAutonomous.spec.js +++ b/test/cypress/integration/route/routeAutonomous.spec.js @@ -1,4 +1,4 @@ -describe('RouteAutonomous', () => { +describe.skip('RouteAutonomous', () => { const getLinkSelector = (colField) => `tr:first-child > [data-col-field="${colField}"] > .no-padding > .link`; diff --git a/test/cypress/integration/shelving/parking/parkingList.spec.js b/test/cypress/integration/shelving/parking/parkingList.spec.js index ca1877621..7372da164 100644 --- a/test/cypress/integration/shelving/parking/parkingList.spec.js +++ b/test/cypress/integration/shelving/parking/parkingList.spec.js @@ -2,7 +2,7 @@ describe('ParkingList', () => { const searchbar = '#searchbar input'; const firstCard = ':nth-child(1) > .q-card > .no-margin > .q-py-none'; - const summaryHeader = '.summaryBody .header'; + const summaryHeader = '.header-link'; beforeEach(() => { cy.viewport(1920, 1080); diff --git a/test/cypress/integration/shelving/shelvingList.spec.js b/test/cypress/integration/shelving/shelvingList.spec.js index 745dd1b78..20b72e419 100644 --- a/test/cypress/integration/shelving/shelvingList.spec.js +++ b/test/cypress/integration/shelving/shelvingList.spec.js @@ -16,8 +16,8 @@ describe('ShelvingList', () => { it('should redirect from preview to basic-data', () => { cy.typeSearchbar('{enter}'); cy.dataCy('cardBtn').eq(0).click(); - cy.get('.q-card > .header').click(); - cy.url().should('include', '/shelving/1/basic-data'); + cy.get('.summaryHeader > .header > .q-icon').click(); + cy.url().should('include', '/shelving/1/summary'); }); it('should filter and redirect if only one result', () => { diff --git a/test/cypress/integration/ticket/negative/TicketLackDetail.spec.js b/test/cypress/integration/ticket/negative/TicketLackDetail.spec.js index 7b1932b11..b4997fa69 100644 --- a/test/cypress/integration/ticket/negative/TicketLackDetail.spec.js +++ b/test/cypress/integration/ticket/negative/TicketLackDetail.spec.js @@ -138,8 +138,8 @@ describe.skip('Ticket Lack detail', () => { cy.get('[data-cy="itemProposal"]').click(); cy.wait('@getItemGetSimilar'); }); - describe('Replace item if', () => { - it.skip('Quantity is less than available', () => { + describe.skip('Replace item if', () => { + it('Quantity is less than available', () => { cy.get(':nth-child(1) > .text-right > .q-btn').click(); }); }); diff --git a/test/cypress/integration/ticket/ticketSale.spec.js b/test/cypress/integration/ticket/ticketSale.spec.js index 81ea761c4..6d84f214c 100644 --- a/test/cypress/integration/ticket/ticketSale.spec.js +++ b/test/cypress/integration/ticket/ticketSale.spec.js @@ -1,139 +1,14 @@ /// <reference types="cypress" /> +const firstRow = 'tbody > :nth-child(1)'; describe('TicketSale', () => { - describe.skip('Free ticket #31', () => { - beforeEach(() => { - cy.login('developer'); - cy.viewport(1920, 1080); - cy.visit('/#/ticket/31/sale'); - }); - - const firstRow = 'tbody > :nth-child(1)'; - - const selectFirstRow = () => { - cy.waitForElement(firstRow); - cy.get(firstRow).find('.q-checkbox__inner').click(); - }; - - it('it should add item to basket', () => { - cy.window().then((win) => { - cy.stub(win, 'open').as('windowOpen'); - }); - cy.dataCy('ticketSaleAddToBasketBtn').should('exist'); - cy.dataCy('ticketSaleAddToBasketBtn').click(); - cy.get('@windowOpen').should('be.calledWithMatch', /\/order\/\d+\/catalog/); - }); - - it('should send SMS', () => { - selectFirstRow(); - cy.dataCy('ticketSaleMoreActionsDropdown').click(); - cy.waitForElement('[data-cy="sendShortageSMSItem"]'); - cy.dataCy('sendShortageSMSItem').should('exist'); - cy.dataCy('sendShortageSMSItem').click(); - cy.dataCy('vnSmsDialog').should('exist'); - cy.dataCy('sendSmsBtn').click(); - cy.checkNotification('SMS sent'); - }); - - it('should recalculate price when "Recalculate price" is clicked', () => { - cy.intercept('POST', '**/recalculatePrice').as('recalculatePrice'); - selectFirstRow(); - cy.dataCy('ticketSaleMoreActionsDropdown').click(); - cy.waitForElement('[data-cy="recalculatePriceItem"]'); - cy.dataCy('recalculatePriceItem').should('exist'); - cy.dataCy('recalculatePriceItem').click(); - cy.wait('@recalculatePrice').its('response.statusCode').should('eq', 200); - cy.checkNotification('Data saved'); - }); - - it('should update discount when "Update discount" is clicked', () => { - selectFirstRow(); - cy.dataCy('ticketSaleMoreActionsDropdown').click(); - cy.waitForElement('[data-cy="updateDiscountItem"]'); - cy.dataCy('updateDiscountItem').should('exist'); - cy.dataCy('updateDiscountItem').click(); - cy.waitForElement('[data-cy="ticketSaleDiscountInput"]'); - cy.dataCy('ticketSaleDiscountInput').find('input').focus(); - cy.dataCy('ticketSaleDiscountInput').find('input').type('10'); - cy.dataCy('saveManaBtn').click(); - cy.waitForElement('.q-notification__message'); - cy.checkNotification('Data saved'); - }); - - it('adds claim', () => { - selectFirstRow(); - cy.dataCy('ticketSaleMoreActionsDropdown').click(); - cy.dataCy('createClaimItem').click(); - cy.dataCy('VnConfirm_confirm').click(); - cy.url().should('contain', 'claim/'); - // Delete created claim to avoid cluttering the database - cy.dataCy('descriptor-more-opts').click(); - cy.dataCy('deleteClaim').click(); - cy.dataCy('VnConfirm_confirm').click(); - cy.checkNotification('Data deleted'); - }); - - it('marks row as reserved', () => { - selectFirstRow(); - cy.dataCy('ticketSaleMoreActionsDropdown').click(); - cy.waitForElement('[data-cy="markAsReservedItem"]'); - cy.dataCy('markAsReservedItem').click(); - cy.dataCy('ticketSaleReservedIcon').should('exist'); - }); - - it('unmarks row as reserved', () => { - selectFirstRow(); - cy.dataCy('ticketSaleMoreActionsDropdown').click(); - cy.waitForElement('[data-cy="unmarkAsReservedItem"]'); - cy.dataCy('unmarkAsReservedItem').click(); - cy.dataCy('ticketSaleReservedIcon').should('not.exist'); - }); - - it('refunds row with warehouse', () => { - selectFirstRow(); - cy.dataCy('ticketSaleMoreActionsDropdown').click(); - cy.dataCy('ticketSaleRefundItem').click(); - cy.dataCy('ticketSaleRefundWithWarehouse').click(); - cy.checkNotification('The following refund ticket have been created'); - }); - - it('refunds row without warehouse', () => { - selectFirstRow(); - cy.dataCy('ticketSaleMoreActionsDropdown').click(); - cy.dataCy('ticketSaleRefundItem').click(); - cy.dataCy('ticketSaleRefundWithoutWarehouse').click(); - cy.checkNotification('The following refund ticket have been created'); - }); - - it('transfer sale to a new ticket', () => { - cy.visit('/#/ticket/32/sale'); - cy.get('.q-item > .q-item__label').should('have.text', ' #32'); - selectFirstRow(); - cy.dataCy('ticketSaleTransferBtn').click(); - cy.dataCy('ticketTransferPopup').should('exist'); - cy.dataCy('ticketTransferNewTicketBtn').click(); - cy.get('.q-item > .q-item__label').should('not.have.text', ' #32'); - }); - - it('should redirect to ticket logs', () => { - cy.get(firstRow).find('.q-btn:last').click(); - cy.url().should('match', /\/ticket\/31\/log/); - }); - }); - describe.skip('Ticket prepared #23', () => { + describe('Ticket #23', () => { beforeEach(() => { cy.login('developer'); cy.viewport(1920, 1080); cy.visit('/#/ticket/23/sale'); }); - const firstRow = 'tbody > :nth-child(1)'; - - const selectFirstRow = () => { - cy.waitForElement(firstRow); - cy.get(firstRow).find('.q-checkbox__inner').click(); - }; - it('update price', () => { const price = Number((Math.random() * 99 + 1).toFixed(2)); cy.waitForElement(firstRow); @@ -148,7 +23,7 @@ describe('TicketSale', () => { cy.get('[data-col-field="price"]') .find('.q-btn > .q-btn__content') - .should('have.text', `€${price}`); + .should('contain.text', `€${price}`); }); it('update discount', () => { const discount = Math.floor(Math.random() * 100) + 1; @@ -196,8 +71,128 @@ describe('TicketSale', () => { .should('have.value', `${quantity}`); }); }); -}); + describe('Ticket to add claim #24', () => { + beforeEach(() => { + cy.login('developer'); + cy.viewport(1920, 1080); + cy.visit('/#/ticket/24/sale'); + }); + it('adds claim', () => { + selectFirstRow(); + cy.dataCy('ticketSaleMoreActionsDropdown').click(); + cy.dataCy('createClaimItem').click(); + cy.dataCy('VnConfirm_confirm').click(); + cy.url().should('contain', 'claim/'); + // Delete created claim to avoid cluttering the database + cy.dataCy('descriptor-more-opts').click(); + cy.dataCy('deleteClaim').click(); + cy.dataCy('VnConfirm_confirm').click(); + }); + }); + describe('Free ticket #31', () => { + beforeEach(() => { + cy.login('developer'); + cy.viewport(1920, 1080); + cy.visit('/#/ticket/31/sale'); + }); + + it('it should add item to basket', () => { + cy.window().then((win) => { + cy.stub(win, 'open').as('windowOpen'); + }); + cy.dataCy('ticketSaleAddToBasketBtn').should('exist'); + cy.dataCy('ticketSaleAddToBasketBtn').click(); + cy.get('@windowOpen').should('be.calledWithMatch', /\/order\/\d+\/catalog/); + }); + + it('should send SMS', () => { + selectFirstRow(); + cy.dataCy('ticketSaleMoreActionsDropdown').click(); + cy.waitForElement('[data-cy="sendShortageSMSItem"]'); + cy.dataCy('sendShortageSMSItem').should('exist'); + cy.dataCy('sendShortageSMSItem').click(); + cy.dataCy('vnSmsDialog').should('exist'); + cy.dataCy('sendSmsBtn').click(); + cy.checkNotification('SMS sent'); + }); + + it('should recalculate price when "Recalculate price" is clicked', () => { + cy.intercept('POST', '**/recalculatePrice').as('recalculatePrice'); + selectFirstRow(); + cy.dataCy('ticketSaleMoreActionsDropdown').click(); + cy.waitForElement('[data-cy="recalculatePriceItem"]'); + cy.dataCy('recalculatePriceItem').should('exist'); + cy.dataCy('recalculatePriceItem').click(); + cy.wait('@recalculatePrice').its('response.statusCode').should('eq', 200); + cy.checkNotification('Data saved'); + cy.dataCy('ticketSaleMoreActionsDropdown').should('be.disabled'); + }); + + it('should update discount when "Update discount" is clicked', () => { + selectFirstRow(); + cy.dataCy('ticketSaleMoreActionsDropdown').click(); + cy.waitForElement('[data-cy="updateDiscountItem"]'); + cy.dataCy('updateDiscountItem').should('exist'); + cy.dataCy('updateDiscountItem').click(); + cy.waitForElement('[data-cy="ticketSaleDiscountInput"]'); + cy.dataCy('ticketSaleDiscountInput').find('input').focus(); + cy.dataCy('ticketSaleDiscountInput').find('input').type('10'); + cy.dataCy('saveManaBtn').click(); + cy.waitForElement('.q-notification__message'); + cy.checkNotification('Data saved'); + cy.dataCy('ticketSaleMoreActionsDropdown').should('be.disabled'); + }); + + it('adds claim', () => { + selectFirstRow(); + cy.dataCy('ticketSaleMoreActionsDropdown').click(); + cy.dataCy('createClaimItem').click(); + cy.dataCy('VnConfirm_confirm').click(); + cy.checkNotification('Future ticket date not allowed'); + }); + + it('refunds row with warehouse', () => { + selectFirstRow(); + cy.dataCy('ticketSaleMoreActionsDropdown').click(); + cy.dataCy('ticketSaleRefundItem').click(); + cy.dataCy('ticketSaleRefundWithWarehouse').click(); + cy.checkNotification('The following refund ticket have been created'); + }); + + it('refunds row without warehouse', () => { + selectFirstRow(); + cy.dataCy('ticketSaleMoreActionsDropdown').click(); + cy.dataCy('ticketSaleRefundItem').click(); + cy.dataCy('ticketSaleRefundWithoutWarehouse').click(); + cy.checkNotification('The following refund ticket have been created'); + }); + + it('should redirect to ticket logs', () => { + cy.get(firstRow).find('.q-btn:last').click(); + cy.url().should('match', /\/ticket\/31\/log/); + }); + }); + describe('Ticket to transfer #32', () => { + beforeEach(() => { + cy.login('developer'); + cy.viewport(1920, 1080); + cy.visit('/#/ticket/32/sale'); + }); + it('transfer sale to a new ticket', () => { + cy.get('.q-item > .q-item__label').should('have.text', ' #32'); + selectFirstRow(); + cy.dataCy('ticketSaleTransferBtn').click(); + cy.dataCy('ticketTransferPopup').should('exist'); + cy.dataCy('ticketTransferNewTicketBtn').click(); + cy.get('.q-item > .q-item__label').should('not.have.text', ' #32'); + }); + }); +}); +function selectFirstRow() { + cy.waitForElement(firstRow); + cy.get(firstRow).find('.q-checkbox__inner').click(); +} function handleVnConfirm() { cy.get('[data-cy="VnConfirm_confirm"]').click(); cy.waitForElement('.q-notification__message'); diff --git a/test/cypress/integration/worker/workerBusiness.spec.js b/test/cypress/integration/worker/workerBusiness.spec.js index 1650b66c7..46da28cd6 100644 --- a/test/cypress/integration/worker/workerBusiness.spec.js +++ b/test/cypress/integration/worker/workerBusiness.spec.js @@ -1,4 +1,4 @@ -describe('WorkerBusiness', () => { +describe.skip('WorkerBusiness', () => { const saveBtn = '.q-mt-lg > .q-btn--standard'; const contributionCode = `Representantes de comercio`; const contractType = `INDEFINIDO A TIEMPO COMPLETO`; diff --git a/test/cypress/integration/zone/zoneLocations.spec.js b/test/cypress/integration/zone/zoneLocations.spec.js index cdc2c778b..3a52d276c 100644 --- a/test/cypress/integration/zone/zoneLocations.spec.js +++ b/test/cypress/integration/zone/zoneLocations.spec.js @@ -1,4 +1,4 @@ -describe('ZoneLocations', () => { +describe.skip('ZoneLocations', () => { const data = { Warehouse: { val: 'Warehouse One', type: 'select' }, }; diff --git a/test/cypress/run.sh b/test/cypress/run.sh index 1f506aa57..0f8c59902 100755 --- a/test/cypress/run.sh +++ b/test/cypress/run.sh @@ -1,24 +1,39 @@ #!/bin/bash +salix_dir="${1:-$HOME/Projects/salix}" +salix_dir=$(eval echo "$salix_dir") + +echo "$salix_dir" + +current_dir=$(pwd) + cleanup() { - if [[ -z "$ended" ]]; then - ended=true - docker-compose -p e2e --project-directory . -f test/cypress/docker-compose.yml down -v - fi + docker-compose -p e2e --project-directory . -f test/cypress/docker-compose.yml down -v } trap cleanup SIGINT -#CLEAN +# CLEAN rm -rf test/cypress/screenshots rm -f test/cypress/results/* rm -f test/cypress/reports/* rm -f junit/e2e-*.xml -#RUN +# RUN export CI=true export TZ=Europe/Madrid +# IMAGES +docker build -t registry.verdnatura.es/salix-back:dev -f "$salix_dir/back/Dockerfile" "$salix_dir" +cd "$salix_dir" && npx myt run -t +docker exec vn-database sh -c "rm -rf /mysql-template" +docker exec vn-database sh -c "cp -a /var/lib/mysql /mysql-template" +docker commit vn-database registry.verdnatura.es/salix-db:dev +docker rm -f vn-database +cd "$current_dir" +docker build -f ./docs/Dockerfile.dev -t lilium-dev . +# END IMAGES + docker-compose -p e2e --project-directory . -f test/cypress/docker-compose.yml up -d docker run -it --rm \ diff --git a/test/cypress/support/commands.js b/test/cypress/support/commands.js index d7238f124..7d9b5eeb9 100755 --- a/test/cypress/support/commands.js +++ b/test/cypress/support/commands.js @@ -62,12 +62,7 @@ Cypress.Commands.overwrite('visit', (originalFn, url, options, waitRequest = tru originalFn(url, options); cy.waitUntil(() => cy.document().then((doc) => doc.readyState === 'complete')); cy.waitUntil(() => cy.get('main').should('exist')); - if (waitRequest) - cy.get('body').then(($body) => { - if ($body.find('[data-cy="loading-spinner"]').length) { - cy.get('[data-cy="loading-spinner"]').should('not.be.visible'); - } - }); + if (waitRequest) cy.waitSpinner(); }); Cypress.Commands.add('waitForElement', (element) => { @@ -99,6 +94,14 @@ Cypress.Commands.add('getValue', (selector) => { }); }); +Cypress.Commands.add('waitSpinner', () => { + cy.get('body').then(($body) => { + if ($body.find('[data-cy="loading-spinner"]').length) { + cy.get('[data-cy="loading-spinner"]').should('not.be.visible'); + } + }); +}); + // Fill Inputs Cypress.Commands.add('selectOption', (selector, option, timeout = 2500) => { cy.waitForElement(selector, timeout); @@ -116,6 +119,7 @@ Cypress.Commands.add('selectOption', (selector, option, timeout = 2500) => { function selectItem(selector, option, ariaControl, hasWrite = true) { if (!hasWrite) cy.wait(100); + cy.waitSpinner(); getItems(ariaControl).then((items) => { const matchingItem = items @@ -135,6 +139,7 @@ function getItems(ariaControl, startTime = Cypress._.now(), timeout = 2500) { .should('exist') .find('.q-item') .should('exist') + .should('be.visible') .then(($items) => { if (!$items?.length || $items.first().text().trim() === '') { if (Cypress._.now() - startTime > timeout) { diff --git a/test/cypress/support/index.js b/test/cypress/support/index.js index 87e869b6d..b0f0fb3b1 100644 --- a/test/cypress/support/index.js +++ b/test/cypress/support/index.js @@ -40,6 +40,11 @@ style.innerHTML = ` `; document.head.appendChild(style); +// FIXME: https://redmine.verdnatura.es/issues/8771 +Cypress.on('uncaught:exception', (err) => { + if (err.code === 'ERR_CANCELED') return false; +}); + const waitForApiReady = (url, maxRetries = 20, delay = 1000) => { let retries = 0;