From 7f376c8ea4cfa0b1c7863017e33c5a9457f9491a Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Fri, 19 Jul 2024 14:53:44 +0200 Subject: [PATCH 01/60] fix: refs #7717 fix catalog filter, searchbar redirect and search --- src/pages/Order/{ => Card}/OrderCatalog.vue | 12 ++++++++++++ src/pages/Order/Card/OrderCatalogFilter.vue | 20 +++++++++++++++----- src/pages/Order/{ => Card}/OrderLines.vue | 0 src/pages/Order/{ => Card}/OrderVolume.vue | 0 src/router/modules/order.js | 6 +++--- 5 files changed, 30 insertions(+), 8 deletions(-) rename src/pages/Order/{ => Card}/OrderCatalog.vue (87%) rename src/pages/Order/{ => Card}/OrderLines.vue (100%) rename src/pages/Order/{ => Card}/OrderVolume.vue (100%) diff --git a/src/pages/Order/OrderCatalog.vue b/src/pages/Order/Card/OrderCatalog.vue similarity index 87% rename from src/pages/Order/OrderCatalog.vue rename to src/pages/Order/Card/OrderCatalog.vue index 2cf6e1c29..071827a8b 100644 --- a/src/pages/Order/OrderCatalog.vue +++ b/src/pages/Order/Card/OrderCatalog.vue @@ -6,6 +6,7 @@ import { useI18n } from 'vue-i18n'; import VnPaginate from 'components/ui/VnPaginate.vue'; import CatalogItem from 'components/ui/CatalogItem.vue'; import OrderCatalogFilter from 'pages/Order/Card/OrderCatalogFilter.vue'; +import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; const route = useRoute(); const stateStore = useStateStore(); @@ -52,6 +53,17 @@ function extractValueTags(items) { </script> <template> + <Teleport to="#searchbar"> + <VnSearchbar + data-key="OrderCatalogList" + :user-params="catalogParams" + :static-params="['orderFk', 'orderBy']" + :redirect="false" + url="Orders/CatalogFilter" + :label="t('Search items')" + :info="t('You can search orders by reference')" + /> + </Teleport> <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above> <QScrollArea class="fit text-grey-8"> <OrderCatalogFilter diff --git a/src/pages/Order/Card/OrderCatalogFilter.vue b/src/pages/Order/Card/OrderCatalogFilter.vue index c354ec94b..992fe4b3f 100644 --- a/src/pages/Order/Card/OrderCatalogFilter.vue +++ b/src/pages/Order/Card/OrderCatalogFilter.vue @@ -27,7 +27,6 @@ const props = defineProps({ required: true, }, }); - const categoryList = ref(null); const selectedCategoryFk = ref(null); const typeList = ref(null); @@ -84,7 +83,14 @@ const selectedCategory = computed(() => (category) => category?.id === selectedCategoryFk.value ) ); - +function filterFn(val, update) { + update(() => { + const needle = val.toLowerCase(); + tagOptions.value = props.tagValue.filter( + (v) => v.toLowerCase().indexOf(needle) > -1 + ); + }); +} const selectedType = computed(() => { return (typeList.value || []).find((type) => type?.id === selectedTypeFk.value); }); @@ -352,7 +358,7 @@ const useLang = (values) => { v-if="!selectedTag" :label="t('params.value')" v-model="value.value" - :options="tagValue || []" + :options="tagOptions || []" option-value="value" option-label="value" dense @@ -362,6 +368,8 @@ const useLang = (values) => { use-input class="filter-input" @new-value="createValue" + @filter="filterFn" + @update:model-value="applyTagFilter(params, searchFn)" /> <VnSelect v-else-if="selectedTag === 1" @@ -377,6 +385,7 @@ const useLang = (values) => { use-input class="filter-input" @new-value="createValue" + @update:model-value="applyTagFilter(params, searchFn)" /> <VnInput v-else @@ -386,6 +395,7 @@ const useLang = (values) => { outlined rounded class="filter-input" + @keyup.enter="applyTagFilter(params, searchFn)" /> <QIcon name="delete" @@ -400,7 +410,7 @@ const useLang = (values) => { @click="tagValues.push({})" /> </QItem> - <QItem> + <!-- <QItem> <QItemSection class="q-py-sm"> <QBtn :label="t('Search')" @@ -414,7 +424,7 @@ const useLang = (values) => { @click.stop="applyTagFilter(params, searchFn)" /> </QItemSection> - </QItem> + </QItem> --> <QSeparator /> </template> </VnFilterPanel> diff --git a/src/pages/Order/OrderLines.vue b/src/pages/Order/Card/OrderLines.vue similarity index 100% rename from src/pages/Order/OrderLines.vue rename to src/pages/Order/Card/OrderLines.vue diff --git a/src/pages/Order/OrderVolume.vue b/src/pages/Order/Card/OrderVolume.vue similarity index 100% rename from src/pages/Order/OrderVolume.vue rename to src/pages/Order/Card/OrderVolume.vue diff --git a/src/router/modules/order.js b/src/router/modules/order.js index aa1ca774d..8d1a6e5fe 100644 --- a/src/router/modules/order.js +++ b/src/router/modules/order.js @@ -72,7 +72,7 @@ export default { title: 'catalog', icon: 'vn:basket', }, - component: () => import('src/pages/Order/OrderCatalog.vue'), + component: () => import('src/pages/Order/Card/OrderCatalog.vue'), }, { name: 'OrderVolume', @@ -81,7 +81,7 @@ export default { title: 'volume', icon: 'vn:volume', }, - component: () => import('src/pages/Order/OrderVolume.vue'), + component: () => import('src/pages/Order/Card/OrderVolume.vue'), }, { name: 'OrderLines', @@ -90,7 +90,7 @@ export default { title: 'lines', icon: 'vn:lines', }, - component: () => import('src/pages/Order/OrderLines.vue'), + component: () => import('src/pages/Order/Card/OrderLines.vue'), }, ], }, From 02e1de083a934e1178bef41c67375f844b2e7f7e Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Fri, 19 Jul 2024 15:00:05 +0200 Subject: [PATCH 02/60] fix: duplicate key --- src/i18n/locale/en.yml | 1 - src/i18n/locale/es.yml | 1 - 2 files changed, 2 deletions(-) diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml index 2f1209a3a..6943252c9 100644 --- a/src/i18n/locale/en.yml +++ b/src/i18n/locale/en.yml @@ -246,7 +246,6 @@ globals: mailForwarding: Mail forwarding mailAlias: Mail alias privileges: Privileges - labeler: Labeler created: Created worker: Worker now: Now diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml index c5c4fab66..f32562313 100644 --- a/src/i18n/locale/es.yml +++ b/src/i18n/locale/es.yml @@ -248,7 +248,6 @@ globals: components: Componentes pictures: Fotos packages: Bultos - labeler: Etiquetas created: Fecha creación worker: Trabajador now: Ahora From def3ca33a26619cb0a38b9d050283c9fc7227125 Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Mon, 22 Jul 2024 12:52:58 +0200 Subject: [PATCH 03/60] fix: refs #7717 fixed searchbar filter with rightmenu filters' applied --- src/components/ui/VnSearchbar.vue | 8 +++++--- src/pages/Order/Card/OrderCatalog.vue | 1 + src/pages/Order/OrderList.vue | 8 ++------ 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/src/components/ui/VnSearchbar.vue b/src/components/ui/VnSearchbar.vue index 0c7a8a3f6..6fb204afe 100644 --- a/src/components/ui/VnSearchbar.vue +++ b/src/components/ui/VnSearchbar.vue @@ -100,9 +100,11 @@ onMounted(() => { }); async function search() { - const staticParams = Object.entries(store.userParams).filter( - ([key, value]) => value && (props.staticParams || []).includes(key) - ); + const staticParams = Object.entries(store.userParams); + console.log('staticParams: ', staticParams); + // .filter( + // ([key, value]) => value && (props.staticParams || []).includes(key) + // ); arrayData.reset(['skip', 'page']); if (props.makeFetch) diff --git a/src/pages/Order/Card/OrderCatalog.vue b/src/pages/Order/Card/OrderCatalog.vue index 071827a8b..d1479e3c3 100644 --- a/src/pages/Order/Card/OrderCatalog.vue +++ b/src/pages/Order/Card/OrderCatalog.vue @@ -53,6 +53,7 @@ function extractValueTags(items) { </script> <template> + <!-- TODO Sobreescribir la barra de búsqueda --> <Teleport to="#searchbar"> <VnSearchbar data-key="OrderCatalogList" diff --git a/src/pages/Order/OrderList.vue b/src/pages/Order/OrderList.vue index 945f61f3b..fa82540e6 100644 --- a/src/pages/Order/OrderList.vue +++ b/src/pages/Order/OrderList.vue @@ -3,12 +3,12 @@ import axios from 'axios'; import { useI18n } from 'vue-i18n'; import { computed, ref } from 'vue'; import { dashIfEmpty, toCurrency, toDate } from 'src/filters'; -import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; import OrderSummary from 'pages/Order/Card/OrderSummary.vue'; import { useSummaryDialog } from 'src/composables/useSummaryDialog'; 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 OrderSearchbar from './Card/OrderSearchbar.vue'; const { t } = useI18n(); const { viewSummary } = useSummaryDialog(); @@ -132,11 +132,7 @@ async function fetchClientAddress(id, data) { } </script> <template> - <VnSearchbar - data-key="OrderList" - :label="t('Search order')" - :info="t('You can search orders by reference')" - /> + <OrderSearchbar /> <VnTable ref="tableRef" data-key="OrderList" From 368b8404fa60f9ddca22fa3b8463406d2e5fd004 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Mon, 22 Jul 2024 15:13:03 +0200 Subject: [PATCH 04/60] updates: OrderCatlago --- src/components/common/VnCard.vue | 8 ++++-- src/pages/Order/Card/OrderCard.vue | 32 +++++++++++++++++++-- src/pages/Order/Card/OrderCatalogFilter.vue | 2 +- 3 files changed, 36 insertions(+), 6 deletions(-) diff --git a/src/components/common/VnCard.vue b/src/components/common/VnCard.vue index 17fa74317..4b7a6ba63 100644 --- a/src/components/common/VnCard.vue +++ b/src/components/common/VnCard.vue @@ -31,7 +31,10 @@ const url = computed(() => { if (props.baseUrl) return `${props.baseUrl}/${route.params.id}`; return props.customUrl; }); - +const searchRightDataKey = computed(() => { + if (!props.searchDataKey) return route.name; + else return props.searchDataKey; +}); const arrayData = useArrayData(props.dataKey, { url: url.value, filter: props.filter, @@ -74,10 +77,9 @@ if (props.baseUrl) { :redirect="searchRedirect" /> </slot> - <slot v-else name="searchbar" /> <RightMenu> <template #right-panel v-if="props.filterPanel"> - <component :is="props.filterPanel" :data-key="props.searchDataKey" /> + <component :is="props.filterPanel" :data-key="searchRightDataKey" /> </template> </RightMenu> <QPageContainer> diff --git a/src/pages/Order/Card/OrderCard.vue b/src/pages/Order/Card/OrderCard.vue index 5b6896656..ed4bea2ff 100644 --- a/src/pages/Order/Card/OrderCard.vue +++ b/src/pages/Order/Card/OrderCard.vue @@ -1,16 +1,44 @@ <script setup> +import { useI18n } from 'vue-i18n'; +import { computed } from 'vue'; +import { useRoute } from 'vue-router'; import VnCard from 'components/common/VnCard.vue'; import OrderDescriptor from 'pages/Order/Card/OrderDescriptor.vue'; import OrderFilter from './OrderFilter.vue'; import OrderSearchbar from './OrderSearchbar.vue'; +import OrderCatalogFilter from './OrderCatalogFilter.vue'; +const config = { + OrderCatalog: OrderCatalogFilter, +}; +const route = useRoute(); + +const routeName = computed(() => route.name); +const customRouteRedirectName = computed(() => { + const route = config[routeName.value]; + if (route) return null; + return 'OrderList'; + //Jon, asi es como lo dejamos + // if (routeName.value === 'OrderCatalog') return null; + // return 'OrderList'; +}); +const customFilterPanel = computed(() => { + const filterPanel = config[routeName.value] ?? OrderFilter; + return filterPanel; + //Jon, asi es como lo dejamos + // if (routeName.value === 'OrderCatalog') return null; + // return 'OrderList'; + // if (routeName.value === 'OrderCatalog') return OrderCatalogFilter; + // return OrderFilter; +}); </script> + <template> <VnCard data-key="Order" base-url="Orders" :descriptor="OrderDescriptor" - :filter-panel="OrderFilter" - search-data-key="OrderList" + :filter-panel="customFilterPanel" + :search-data-key="customRouteRedirectName" > <template #searchbar> <OrderSearchbar /> diff --git a/src/pages/Order/Card/OrderCatalogFilter.vue b/src/pages/Order/Card/OrderCatalogFilter.vue index 992fe4b3f..8d96fe93a 100644 --- a/src/pages/Order/Card/OrderCatalogFilter.vue +++ b/src/pages/Order/Card/OrderCatalogFilter.vue @@ -205,7 +205,7 @@ const useLang = (values) => { <VnFilterPanel :data-key="props.dataKey" :hidden-tags="['orderFk', 'orderBy']" - :unremovable-params="['orderFk', 'orderBy']" + :un-removable-params="['orderFk', 'orderBy']" :expr-builder="exprBuilder" :custom-tags="['tagGroups']" @remove="clearFilter" From ce6a94bab8a13341adac336a4e1e7d80e5324e16 Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Tue, 23 Jul 2024 11:46:21 +0200 Subject: [PATCH 05/60] fix: refs #7717 fix catalog searchbar and worker tests(refs #7323) --- src/components/ui/VnFilterPanel.vue | 72 ++++++++++--------- src/components/ui/VnSearchbar.vue | 4 -- .../InvoiceOutNegativeBasesFilter.vue | 2 +- src/pages/Order/Card/OrderCard.vue | 9 --- .../integration/worker/workerList.spec.js | 10 ++- .../integration/worker/workerLocker.spec.js | 7 +- 6 files changed, 49 insertions(+), 55 deletions(-) diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue index 27b6e7b34..f79852a9d 100644 --- a/src/components/ui/VnFilterPanel.vue +++ b/src/components/ui/VnFilterPanel.vue @@ -106,21 +106,23 @@ watch( const isLoading = ref(false); async function search(evt) { - if (evt && $props.disableSubmitEvent) return; + try { + if (evt && $props.disableSubmitEvent) return; - store.filter.where = {}; - isLoading.value = true; - const filter = { ...userParams.value, ...$props.modelValue }; - store.userParamsChanged = true; - const { params: newParams } = await arrayData.addFilter({ - params: filter, - }); - userParams.value = newParams; + store.filter.where = {}; + isLoading.value = true; + const filter = { ...userParams.value, ...$props.modelValue }; + store.userParamsChanged = true; + const { params: newParams } = await arrayData.addFilter({ + params: filter, + }); + userParams.value = newParams; - if (!$props.showAll && !Object.values(filter).length) store.data = []; - - isLoading.value = false; - emit('search'); + if (!$props.showAll && !Object.values(filter).length) store.data = []; + emit('search'); + } finally { + isLoading.value = false; + } } async function reload() { @@ -135,29 +137,31 @@ async function reload() { } async function clearFilters() { - isLoading.value = true; - store.userParamsChanged = true; - arrayData.reset(['skip', 'filter.skip', 'page']); - // Filtrar los params no removibles - const removableFilters = Object.keys(userParams.value).filter((param) => - $props.unRemovableParams.includes(param) - ); - const newParams = {}; - // Conservar solo los params que no son removibles - for (const key of removableFilters) { - newParams[key] = userParams.value[key]; - } - userParams.value = {}; - userParams.value = { ...newParams }; // Actualizar los params con los removibles - await arrayData.applyFilter({ params: userParams.value }); + try { + isLoading.value = true; + store.userParamsChanged = true; + arrayData.reset(['skip', 'filter.skip', 'page']); + // Filtrar los params no removibles + const removableFilters = Object.keys(userParams.value).filter((param) => + $props.unRemovableParams.includes(param) + ); + const newParams = {}; + // Conservar solo los params que no son removibles + for (const key of removableFilters) { + newParams[key] = userParams.value[key]; + } + userParams.value = {}; + userParams.value = { ...newParams }; // Actualizar los params con los removibles + await arrayData.applyFilter({ params: userParams.value }); - if (!$props.showAll) { - store.data = []; + if (!$props.showAll) { + store.data = []; + } + emit('clear'); + emit('update:modelValue', userParams.value); + } finally { + isLoading.value = false; } - - isLoading.value = false; - emit('clear'); - emit('update:modelValue', userParams.value); } const tagsList = computed(() => { diff --git a/src/components/ui/VnSearchbar.vue b/src/components/ui/VnSearchbar.vue index 6fb204afe..694ce294e 100644 --- a/src/components/ui/VnSearchbar.vue +++ b/src/components/ui/VnSearchbar.vue @@ -101,10 +101,6 @@ onMounted(() => { async function search() { const staticParams = Object.entries(store.userParams); - console.log('staticParams: ', staticParams); - // .filter( - // ([key, value]) => value && (props.staticParams || []).includes(key) - // ); arrayData.reset(['skip', 'page']); if (props.makeFetch) diff --git a/src/pages/InvoiceOut/InvoiceOutNegativeBasesFilter.vue b/src/pages/InvoiceOut/InvoiceOutNegativeBasesFilter.vue index 9eeac8355..4865a614a 100644 --- a/src/pages/InvoiceOut/InvoiceOutNegativeBasesFilter.vue +++ b/src/pages/InvoiceOut/InvoiceOutNegativeBasesFilter.vue @@ -19,7 +19,7 @@ const props = defineProps({ <VnFilterPanel :data-key="props.dataKey" :search-button="true" - :unremovable-params="['from', 'to']" + :un-removable-params="['from', 'to']" :hidden-tags="['from', 'to']" > <template #tags="{ tag, formatFn }"> diff --git a/src/pages/Order/Card/OrderCard.vue b/src/pages/Order/Card/OrderCard.vue index ed4bea2ff..67c0f1de5 100644 --- a/src/pages/Order/Card/OrderCard.vue +++ b/src/pages/Order/Card/OrderCard.vue @@ -1,5 +1,4 @@ <script setup> -import { useI18n } from 'vue-i18n'; import { computed } from 'vue'; import { useRoute } from 'vue-router'; import VnCard from 'components/common/VnCard.vue'; @@ -17,18 +16,10 @@ const customRouteRedirectName = computed(() => { const route = config[routeName.value]; if (route) return null; return 'OrderList'; - //Jon, asi es como lo dejamos - // if (routeName.value === 'OrderCatalog') return null; - // return 'OrderList'; }); const customFilterPanel = computed(() => { const filterPanel = config[routeName.value] ?? OrderFilter; return filterPanel; - //Jon, asi es como lo dejamos - // if (routeName.value === 'OrderCatalog') return null; - // return 'OrderList'; - // if (routeName.value === 'OrderCatalog') return OrderCatalogFilter; - // return OrderFilter; }); </script> diff --git a/test/cypress/integration/worker/workerList.spec.js b/test/cypress/integration/worker/workerList.spec.js index 29df01f3b..16f5430de 100644 --- a/test/cypress/integration/worker/workerList.spec.js +++ b/test/cypress/integration/worker/workerList.spec.js @@ -5,8 +5,14 @@ describe('WorkerList', () => { cy.visit('/#/worker/list'); }); - it('should open the worker summary', () => { - cy.get('.bg-header > :nth-child(2) > .full-width > :nth-child(1) >').type( + it('should opern the worker summary', () => { + cy.get( + ':nth-child(1) > .text-right > .q-btn > .q-btn__content > .q-icon' + ).click(); + }); + + it('should go to the worker summary', () => { + cy.get('#searchbar > .q-field > .q-field__inner > .q-field__control').type( 'jessica jones{enter}' ); }); diff --git a/test/cypress/integration/worker/workerLocker.spec.js b/test/cypress/integration/worker/workerLocker.spec.js index ef2f88300..75539fcd1 100644 --- a/test/cypress/integration/worker/workerLocker.spec.js +++ b/test/cypress/integration/worker/workerLocker.spec.js @@ -2,7 +2,6 @@ describe('WorkerLocker', () => { const workerId = 1109; const lockerCode = '2F'; const input = '.q-card input'; - const firstOpt = '[role="listbox"] .q-item:nth-child(1)'; beforeEach(() => { cy.viewport(1280, 720); cy.login('productionBoss'); @@ -11,9 +10,7 @@ describe('WorkerLocker', () => { it('should allocates a locker', () => { cy.get(input).click(); - cy.get(input).type(lockerCode); - cy.get(firstOpt).click(); - cy.saveCard(); - cy.get(input).invoke('val').should('eq', lockerCode); + cy.get(input).type(`${lockerCode}{enter}`); + cy.get(input).should('have.value', lockerCode); }); }); From 1ac0a8596916c920f842516015a7baba5742f6d8 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Tue, 23 Jul 2024 15:09:42 +0200 Subject: [PATCH 06/60] updates --- src/components/ui/VnFilterPanel.vue | 4 ++-- src/pages/Order/Card/OrderCatalog.vue | 22 ++++++++++----------- src/pages/Order/Card/OrderCatalogFilter.vue | 12 +++++++++-- 3 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue index f79852a9d..2dce3228c 100644 --- a/src/components/ui/VnFilterPanel.vue +++ b/src/components/ui/VnFilterPanel.vue @@ -95,8 +95,8 @@ watch( ); watch( - () => arrayData.store.userParams, - (val) => setUserParams(val) + () => [store.userParams, store.userFilter], + ([userParams, userFilter]) => setUserParams({ ...userParams, filter: userFilter }) ); watch( diff --git a/src/pages/Order/Card/OrderCatalog.vue b/src/pages/Order/Card/OrderCatalog.vue index d1479e3c3..cb3e9a8eb 100644 --- a/src/pages/Order/Card/OrderCatalog.vue +++ b/src/pages/Order/Card/OrderCatalog.vue @@ -54,17 +54,17 @@ function extractValueTags(items) { <template> <!-- TODO Sobreescribir la barra de búsqueda --> - <Teleport to="#searchbar"> - <VnSearchbar - data-key="OrderCatalogList" - :user-params="catalogParams" - :static-params="['orderFk', 'orderBy']" - :redirect="false" - url="Orders/CatalogFilter" - :label="t('Search items')" - :info="t('You can search orders by reference')" - /> - </Teleport> + <!-- <Teleport to="#searchbar"> --> + <VnSearchbar + data-key="OrderCatalogList" + :user-params="catalogParams" + :static-params="['orderFk', 'orderBy']" + :redirect="false" + url="Orders/CatalogFilter" + :label="t('Search items')" + :info="t('You can search orders by reference')" + /> + <!-- </Teleport> --> <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above> <QScrollArea class="fit text-grey-8"> <OrderCatalogFilter diff --git a/src/pages/Order/Card/OrderCatalogFilter.vue b/src/pages/Order/Card/OrderCatalogFilter.vue index 8d96fe93a..d9447e530 100644 --- a/src/pages/Order/Card/OrderCatalogFilter.vue +++ b/src/pages/Order/Card/OrderCatalogFilter.vue @@ -29,7 +29,7 @@ const props = defineProps({ }); const categoryList = ref(null); const selectedCategoryFk = ref(null); -const typeList = ref(null); +const typeList = ref([]); const selectedTypeFk = ref(null); const validationsStore = useValidator(); const selectedOrder = ref(null); @@ -165,7 +165,8 @@ const onOrderFieldChange = (value, params) => { break; } }; - +const getCategory = (fk) => + (categoryList.value || []).find((category) => category?.id === fk); const _moreFields = ['ASC', 'DESC']; const _moreFieldsTypes = ['Relevancy', 'ColorAndPrice', 'Name', 'Price']; const setCategoryList = (data) => { @@ -177,6 +178,13 @@ const setCategoryList = (data) => { })); moreFields.value = useLang(_moreFields); moreFieldsOrder.value = useLang(_moreFieldsTypes); + const { categoryFk } = JSON.parse(JSON.parse(route.query.params).filter).where; + + if (!selectedCategoryFk.value) { + selectedCategoryFk.value = categoryFk; + selectedCategory.value = getCategory(categoryFk); + loadTypes(categoryFk); + } }; const getCategoryClass = (category, params) => { From e013e5571dfad469af9bbd657d229a9ee9579989 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Tue, 23 Jul 2024 20:18:53 +0200 Subject: [PATCH 07/60] perf: use ref at component start --- src/pages/Order/Card/OrderCatalogFilter.vue | 41 +++++++++++++++++---- 1 file changed, 34 insertions(+), 7 deletions(-) diff --git a/src/pages/Order/Card/OrderCatalogFilter.vue b/src/pages/Order/Card/OrderCatalogFilter.vue index d9447e530..62095bf1a 100644 --- a/src/pages/Order/Card/OrderCatalogFilter.vue +++ b/src/pages/Order/Card/OrderCatalogFilter.vue @@ -28,7 +28,34 @@ const props = defineProps({ }, }); const categoryList = ref(null); -const selectedCategoryFk = ref(null); +const params = JSON.parse(route?.query?.params ?? '{}'); +const filter = JSON.parse(params?.filter ?? '{}'); +const selectedCategoryFk = ref(filter?.where?.categoryFk ?? null); +// CHATGPT +/** + // Función para parsear JSON de manera segura +const parseJSON = (str, fallback) => { + try { + return JSON.parse(str); + } catch (e) { + console.error("Error parsing JSON:", e); + return fallback; + } +}; + +// Obtener los parámetros de la ruta +const params = parseJSON(route?.query?.params, {}); + +// Extraer y parsear el filtro de los parámetros +const { filter: filterStr = '{}' } = params; +const filter = parseJSON(filterStr, {}); + +// Obtener el categoryFk del filtro, si existe +const selectedCategoryFk = ref(filter?.where?.categoryFk ?? null); + +console.log(selectedCategoryFk.value); + + */ const typeList = ref([]); const selectedTypeFk = ref(null); const validationsStore = useValidator(); @@ -178,13 +205,13 @@ const setCategoryList = (data) => { })); moreFields.value = useLang(_moreFields); moreFieldsOrder.value = useLang(_moreFieldsTypes); - const { categoryFk } = JSON.parse(JSON.parse(route.query.params).filter).where; + // const { categoryFk } = JSON.parse(JSON.parse(route.query.params).filter).where; - if (!selectedCategoryFk.value) { - selectedCategoryFk.value = categoryFk; - selectedCategory.value = getCategory(categoryFk); - loadTypes(categoryFk); - } + // if (!selectedCategoryFk.value) { + // selectedCategoryFk.value = categoryFk; + // selectedCategory.value = getCategory(categoryFk); + // loadTypes(categoryFk); + // } }; const getCategoryClass = (category, params) => { From cc518d5a80d4734a2a7c750f07032796e74b5f33 Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Wed, 24 Jul 2024 10:33:22 +0200 Subject: [PATCH 08/60] perf: refs #7717 right menu filter --- src/filters/getParamWhere.js | 21 +++++++++++ src/filters/index.js | 2 + src/pages/Order/Card/OrderCatalog.vue | 3 -- src/pages/Order/Card/OrderCatalogFilter.vue | 41 ++------------------- 4 files changed, 27 insertions(+), 40 deletions(-) create mode 100644 src/filters/getParamWhere.js diff --git a/src/filters/getParamWhere.js b/src/filters/getParamWhere.js new file mode 100644 index 000000000..48cd9c479 --- /dev/null +++ b/src/filters/getParamWhere.js @@ -0,0 +1,21 @@ +// parsing JSON safely +function parseJSON(str, fallback) { + try { + return JSON.parse(str ?? '{}'); + } catch (e) { + console.error('Error parsing JSON:', e); + return fallback; + } +} +export default function (route, param) { + // catch route query params + const params = parseJSON(route?.query?.params, {}); + + // extract and parse filter from params + const { filter: filterStr = '{}' } = params; + const where = parseJSON(filterStr, {})?.where; + if (where && where[param] !== undefined) { + return where[param]; + } + return null; +} diff --git a/src/filters/index.js b/src/filters/index.js index 940788ed1..5f08f19c7 100644 --- a/src/filters/index.js +++ b/src/filters/index.js @@ -11,6 +11,7 @@ import dashIfEmpty from './dashIfEmpty'; import dateRange from './dateRange'; import toHour from './toHour'; import dashOrCurrency from './dashOrCurrency'; +import getParamWhere from './getParamWhere'; export { toLowerCase, @@ -26,4 +27,5 @@ export { toPercentage, dashIfEmpty, dateRange, + getParamWhere, }; diff --git a/src/pages/Order/Card/OrderCatalog.vue b/src/pages/Order/Card/OrderCatalog.vue index cb3e9a8eb..dc7115422 100644 --- a/src/pages/Order/Card/OrderCatalog.vue +++ b/src/pages/Order/Card/OrderCatalog.vue @@ -53,8 +53,6 @@ function extractValueTags(items) { </script> <template> - <!-- TODO Sobreescribir la barra de búsqueda --> - <!-- <Teleport to="#searchbar"> --> <VnSearchbar data-key="OrderCatalogList" :user-params="catalogParams" @@ -64,7 +62,6 @@ function extractValueTags(items) { :label="t('Search items')" :info="t('You can search orders by reference')" /> - <!-- </Teleport> --> <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above> <QScrollArea class="fit text-grey-8"> <OrderCatalogFilter diff --git a/src/pages/Order/Card/OrderCatalogFilter.vue b/src/pages/Order/Card/OrderCatalogFilter.vue index 62095bf1a..85476b438 100644 --- a/src/pages/Order/Card/OrderCatalogFilter.vue +++ b/src/pages/Order/Card/OrderCatalogFilter.vue @@ -9,6 +9,7 @@ import VnSelect from 'components/common/VnSelect.vue'; import VnFilterPanelChip from 'components/ui/VnFilterPanelChip.vue'; import { useValidator } from 'src/composables/useValidator'; import VnInput from 'src/components/common/VnInput.vue'; +import getParamWhere from 'src/filters/getParamWhere'; const { t } = useI18n(); @@ -28,34 +29,7 @@ const props = defineProps({ }, }); const categoryList = ref(null); -const params = JSON.parse(route?.query?.params ?? '{}'); -const filter = JSON.parse(params?.filter ?? '{}'); -const selectedCategoryFk = ref(filter?.where?.categoryFk ?? null); -// CHATGPT -/** - // Función para parsear JSON de manera segura -const parseJSON = (str, fallback) => { - try { - return JSON.parse(str); - } catch (e) { - console.error("Error parsing JSON:", e); - return fallback; - } -}; - -// Obtener los parámetros de la ruta -const params = parseJSON(route?.query?.params, {}); - -// Extraer y parsear el filtro de los parámetros -const { filter: filterStr = '{}' } = params; -const filter = parseJSON(filterStr, {}); - -// Obtener el categoryFk del filtro, si existe -const selectedCategoryFk = ref(filter?.where?.categoryFk ?? null); - -console.log(selectedCategoryFk.value); - - */ +const selectedCategoryFk = ref(getParamWhere(route, 'categoryFk')); const typeList = ref([]); const selectedTypeFk = ref(null); const validationsStore = useValidator(); @@ -98,7 +72,7 @@ const selectCategory = (params, category, search) => { search(); }; -const loadTypes = async (categoryFk) => { +const loadTypes = async (categoryFk = selectedCategoryFk.value) => { const { data } = await axios.get(`Orders/${route.params.id}/getItemTypeAvailable`, { params: { itemCategoryId: categoryFk }, }); @@ -192,8 +166,6 @@ const onOrderFieldChange = (value, params) => { break; } }; -const getCategory = (fk) => - (categoryList.value || []).find((category) => category?.id === fk); const _moreFields = ['ASC', 'DESC']; const _moreFieldsTypes = ['Relevancy', 'ColorAndPrice', 'Name', 'Price']; const setCategoryList = (data) => { @@ -205,13 +177,8 @@ const setCategoryList = (data) => { })); moreFields.value = useLang(_moreFields); moreFieldsOrder.value = useLang(_moreFieldsTypes); - // const { categoryFk } = JSON.parse(JSON.parse(route.query.params).filter).where; - // if (!selectedCategoryFk.value) { - // selectedCategoryFk.value = categoryFk; - // selectedCategory.value = getCategory(categoryFk); - // loadTypes(categoryFk); - // } + selectedCategoryFk.value && loadTypes(); }; const getCategoryClass = (category, params) => { From d588db05ac9b5e6adb97ddc01a5d98f91086f434 Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Fri, 26 Jul 2024 14:09:09 +0200 Subject: [PATCH 09/60] fix: refs #7717 fix OrderList table filters' and summary table style --- src/pages/Order/Card/OrderSummary.vue | 37 ++++++++++---------------- src/pages/Order/OrderList.vue | 38 ++++++++++++++++++--------- 2 files changed, 40 insertions(+), 35 deletions(-) diff --git a/src/pages/Order/Card/OrderSummary.vue b/src/pages/Order/Card/OrderSummary.vue index cb708fb1d..09c47d4a7 100644 --- a/src/pages/Order/Card/OrderSummary.vue +++ b/src/pages/Order/Card/OrderSummary.vue @@ -180,15 +180,17 @@ const detailsColumns = ref([ <ItemDescriptorProxy :id="props.row.item?.id" /> </span> </QTd> - <QTd key="description" :props="props" class="description"> - <div class="name"> - <span>{{ props.row.item.name }}</span> - <span - v-if="props.row.item.subName" - class="subName" - > - {{ props.row.item.subName }} - </span> + <QTd key="description" :props="props"> + <div class="description"> + <div class="name"> + {{ props.row.item.name }} + <span + v-if="props.row.item.subName" + class="subName" + > + {{ props.row.item.subName }} + </span> + </div> </div> <FetchedTags :item="props.row.item" :max-length="5" /> </QTd> @@ -228,24 +230,13 @@ const detailsColumns = ref([ } .description { - display: flex; - flex-direction: column; - justify-content: center; text-align: left; - height: auto; - padding-top: 12px; - padding-bottom: 12px; + padding-top: 15px; + padding-bottom: 15px; .name { - display: flex; - align-items: center; - padding-bottom: 8px; - - & > * { - flex: 1; - } - .subName { + margin-left: 5%; text-transform: uppercase; color: var(--vn-label-color); } diff --git a/src/pages/Order/OrderList.vue b/src/pages/Order/OrderList.vue index fa82540e6..e0261fc19 100644 --- a/src/pages/Order/OrderList.vue +++ b/src/pages/Order/OrderList.vue @@ -29,7 +29,7 @@ const columns = computed(() => [ }, { align: 'left', - name: 'clientName', + name: 'clientFk', label: t('module.customer'), isTitle: true, cardVisible: true, @@ -41,20 +41,26 @@ const columns = computed(() => [ columnField: { component: null, }, + format: (row) => row?.clientName, }, { align: 'left', - name: 'name', + name: 'salesPersonFk', label: t('module.salesPerson'), - component: 'select', - attrs: { - url: 'Workers/activeWithInheritedRole', - fields: ['id', 'name'], - where: { role: 'salesPerson' }, - }, - columnField: { - component: null, + columnFilter: { + component: 'select', + inWhere: true, + attrs: { + url: 'Workers/activeWithInheritedRole', + fields: ['id', 'name'], + where: { role: 'salesPerson' }, + useLike: false, + optionValue: 'id', + optionLabel: 'name', + optionFilter: 'firstName', + }, }, + format: (row) => row?.name, }, { align: 'left', @@ -92,22 +98,30 @@ const columns = computed(() => [ }, { align: 'left', - name: 'agencyName', + name: 'agencyModeFk', label: t('module.agency'), component: 'select', cardVisible: true, attrs: { - url: 'Agencies', + url: 'agencyModes', fields: ['id', 'name'], + find: { + value: 'agencyModeFk', + label: 'agencyName', + }, }, columnField: { component: null, }, + format: (row) => row?.agencyName, }, { align: 'left', name: 'total', label: t('module.total'), + columnFilter: { + inWhere: true, + }, format: ({ total }) => toCurrency(total), cardVisible: true, }, From 4d7fe0fd864f816e733aea6ea7c9aaa779d0225c Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Fri, 26 Jul 2024 14:56:26 +0200 Subject: [PATCH 10/60] fix: refs #7717 fix volume and lines redirect --- src/pages/Order/Card/OrderLines.vue | 13 ++++++++++- src/pages/Order/Card/OrderVolume.vue | 32 +++++++++++++++++++++------- 2 files changed, 36 insertions(+), 9 deletions(-) diff --git a/src/pages/Order/Card/OrderLines.vue b/src/pages/Order/Card/OrderLines.vue index f361d9898..391a4e374 100644 --- a/src/pages/Order/Card/OrderLines.vue +++ b/src/pages/Order/Card/OrderLines.vue @@ -1,8 +1,9 @@ <script setup> import { useRoute } from 'vue-router'; import { useI18n } from 'vue-i18n'; -import { ref, computed } from 'vue'; +import { ref, computed, watch } from 'vue'; import { useQuasar } from 'quasar'; +import { useRouter } from 'vue-router'; import VnConfirm from 'components/ui/VnConfirm.vue'; import { toCurrency, toDate } from 'src/filters'; @@ -14,6 +15,7 @@ import VnLv from 'src/components/ui/VnLv.vue'; import FetchedTags from 'src/components/ui/FetchedTags.vue'; import { useStateStore } from 'stores/useStateStore'; +const router = useRouter(); const stateStore = useStateStore(); const route = useRoute(); const { t } = useI18n(); @@ -194,6 +196,15 @@ async function confirmOrder() { type: 'positive', }); } + +watch( + () => router.currentRoute.value.params.id, + () => { + lineFilter.value.where.orderFk = router.currentRoute.value.params.id; + + tableLinesRef.value.reload(); + } +); </script> <template> diff --git a/src/pages/Order/Card/OrderVolume.vue b/src/pages/Order/Card/OrderVolume.vue index 35d68de16..62aa08bc3 100644 --- a/src/pages/Order/Card/OrderVolume.vue +++ b/src/pages/Order/Card/OrderVolume.vue @@ -1,7 +1,9 @@ <script setup> +import axios from 'axios'; import { useRoute } from 'vue-router'; import { useI18n } from 'vue-i18n'; -import { ref } from 'vue'; +import { ref, watch } from 'vue'; +import { useRouter } from 'vue-router'; import VnPaginate from 'components/ui/VnPaginate.vue'; import FetchData from 'components/FetchData.vue'; @@ -10,11 +12,20 @@ import CardList from 'components/ui/CardList.vue'; import FetchedTags from 'components/ui/FetchedTags.vue'; import { dashIfEmpty } from 'src/filters'; -import axios from 'axios'; +const router = useRouter(); const route = useRoute(); const { t } = useI18n(); const volumeSummary = ref(null); +const volumeRef = ref(); +const volumeFilter = ref({ + include: [ + { + relation: 'item', + }, + ], + where: { orderFk: route.params.id }, +}); const loadVolumes = async (rows) => { const { data } = await axios.get(`Orders/${route.params.id}/getVolumes`); @@ -26,6 +37,15 @@ const loadVolumes = async (rows) => { }); }); }; + +watch( + () => router.currentRoute.value.params.id, + () => { + volumeFilter.value.where.orderFk = router.currentRoute.value.params.id; + + volumeRef.value.fetch(); + } +); </script> <template> @@ -54,15 +74,11 @@ const loadVolumes = async (rows) => { </QCard> <VnPaginate data-key="OrderCatalogVolume" + ref="volumeRef" url="OrderRows" :limit="20" auto-load - :filter="{ - include: { - relation: 'item', - }, - where: { orderFk: route.params.id }, - }" + :user-filter="volumeFilter" order="itemFk" @on-fetch="(data) => loadVolumes(data)" > From 7e3bfce7320beb2bdba80d8cefbd37c71aa5c4d5 Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Tue, 30 Jul 2024 13:31:12 +0200 Subject: [PATCH 11/60] fix: refs #7717 fix basic data form & minor errors --- src/pages/Order/Card/OrderForm.vue | 9 ++------- src/pages/Order/Card/OrderLines.vue | 5 ++++- src/pages/Order/Card/OrderVolume.vue | 2 +- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/src/pages/Order/Card/OrderForm.vue b/src/pages/Order/Card/OrderForm.vue index f447950d2..91640101c 100644 --- a/src/pages/Order/Card/OrderForm.vue +++ b/src/pages/Order/Card/OrderForm.vue @@ -1,5 +1,5 @@ <script setup> -import { useRoute, useRouter } from 'vue-router'; +import { useRoute } from 'vue-router'; import { ref } from 'vue'; import { useI18n } from 'vue-i18n'; import axios from 'axios'; @@ -15,7 +15,6 @@ const route = useRoute(); const state = useState(); const ORDER_MODEL = 'order'; -const router = useRouter(); const isNew = Boolean(!route.params.id); const clientList = ref([]); const agencyList = ref([]); @@ -106,10 +105,6 @@ const onClientChange = async (clientId) => { console.error('Error al cambiar el cliente:', error); } }; - -async function onDataSaved({ id }) { - await router.push({ path: `/order/${id}/catalog` }); -} </script> <template> @@ -117,7 +112,7 @@ async function onDataSaved({ id }) { <div class="q-pa-md"> <FormModel :url="`Orders/${route.params.id}`" - @on-data-saved="onDataSaved" + :url-update="`Orders/${route.params.id}/updateBasicData`" :model="ORDER_MODEL" :mapper="orderMapper" :filter="orderFilter" diff --git a/src/pages/Order/Card/OrderLines.vue b/src/pages/Order/Card/OrderLines.vue index 391a4e374..9165cfd67 100644 --- a/src/pages/Order/Card/OrderLines.vue +++ b/src/pages/Order/Card/OrderLines.vue @@ -106,6 +106,7 @@ const columns = computed(() => [ component: null, }, format: (row) => row?.item?.name, + columnClass: 'expand', }, { align: 'left', @@ -149,7 +150,6 @@ const columns = computed(() => [ align: 'left', name: 'amount', label: t('lines.amount'), - format: (row) => toCurrency(row.amount), }, { align: 'right', @@ -278,6 +278,9 @@ watch( </div> <FetchedTags :item="row?.item" :max-length="6" /> </template> + <template #column-amount="{ row }"> + {{ toCurrency(row.quantity * row.price) }} + </template> </VnTable> </div> <QPageSticky :offset="[20, 20]" v-if="!order?.isConfirmed" style="z-index: 2"> diff --git a/src/pages/Order/Card/OrderVolume.vue b/src/pages/Order/Card/OrderVolume.vue index 62aa08bc3..69f0a9c6b 100644 --- a/src/pages/Order/Card/OrderVolume.vue +++ b/src/pages/Order/Card/OrderVolume.vue @@ -166,7 +166,7 @@ es: total: Total boxes: Cajas item: Artículo - subName: Subname + subName: Subnombre quantity: Cantidad volume: m³ por cantidad </i18n> From 68ce5880dd7f1a82aa206ca44bbf1938fe5563e0 Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Wed, 31 Jul 2024 07:35:59 +0200 Subject: [PATCH 12/60] refactor: refs #7717 deleted useless code --- src/components/common/VnCard.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/common/VnCard.vue b/src/components/common/VnCard.vue index 4b7a6ba63..8470fdff0 100644 --- a/src/components/common/VnCard.vue +++ b/src/components/common/VnCard.vue @@ -33,7 +33,7 @@ const url = computed(() => { }); const searchRightDataKey = computed(() => { if (!props.searchDataKey) return route.name; - else return props.searchDataKey; + return props.searchDataKey; }); const arrayData = useArrayData(props.dataKey, { url: url.value, From 6b69a1612222cf94b65e99724a4943e2af991489 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Wed, 7 Aug 2024 12:22:52 +0200 Subject: [PATCH 13/60] refs #7355 changes BasicData --- src/i18n/locale/es.yml | 2 +- src/pages/Account/AccountList.vue | 9 ++++++++- src/pages/Account/Card/AccountBasicData.vue | 4 +++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml index 0266bd208..708084d45 100644 --- a/src/i18n/locale/es.yml +++ b/src/i18n/locale/es.yml @@ -873,7 +873,7 @@ worker: card: workerId: ID Trabajador name: Nombre - email: Email + email: Correo personal phone: Teléfono mobile: Móvil active: Activo diff --git a/src/pages/Account/AccountList.vue b/src/pages/Account/AccountList.vue index ecb027e49..d789fdf84 100644 --- a/src/pages/Account/AccountList.vue +++ b/src/pages/Account/AccountList.vue @@ -48,6 +48,14 @@ const columns = computed(() => [ cardVisible: true, create: true, }, + { + align: 'left', + name: 'email', + label: t('email'), + component: 'input', + create: true, + visible: false, + }, { align: 'right', label: '', @@ -96,7 +104,6 @@ const exprBuilder = (param, value) => { order="id DESC" :columns="columns" default-mode="table" - auto-load redirect="account" :use-model="true" /> diff --git a/src/pages/Account/Card/AccountBasicData.vue b/src/pages/Account/Card/AccountBasicData.vue index 42b77419f..f38299f9e 100644 --- a/src/pages/Account/Card/AccountBasicData.vue +++ b/src/pages/Account/Card/AccountBasicData.vue @@ -37,9 +37,11 @@ watch( <VnInput v-model="data.nickname" :label="t('account.card.alias')" /> <VnInput v-model="data.email" :label="t('account.card.email')" /> <VnSelect + url="Languages" v-model="data.lang" - :options="['es', 'en']" :label="t('account.card.lang')" + option-value="code" + option-label="code" /> </div> </template> From 3eaa5c864d45c28ada6322ef5d333b8588260a95 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Thu, 8 Aug 2024 13:57:01 +0200 Subject: [PATCH 14/60] refs #7355 account fixes --- src/components/ui/VnFilterPanel.vue | 8 ++-- src/pages/Account/AccountAcls.vue | 26 ++++++++-- src/pages/Account/Card/AccountCard.vue | 5 +- .../Account/Card/AccountDescriptorMenu.vue | 48 +++++++++++++++++++ src/pages/Account/Card/AccountPrivileges.vue | 9 +--- src/pages/Account/Role/AccountRoles.vue | 17 ++++++- src/pages/Account/Role/Card/RoleBasicData.vue | 5 -- src/router/modules/account.js | 18 +++---- 8 files changed, 102 insertions(+), 34 deletions(-) diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue index 430383e40..ebae673a2 100644 --- a/src/components/ui/VnFilterPanel.vue +++ b/src/components/ui/VnFilterPanel.vue @@ -37,7 +37,7 @@ const $props = defineProps({ }, hiddenTags: { type: Array, - default: () => ['filter'], + default: () => ['filter', 'search', 'or', 'and'], }, customTags: { type: Array, @@ -195,8 +195,10 @@ function formatValue(value) { function sanitizer(params) { for (const [key, value] of Object.entries(params)) { - if (typeof value == 'object') - params[key] = Object.values(value)[0].replaceAll('%', ''); + if (typeof value == 'object') { + const param = Object.values(value)[0]; + if (typeof param == 'string') params[key] = param.replaceAll('%', ''); + } } return params; } diff --git a/src/pages/Account/AccountAcls.vue b/src/pages/Account/AccountAcls.vue index ad8600786..3a6679956 100644 --- a/src/pages/Account/AccountAcls.vue +++ b/src/pages/Account/AccountAcls.vue @@ -41,14 +41,12 @@ const columns = computed(() => [ name: 'id', label: t('id'), isId: true, - field: 'id', cardVisible: true, }, { align: 'left', name: 'model', label: t('model'), - field: 'model', cardVisible: true, create: true, }, @@ -56,15 +54,19 @@ const columns = computed(() => [ align: 'left', name: 'principalId', label: t('principalId'), - field: 'principalId', cardVisible: true, + component: 'select', + attrs: { + url: 'VnRoles', + optionLabel: 'name', + optionValue: 'name', + }, create: true, }, { align: 'left', name: 'property', label: t('property'), - field: 'property', cardVisible: true, create: true, }, @@ -72,7 +74,10 @@ const columns = computed(() => [ align: 'left', name: 'accessType', label: t('accessType'), - field: 'accessType', + component: 'select', + attrs: { + options: ['READ', 'WRITE', '*'], + }, cardVisible: true, create: true, }, @@ -162,4 +167,15 @@ es: Are you sure you want to continue?: ¿Seguro que quieres continuar? Remove ACL: Eliminar Acl Do you want to remove this ACL?: ¿Quieres eliminar este ACL? + principalId: Rol + model: Modelo +en: + New ACL: New ACL + ACL removed: ACL removed + ACL will be removed: ACL will be removed + Are you sure you want to continue?: Are you sure you want to continue? + Remove ACL: Remove ACL + Do you want to remove this ACL?: Do you want to remove this ACL? + principalId: Rol + model: Models </i18n> diff --git a/src/pages/Account/Card/AccountCard.vue b/src/pages/Account/Card/AccountCard.vue index e4db3ee2b..555ce0866 100644 --- a/src/pages/Account/Card/AccountCard.vue +++ b/src/pages/Account/Card/AccountCard.vue @@ -25,9 +25,8 @@ const searchBarDataKeys = { <VnCard data-key="Account" :descriptor="AccountDescriptor" - :search-data-key="searchBarDataKeys[routeName]" - :search-custom-route-redirect="customRouteRedirectName" - :search-redirect="!!customRouteRedirectName" + search-data-key="AccountUsers" + search-url="VnUsers/preview" :searchbar-label="t('account.search')" :searchbar-info="t('account.searchInfo')" /> diff --git a/src/pages/Account/Card/AccountDescriptorMenu.vue b/src/pages/Account/Card/AccountDescriptorMenu.vue index 05576b145..f67cc0c6b 100644 --- a/src/pages/Account/Card/AccountDescriptorMenu.vue +++ b/src/pages/Account/Card/AccountDescriptorMenu.vue @@ -82,6 +82,54 @@ const removeAccount = async () => { }; </script> <template> + <VnConfirm + v-model="showSyncDialog" + :message="t('account.card.actions.sync.message')" + :title="t('account.card.actions.sync.title')" + :promise="sync" + > + <template #customHTML> + {{ shouldSyncPassword }} + <QCheckbox + :label="t('account.card.actions.sync.checkbox')" + v-model="shouldSyncPassword" + class="full-width" + clearable + clear-icon="close" + > + <QIcon style="padding-left: 10px" color="primary" name="info" size="sm"> + <QTooltip>{{ t('account.card.actions.sync.tooltip') }}</QTooltip> + </QIcon></QCheckbox + > + <QInput + v-if="shouldSyncPassword" + :label="t('login.password')" + v-model="syncPassword" + class="full-width" + clearable + clear-icon="close" + type="password" + /> + </template> + </VnConfirm> + <!-- <QItem v-ripple clickable @click="setPassword"> + <QItemSection>{{ t('account.card.actions.setPassword') }}</QItemSection> + </QItem> + <QItem + v-if="!account.hasAccount" + v-ripple + clickable + @click=" + openConfirmationModal( + t('account.card.actions.enableAccount.title'), + t('account.card.actions.enableAccount.subtitle'), + () => updateStatusAccount(true) + ) + " + > + <QItemSection>{{ t('account.card.actions.enableAccount.name') }}</QItemSection> + </QItem> --> + <QItem v-if="account.hasAccount" v-ripple diff --git a/src/pages/Account/Card/AccountPrivileges.vue b/src/pages/Account/Card/AccountPrivileges.vue index f1f24f19b..1300f5018 100644 --- a/src/pages/Account/Card/AccountPrivileges.vue +++ b/src/pages/Account/Card/AccountPrivileges.vue @@ -14,16 +14,11 @@ const rolesOptions = ref([]); const formModelRef = ref(); </script> <template> - <FetchData - url="VnRoles" - :filter="{ fields: ['id', 'name'], order: 'name ASC' }" - auto-load - @on-fetch="(data) => (rolesOptions = data)" - /> + <FetchData url="VnRoles" auto-load @on-fetch="(data) => (rolesOptions = data)" /> <FormModel ref="formModelRef" model="AccountPrivileges" - :url="`VnUsers/${route.params.id}`" + :url="`VnUsers/${route.params.id}/privileges`" :url-create="`VnUsers/${route.params.id}/privileges`" auto-load @on-data-saved="formModelRef.fetch()" diff --git a/src/pages/Account/Role/AccountRoles.vue b/src/pages/Account/Role/AccountRoles.vue index c11bb340a..07b205993 100644 --- a/src/pages/Account/Role/AccountRoles.vue +++ b/src/pages/Account/Role/AccountRoles.vue @@ -5,6 +5,8 @@ import VnTable from 'components/VnTable/VnTable.vue'; import { useRoute } from 'vue-router'; import VnSearchbar from 'components/ui/VnSearchbar.vue'; import { useStateStore } from 'stores/useStateStore'; +import { useSummaryDialog } from 'src/composables/useSummaryDialog'; +import RoleSummary from './Card/RoleSummary.vue'; const route = useRoute(); const stateStore = useStateStore(); const { t } = useI18n(); @@ -16,7 +18,7 @@ const $props = defineProps({ }); const tableRef = ref(); const entityId = computed(() => $props.id || route.params.id); - +const { viewSummary } = useSummaryDialog(); const columns = computed(() => [ { align: 'left', @@ -42,6 +44,18 @@ const columns = computed(() => [ cardVisible: true, create: true, }, + { + align: 'right', + label: '', + name: 'tableActions', + actions: [ + { + title: t('View Summary'), + icon: 'preview', + action: (row) => viewSummary(row.id, RoleSummary), + }, + ], + }, ]); const exprBuilder = (param, value) => { switch (param) { @@ -89,6 +103,5 @@ const exprBuilder = (param, value) => { default-mode="table" auto-load redirect="account/role" - :is-editable="true" /> </template> diff --git a/src/pages/Account/Role/Card/RoleBasicData.vue b/src/pages/Account/Role/Card/RoleBasicData.vue index cddf755f0..56f4f0c27 100644 --- a/src/pages/Account/Role/Card/RoleBasicData.vue +++ b/src/pages/Account/Role/Card/RoleBasicData.vue @@ -23,11 +23,6 @@ const { t } = useI18n(); /> </div> </VnRow> - <VnRow> - <div class="col"> - <QCheckbox :label="t('mailAlias.isPublic')" v-model="data.isPublic" /> - </div> - </VnRow> </template> </FormModel> </template> diff --git a/src/router/modules/account.js b/src/router/modules/account.js index 3faa00fbc..cfec2b95d 100644 --- a/src/router/modules/account.js +++ b/src/router/modules/account.js @@ -65,13 +65,13 @@ export default { component: () => import('src/pages/Account/AccountAliasList.vue'), }, { - path: 'connections', - name: 'AccountConnections', + path: 'acls', + name: 'AccountAcls', meta: { - title: 'connections', + title: 'acls', icon: 'check', }, - component: () => import('src/pages/Account/AccountConnections.vue'), + component: () => import('src/pages/Account/AccountAcls.vue'), }, { path: 'accounts', @@ -104,13 +104,13 @@ export default { component: () => import('src/pages/Account/AccountSamba.vue'), }, { - path: 'acls', - name: 'AccountAcls', + path: 'connections', + name: 'AccountConnections', meta: { - title: 'acls', - icon: 'check', + title: 'connections', + icon: 'share', }, - component: () => import('src/pages/Account/AccountAcls.vue'), + component: () => import('src/pages/Account/AccountConnections.vue'), }, { path: 'acl-form', From 40c3ba15e6e0e02756526535af42462df54d3f11 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Thu, 8 Aug 2024 14:55:53 +0200 Subject: [PATCH 15/60] refs #7355 account acls roles cars --- src/pages/Account/AccountAcls.vue | 7 ------- src/pages/Account/Role/AccountRoles.vue | 16 ++++++---------- src/pages/Account/Role/Card/RoleCard.vue | 5 ++--- 3 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/pages/Account/AccountAcls.vue b/src/pages/Account/AccountAcls.vue index 3a6679956..080956d46 100644 --- a/src/pages/Account/AccountAcls.vue +++ b/src/pages/Account/AccountAcls.vue @@ -123,13 +123,6 @@ const deleteAcl = async ({ id }) => { </script> <template> - <FetchData - url="VnRoles" - :filter="{ fields: ['name'], order: 'name ASC' }" - @on-fetch="(data) => (rolesOptions = data)" - auto-load - /> - <VnSearchbar data-key="AccountAcls" url="ACLs" diff --git a/src/pages/Account/Role/AccountRoles.vue b/src/pages/Account/Role/AccountRoles.vue index 07b205993..0604b71af 100644 --- a/src/pages/Account/Role/AccountRoles.vue +++ b/src/pages/Account/Role/AccountRoles.vue @@ -76,16 +76,12 @@ const exprBuilder = (param, value) => { </script> <template> - <template v-if="stateStore.isHeaderMounted()"> - <Teleport to="#searchbar"> - <VnSearchbar - data-key="Roles" - :expr-builder="exprBuilder" - :label="t('role.searchRoles')" - :info="t('role.searchInfo')" - /> - </Teleport> - </template> + <VnSearchbar + data-key="Roles" + :expr-builder="exprBuilder" + :label="t('role.searchRoles')" + :info="t('role.searchInfo')" + /> <VnTable ref="tableRef" data-key="Roles" diff --git a/src/pages/Account/Role/Card/RoleCard.vue b/src/pages/Account/Role/Card/RoleCard.vue index 8a65d37d7..e5c8c3ebb 100644 --- a/src/pages/Account/Role/Card/RoleCard.vue +++ b/src/pages/Account/Role/Card/RoleCard.vue @@ -22,9 +22,8 @@ const searchBarDataKeys = { <VnCard data-key="Role" :descriptor="RoleDescriptor" - :search-data-key="searchBarDataKeys[routeName]" - :search-custom-route-redirect="customRouteRedirectName" - :search-redirect="!!customRouteRedirectName" + search-data-key="AccountRoles" + search-url="VnRoles" :searchbar-label="t('role.searchRoles')" :searchbar-info="t('role.searchInfo')" /> From 3f76b5496a2c30c278bd1b7c06e1f16b472bd090 Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Fri, 9 Aug 2024 07:46:32 +0200 Subject: [PATCH 16/60] fix: refs #7717 fix order sections --- src/composables/useArrayData.js | 2 +- src/pages/Order/Card/OrderCatalog.vue | 25 +++++++++++++++---- src/pages/Order/Card/OrderCatalogFilter.vue | 3 ++- .../Order/Card/OrderCatalogItemDialog.vue | 23 ++++++++++------- src/pages/Order/Card/OrderDescriptor.vue | 19 +++++++++----- src/pages/Order/Card/OrderForm.vue | 21 +++++++++++++--- src/pages/Order/Card/OrderLines.vue | 19 +++++++++++++- src/pages/Order/Card/OrderVolume.vue | 8 +++--- 8 files changed, 90 insertions(+), 30 deletions(-) diff --git a/src/composables/useArrayData.js b/src/composables/useArrayData.js index 50d620a34..651bcefb0 100644 --- a/src/composables/useArrayData.js +++ b/src/composables/useArrayData.js @@ -28,7 +28,7 @@ export function useArrayData(key = useRoute().meta.moduleName, userOptions) { delete params.filter; store.userParams = { ...params, ...store.userParams }; store.userFilter = { ...filter, ...store.userFilter }; - if (filter.order) store.order = filter.order; + if (filter?.order) store.order = filter.order; } }); diff --git a/src/pages/Order/Card/OrderCatalog.vue b/src/pages/Order/Card/OrderCatalog.vue index dc7115422..68bf9511f 100644 --- a/src/pages/Order/Card/OrderCatalog.vue +++ b/src/pages/Order/Card/OrderCatalog.vue @@ -1,7 +1,8 @@ <script setup> import { useStateStore } from 'stores/useStateStore'; -import { useRoute } from 'vue-router'; +import { useRoute, useRouter } from 'vue-router'; import { onMounted, onUnmounted, ref } from 'vue'; +import axios from 'axios'; import { useI18n } from 'vue-i18n'; import VnPaginate from 'components/ui/VnPaginate.vue'; import CatalogItem from 'components/ui/CatalogItem.vue'; @@ -9,10 +10,15 @@ import OrderCatalogFilter from 'pages/Order/Card/OrderCatalogFilter.vue'; import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; const route = useRoute(); +const router = useRouter(); const stateStore = useStateStore(); const { t } = useI18n(); +const tags = ref([]); -onMounted(() => (stateStore.rightDrawer = true)); +onMounted(() => { + stateStore.rightDrawer = true; + checkOrderConfirmation(); +}); onUnmounted(() => (stateStore.rightDrawer = false)); const catalogParams = { @@ -20,7 +26,12 @@ const catalogParams = { orderBy: JSON.stringify({ field: 'relevancy DESC, name', way: 'ASC', isTag: false }), }; -const tags = ref([]); +async function checkOrderConfirmation() { + const response = await axios.get(`Orders/${route.params.id}`); + if (response.data.isConfirmed === 1) { + router.push(`/order/${route.params.id}/line`); + } +} function extractTags(items) { const resultTags = []; @@ -60,7 +71,7 @@ function extractValueTags(items) { :redirect="false" url="Orders/CatalogFilter" :label="t('Search items')" - :info="t('You can search orders by reference')" + :info="t('You can search items by name or id')" /> <QDrawer v-model="stateStore.rightDrawer" side="right" :width="256" show-if-above> <QScrollArea class="fit text-grey-8"> @@ -78,7 +89,6 @@ function extractValueTags(items) { url="Orders/CatalogFilter" :limit="50" :user-params="catalogParams" - auto-load @on-fetch="extractTags" :update-router="false" > @@ -116,3 +126,8 @@ function extractValueTags(items) { text-align: center; } </style> + +<i18n> +es: + You can search items by name or id: Puedes buscar items por nombre o id +</i18n> diff --git a/src/pages/Order/Card/OrderCatalogFilter.vue b/src/pages/Order/Card/OrderCatalogFilter.vue index 85476b438..887cb6740 100644 --- a/src/pages/Order/Card/OrderCatalogFilter.vue +++ b/src/pages/Order/Card/OrderCatalogFilter.vue @@ -102,7 +102,8 @@ function exprBuilder(param, value) { case 'typeFk': return { [param]: value }; case 'search': - return { 'i.name': { like: `%${value}%` } }; + if (/^\d+$/.test(value)) return { 'i.id': value }; + else return { 'i.name': { like: `%${value}%` } }; } } diff --git a/src/pages/Order/Card/OrderCatalogItemDialog.vue b/src/pages/Order/Card/OrderCatalogItemDialog.vue index 047816127..3f97443ca 100644 --- a/src/pages/Order/Card/OrderCatalogItemDialog.vue +++ b/src/pages/Order/Card/OrderCatalogItemDialog.vue @@ -5,10 +5,12 @@ import { useI18n } from 'vue-i18n'; import axios from 'axios'; import { useRoute } from 'vue-router'; import useNotify from 'composables/useNotify'; +import { useArrayData } from 'composables/useArrayData'; const { t } = useI18n(); -const route = useRoute(); const { notify } = useNotify(); +const emit = defineEmits(['added']); +const route = useRoute(); const props = defineProps({ prices: { type: Array, @@ -16,9 +18,8 @@ const props = defineProps({ }, }); -const emit = defineEmits(['added']); - const fields = ref((props.prices || []).map((item) => ({ ...item, quantity: 0 }))); +const descriptorData = useArrayData('orderData'); const addToOrder = async () => { const items = (fields.value || []).filter((item) => Number(item.quantity) > 0); @@ -28,19 +29,20 @@ const addToOrder = async () => { }); notify(t('globals.dataSaved'), 'positive'); emit('added'); + descriptorData.fetch({}); }; </script> <template> - <div class="container order-catalog-item q-pb-md"> + <div class="container order-catalog-item q-pa-md"> <QForm @submit="addToOrder"> <QMarkupTable class="shadow-0"> <tbody> <tr v-for="item in fields" :key="item.warehouse"> - <td class="text-bold q-py-lg"> + <td class="text-bold q-pr-md td" style="width: 35%"> {{ item.warehouse }} </td> - <td class="text-right"> + <td class="text-right" style="width: 35%"> <span class="link" @click=" @@ -75,8 +77,11 @@ const addToOrder = async () => { </template> <style lang="scss" scoped> -.container { - max-width: 448px; - width: 100%; +// .container { +// max-width: 768px; +// width: 100%; +// } +.td { + width: 200px; } </style> diff --git a/src/pages/Order/Card/OrderDescriptor.vue b/src/pages/Order/Card/OrderDescriptor.vue index 4d84a32fc..8d2b5309e 100644 --- a/src/pages/Order/Card/OrderDescriptor.vue +++ b/src/pages/Order/Card/OrderDescriptor.vue @@ -4,13 +4,13 @@ import { useRoute } from 'vue-router'; import { useI18n } from 'vue-i18n'; import { toCurrency, toDate } from 'src/filters'; import { useState } from 'src/composables/useState'; - import useCardDescription from 'src/composables/useCardDescription'; -import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; + import CardDescriptor from 'components/ui/CardDescriptor.vue'; import VnLv from 'src/components/ui/VnLv.vue'; -import OrderDescriptorMenu from 'pages/Order/Card/OrderDescriptorMenu.vue'; import FetchData from 'components/FetchData.vue'; +import OrderDescriptorMenu from 'pages/Order/Card/OrderDescriptorMenu.vue'; +import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; const DEFAULT_ITEMS = 0; @@ -25,6 +25,8 @@ const $props = defineProps({ const route = useRoute(); const state = useState(); const { t } = useI18n(); +const data = ref(useCardDescription()); +const getTotalRef = ref(); const entityId = computed(() => { return $props.id || route.params.id; @@ -57,11 +59,11 @@ const filter = { ], }; -const data = ref(useCardDescription()); const setData = (entity) => { if (!entity) return; + getTotalRef.value && getTotalRef.value.fetch(); data.value = useCardDescription(entity?.client?.name, entity?.id); - state.set('OrderDescriptor', entity); + state.set('orderData', entity); }; const getConfirmationValue = (isConfirmed) => { @@ -69,10 +71,15 @@ const getConfirmationValue = (isConfirmed) => { }; const total = ref(null); + +function ticketFilter(order) { + return JSON.stringify({ id: order.id }); +} </script> <template> <FetchData + ref="getTotalRef" :url="`Orders/${entityId}/getTotal`" @on-fetch="(response) => (total = response)" auto-load @@ -120,7 +127,7 @@ const total = ref(null); color="primary" :to="{ name: 'TicketList', - query: { params: JSON.stringify({ orderFk: entity.id }) }, + query: { table: ticketFilter(entity) }, }" > <QTooltip>{{ t('order.summary.orderTicketList') }}</QTooltip> diff --git a/src/pages/Order/Card/OrderForm.vue b/src/pages/Order/Card/OrderForm.vue index 91640101c..74d0a8b75 100644 --- a/src/pages/Order/Card/OrderForm.vue +++ b/src/pages/Order/Card/OrderForm.vue @@ -64,11 +64,14 @@ const fetchOrderDetails = (order) => { }; const orderMapper = (order) => { - return { + const mappedOrder = { addressId: order.addressFk, - agencyModeId: order.agencyModeFk, landed: new Date(order.landed).toISOString(), }; + if (order.agencyModeFk !== null && order.agencyModeFk !== undefined) { + mappedOrder.agencyModeId = order.agencyModeFk; + } + return mappedOrder; }; const orderFilter = { include: [ @@ -184,7 +187,19 @@ const onClientChange = async (clientId) => { option-value="agencyModeFk" option-label="agencyMode" hide-selected - :disable="!agencyList?.length" + :disable="!agencyList?.length && data.isConfirmed === 1" + clearable + emit-value + map-options + :model-value=" + !data.isConfirmed && + agencyList?.length && + agencyList.some( + (agency) => agency.agencyModeFk === data.agency_id + ) + ? data.agencyModeFk + : null + " > </VnSelect> </VnRow> diff --git a/src/pages/Order/Card/OrderLines.vue b/src/pages/Order/Card/OrderLines.vue index 21ea3b300..d4a5c4b1d 100644 --- a/src/pages/Order/Card/OrderLines.vue +++ b/src/pages/Order/Card/OrderLines.vue @@ -61,6 +61,13 @@ const lineFilter = ref({ fields: ['id', 'name'], }, }, + { + relation: 'order', + scope: { + fields: ['id', 'isConfirmed'], + where: { id: route.params.id }, + }, + }, ], where: { orderFk: route.params.id }, }); @@ -157,8 +164,9 @@ const columns = computed(() => [ name: 'tableActions', actions: [ { - title: t('delete'), + title: t('Delete'), icon: 'delete', + show: (row) => !row.order.isConfirmed, action: (row) => confirmRemove(row), isPrimary: true, }, @@ -281,6 +289,15 @@ watch( <template #column-amount="{ row }"> {{ toCurrency(row.quantity * row.price) }} </template> + <template #column-tableActions="{ row }"> + <QIcon + v-if="row.order?.isConfirmed === 0" + name="delete" + icon="delete" + @click="confirmRemove(row)" + class="cursor-pointer" + /> + </template> </VnTable> </div> <QPageSticky :offset="[20, 20]" v-if="!order?.isConfirmed" style="z-index: 2"> diff --git a/src/pages/Order/Card/OrderVolume.vue b/src/pages/Order/Card/OrderVolume.vue index 69f0a9c6b..8622a1fa5 100644 --- a/src/pages/Order/Card/OrderVolume.vue +++ b/src/pages/Order/Card/OrderVolume.vue @@ -10,14 +10,16 @@ import FetchData from 'components/FetchData.vue'; import VnLv from 'components/ui/VnLv.vue'; import CardList from 'components/ui/CardList.vue'; import FetchedTags from 'components/ui/FetchedTags.vue'; - +import { useStateStore } from 'stores/useStateStore'; import { dashIfEmpty } from 'src/filters'; +import { onMounted } from 'vue'; const router = useRouter(); const route = useRoute(); const { t } = useI18n(); const volumeSummary = ref(null); const volumeRef = ref(); +const stateStore = useStateStore(); const volumeFilter = ref({ include: [ { @@ -46,6 +48,7 @@ watch( volumeRef.value.fetch(); } ); +onMounted(() => (stateStore.rightDrawer = false)); </script> <template> @@ -63,9 +66,6 @@ watch( {{ t('globals.noResults') }} </div> <QCard v-else class="order-volume-summary q-pa-lg"> - <p class="header text-right block"> - {{ t('summary') }} - </p> <VnLv :label="t('total')" :value="`${volumeSummary?.totalVolume} m³`" /> <VnLv :label="t('boxes')" From ee71ae9d24ccb018b7017f09016bff4985dc16c7 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Fri, 9 Aug 2024 13:07:37 +0200 Subject: [PATCH 17/60] feat(orderList): use orderFilter and fixed this --- src/pages/Order/Card/OrderFilter.vue | 232 ++++++++++----------------- src/pages/Order/OrderList.vue | 14 +- 2 files changed, 94 insertions(+), 152 deletions(-) diff --git a/src/pages/Order/Card/OrderFilter.vue b/src/pages/Order/Card/OrderFilter.vue index 347affb04..be47eed54 100644 --- a/src/pages/Order/Card/OrderFilter.vue +++ b/src/pages/Order/Card/OrderFilter.vue @@ -21,15 +21,13 @@ const salesPersonFilter = { fields: ['id', 'nickname'], }; const salesPersonList = ref(null); -const sourceFilter = { fields: ['value'] }; -const sourceList = ref(null); +const sourceList = ref([]); </script> <template> <FetchData url="AgencyModes/isActive" :filter="agencyFilter" - limit="30" sort-by="name ASC" auto-load @on-fetch="(data) => (agencyList = data)" @@ -37,7 +35,6 @@ const sourceList = ref(null); <FetchData url="Workers/search" :filter="salesPersonFilter" - limit="30" sort-by="nickname ASC" @on-fetch="(data) => (salesPersonList = data)" :params="{ departmentCodes: ['VT'] }" @@ -45,8 +42,7 @@ const sourceList = ref(null); /> <FetchData url="Orders/getSourceValues" - :filter="sourceFilter" - limit="30" + :filter="{ fields: ['value'] }" sort-by="value ASC" @on-fetch="(data) => (sourceList = data)" auto-load @@ -59,148 +55,88 @@ const sourceList = ref(null); </div> </template> <template #body="{ params }"> - <QItem> - <QItemSection> - <VnInput - is-outlined - :label="t('customerId')" - v-model="params.clientFk" - lazy-rules - > - <template #prepend> - <QIcon name="badge" size="sm"></QIcon> - </template> - </VnInput> - </QItemSection> - </QItem> - <QItem> - <QItemSection v-if="agencyList"> - <VnSelect - :label="t('agency')" - v-model="params.agencyModeFk" - :options="agencyList" - option-value="id" - option-label="name" - dense - outlined - rounded - emit-value - map-options - use-input - :input-debounce="0" - /> - </QItemSection> - <QItemSection v-else> - <QSkeleton type="QInput" class="full-width" /> - </QItemSection> - </QItem> - <QItem> - <QItemSection v-if="salesPersonList"> - <VnSelect - :label="t('salesPerson')" - v-model="params.workerFk" - :options="salesPersonList" - option-value="id" - option-label="name" - dense - outlined - rounded - emit-value - map-options - use-input - :input-debounce="0" - > - <template #option="{ itemProps, opt }"> - <QItem v-bind="itemProps"> - <QItemSection> - <QItemLabel>{{ opt.name }}</QItemLabel> - <QItemLabel caption> - {{ opt.nickname }},{{ opt.code }} - </QItemLabel> - </QItemSection> - </QItem> - </template> - </VnSelect> - </QItemSection> - <QItemSection v-else> - <QSkeleton type="QInput" class="full-width" /> - </QItemSection> - </QItem> - <QItem> - <QItemSection> - <VnInputDate - v-model="params.from" - :label="t('fromLanded')" - dense - outlined - rounded - /> - </QItemSection> - </QItem> - <QItem> - <QItemSection> - <VnInputDate - v-model="params.to" - :label="t('toLanded')" - dense - outlined - rounded - /> - </QItemSection> - </QItem> - <QItem> - <QItemSection> - <VnInput - :label="t('orderId')" - v-model="params.orderFk" - lazy-rules - is-outlined - /> - </QItemSection> - </QItem> - <QItem> - <QItemSection v-if="sourceList"> - <VnSelect - :label="t('application')" - v-model="params.sourceApp" - :options="sourceList" - option-label="value" - emit-value - map-options - use-input - dense - outlined - rounded - :input-debounce="0" - /> - </QItemSection> - <QItemSection v-else> - <QSkeleton type="QInput" class="full-width" /> - </QItemSection> - </QItem> - <QItem> - <QItemSection> - <QCheckbox - v-model="params.myTeam" - :label="t('myTeam')" - toggle-indeterminate - /> - </QItemSection> - </QItem> - <QItem> - <QItemSection> - <QCheckbox - v-model="params.isConfirmed" - :label="t('isConfirmed')" - toggle-indeterminate - /> - </QItemSection> - </QItem> - <QItem> - <QItemSection> - <QCheckbox v-model="params.showEmpty" :label="t('showEmpty')" /> - </QItemSection> - </QItem> + <div class="q-px-md q-gutter-y-sm"> + <VnInput + :label="t('customerId')" + v-model="params.clientFk" + lazy-rules + dense + outlined + rounded + /> + <VnSelect + :label="t('agency')" + v-model="params.agencyModeFk" + :options="agencyList" + :input-debounce="0" + dense + outlined + rounded + /> + <VnSelect + :label="t('salesPerson')" + v-model="params.workerFk" + url="Workers/search" + :filter="{ departmentCodes: ['VT'] }" + sort-by="nickname ASC" + option-label="nickname" + dense + outlined + rounded + > + <template #option="{ itemProps, opt }"> + <QItem v-bind="itemProps"> + <QItemSection> + <QItemLabel>{{ opt.name }}</QItemLabel> + <QItemLabel caption> + {{ opt.nickname }},{{ opt.code }} + </QItemLabel> + </QItemSection> + </QItem> + </template> + </VnSelect> + <VnInputDate + v-model="params.from" + :label="t('fromLanded')" + dense + outlined + rounded + /> + <VnInputDate + v-model="params.to" + :label="t('toLanded')" + dense + outlined + rounded + /> + <VnInput + :label="t('orderId')" + v-model="params.orderFk" + lazy-rules + is-outlined + /> + <VnSelect + :label="t('application')" + v-model="params.sourceApp" + :options="sourceList" + option-label="value" + dense + outlined + rounded + :input-debounce="0" + /> + <QCheckbox + v-model="params.myTeam" + :label="t('myTeam')" + toggle-indeterminate + /> + <QCheckbox + v-model="params.isConfirmed" + :label="t('isConfirmed')" + toggle-indeterminate + /> + <QCheckbox v-model="params.showEmpty" :label="t('showEmpty')" /> + </div> </template> </VnFilterPanel> </template> diff --git a/src/pages/Order/OrderList.vue b/src/pages/Order/OrderList.vue index e0261fc19..1622f5ffe 100644 --- a/src/pages/Order/OrderList.vue +++ b/src/pages/Order/OrderList.vue @@ -9,6 +9,8 @@ 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 OrderSearchbar from './Card/OrderSearchbar.vue'; +import RightMenu from 'src/components/common/RightMenu.vue'; +import OrderFilter from './Card/OrderFilter.vue'; const { t } = useI18n(); const { viewSummary } = useSummaryDialog(); @@ -119,9 +121,6 @@ const columns = computed(() => [ align: 'left', name: 'total', label: t('module.total'), - columnFilter: { - inWhere: true, - }, format: ({ total }) => toCurrency(total), cardVisible: true, }, @@ -147,10 +146,16 @@ async function fetchClientAddress(id, data) { </script> <template> <OrderSearchbar /> + <RightMenu> + <template #right-panel> + <OrderFilter data-key="OrderList" /> + </template> + </RightMenu> <VnTable ref="tableRef" data-key="OrderList" url="Orders/filter" + :order="['landed DESC', 'clientFk ASC', 'id DESC']" :create="{ urlCreate: 'Orders/new', title: 'Create Order', @@ -162,9 +167,10 @@ async function fetchClientAddress(id, data) { addressId: null, }, }" + :user-params="{ showEmpty: false }" + :right-search="false" :columns="columns" redirect="order" - auto-load > <template #more-create-dialog="{ data }"> <VnSelect From b21ba3dc2c0f06924eb30c266aff0c0a0128dec1 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Fri, 9 Aug 2024 13:55:29 +0200 Subject: [PATCH 18/60] refs #7355 account cards roles cards --- src/pages/Account/AccountList.vue | 2 +- src/pages/Account/Card/AccountCard.vue | 15 --------------- src/pages/Account/Role/Card/RoleCard.vue | 13 ------------- 3 files changed, 1 insertion(+), 29 deletions(-) diff --git a/src/pages/Account/AccountList.vue b/src/pages/Account/AccountList.vue index d789fdf84..cdd88551b 100644 --- a/src/pages/Account/AccountList.vue +++ b/src/pages/Account/AccountList.vue @@ -91,9 +91,9 @@ const exprBuilder = (param, value) => { <template> <VnSearchbar - :label="t('account.search')" data-key="AccountUsers" :expr-builder="exprBuilder" + :label="t('account.search')" :info="t('account.searchInfo')" /> diff --git a/src/pages/Account/Card/AccountCard.vue b/src/pages/Account/Card/AccountCard.vue index 555ce0866..813e7fffd 100644 --- a/src/pages/Account/Card/AccountCard.vue +++ b/src/pages/Account/Card/AccountCard.vue @@ -1,24 +1,9 @@ <script setup> -import { computed } from 'vue'; import { useI18n } from 'vue-i18n'; -import { useRoute } from 'vue-router'; - import VnCard from 'components/common/VnCard.vue'; import AccountDescriptor from './AccountDescriptor.vue'; const { t } = useI18n(); -const route = useRoute(); - -const routeName = computed(() => route.name); -const customRouteRedirectName = computed(() => routeName.value); -const searchBarDataKeys = { - AccountSummary: 'AccountSummary', - AccountInheritedRoles: 'AccountInheritedRoles', - AccountMailForwarding: 'AccountMailForwarding', - AccountMailAlias: 'AccountMailAlias', - AccountPrivileges: 'AccountPrivileges', - AccountLog: 'AccountLog', -}; </script> <template> diff --git a/src/pages/Account/Role/Card/RoleCard.vue b/src/pages/Account/Role/Card/RoleCard.vue index e5c8c3ebb..97c842b8f 100644 --- a/src/pages/Account/Role/Card/RoleCard.vue +++ b/src/pages/Account/Role/Card/RoleCard.vue @@ -1,22 +1,9 @@ <script setup> -import { computed } from 'vue'; import { useI18n } from 'vue-i18n'; -import { useRoute } from 'vue-router'; import VnCard from 'components/common/VnCard.vue'; import RoleDescriptor from './RoleDescriptor.vue'; const { t } = useI18n(); -const route = useRoute(); - -const routeName = computed(() => route.name); -const customRouteRedirectName = computed(() => routeName.value); -const searchBarDataKeys = { - RoleSummary: 'RoleSummary', - RoleBasicData: 'RoleBasicData', - SubRoles: 'SubRoles', - InheritedRoles: 'InheritedRoles', - RoleLog: 'RoleLog', -}; </script> <template> <VnCard From dc7c686508acbaedb756fac6eddf7d2a4f355b63 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Fri, 9 Aug 2024 14:19:08 +0200 Subject: [PATCH 19/60] feat(orderList): correct create order --- src/pages/Order/OrderList.vue | 85 ++++++++++++++++++++++------------- 1 file changed, 54 insertions(+), 31 deletions(-) diff --git a/src/pages/Order/OrderList.vue b/src/pages/Order/OrderList.vue index 1622f5ffe..9870be9b3 100644 --- a/src/pages/Order/OrderList.vue +++ b/src/pages/Order/OrderList.vue @@ -15,9 +15,9 @@ import OrderFilter from './Card/OrderFilter.vue'; const { t } = useI18n(); const { viewSummary } = useSummaryDialog(); const tableRef = ref(); -const clientList = ref([]); const agencyList = ref([]); -const selectedAddress = ref(); +const addressesList = ref([]); +const clientId = ref(); const columns = computed(() => [ { @@ -102,20 +102,19 @@ const columns = computed(() => [ align: 'left', name: 'agencyModeFk', label: t('module.agency'), - component: 'select', - cardVisible: true, - attrs: { - url: 'agencyModes', - fields: ['id', 'name'], - find: { - value: 'agencyModeFk', - label: 'agencyName', + format: (row) => row?.agencyName, + columnFilter: { + component: 'select', + attrs: { + url: 'agencyModes', + fields: ['id', 'name'], + find: { + value: 'agencyModeFk', + label: 'agencyName', + }, }, }, - columnField: { - component: null, - }, - format: (row) => row?.agencyName, + cardVisible: true, }, { align: 'left', @@ -138,10 +137,22 @@ const columns = computed(() => [ }, ]); -async function fetchClientAddress(id, data) { - const clientData = await axios.get(`Clients/${id}`); - selectedAddress.value = clientData.data.defaultAddressFk; - data.addressId = selectedAddress.value; +async function fetchClientAddress(id, formData) { + const { data } = await axios.get(`Clients/${id}`, { + params: { filter: { include: { relation: 'addresses' } } }, + }); + addressesList.value = data.addresses; + formData.addressId = data.defaultAddressFk; + fetchAgencies(formData); +} + +async function fetchAgencies({ landed, addressId }) { + if (!landed || !addressId) return (agencyList.value = []); + + const { data } = await axios.get('Agencies/landsThatDay', { + params: { addressFk: addressId, landed }, + }); + agencyList.value = data; } </script> <template> @@ -175,29 +186,41 @@ async function fetchClientAddress(id, data) { <template #more-create-dialog="{ data }"> <VnSelect url="Clients" - v-model="data.id" + :include="{ relation: 'addresses' }" + v-model="clientId" :label="t('module.customer')" - :options="clientList" - option-value="id" - option-label="name" @update:model-value="(id) => fetchClientAddress(id, data)" /> <VnSelect - url="Clients" - v-model="selectedAddress" + v-model="data.addressId" + :options="addressesList" :label="t('module.address')" - :options="selectedAddress" - option-value="defaultAddressFk" - option-label="street" + option-value="id" + option-label="nickname" + @update:model-value="() => fetchAgencies(data)" + > + <template #option="scope"> + <QItem v-bind="scope.itemProps"> + <QItemSection> + <QItemLabel> + {{ scope.opt?.nickname }}: {{ scope.opt?.street }}, + {{ scope.opt?.city }}</QItemLabel + > + </QItemSection> + </QItem> + </template> + </VnSelect> + <VnInputDate + v-model="data.landed" + :label="t('module.landed')" + @update:model-value="() => fetchAgencies(data)" /> - <VnInputDate v-model="data.landed" :label="t('module.landed')" /> <VnSelect - url="Agencies" v-model="data.agencyModeId" :label="t('module.agency')" :options="agencyList" - option-value="id" - option-label="name" + option-value="agencyModeFk" + option-label="agencyMode" /> </template> </VnTable> From 37244cdbc7a78f9b98fb9875747678b4a52fe790 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Fri, 9 Aug 2024 14:55:49 +0200 Subject: [PATCH 20/60] feat(orderBasicData): add notes --- src/i18n/locale/en.yml | 1 + src/i18n/locale/es.yml | 1 + .../Card/{OrderForm.vue => OrderBasicData.vue} | 16 +++++++++++----- src/router/modules/order.js | 2 +- 4 files changed, 14 insertions(+), 6 deletions(-) rename src/pages/Order/Card/{OrderForm.vue => OrderBasicData.vue} (95%) diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml index 12680d0cb..e639b9fda 100644 --- a/src/i18n/locale/en.yml +++ b/src/i18n/locale/en.yml @@ -93,6 +93,7 @@ globals: since: Since from: From to: To + notes: Notes pageTitles: logIn: Login summary: Summary diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml index 747a10d51..2d7358d5e 100644 --- a/src/i18n/locale/es.yml +++ b/src/i18n/locale/es.yml @@ -93,6 +93,7 @@ globals: since: Desde from: Desde to: Hasta + notes: Notas pageTitles: logIn: Inicio de sesión summary: Resumen diff --git a/src/pages/Order/Card/OrderForm.vue b/src/pages/Order/Card/OrderBasicData.vue similarity index 95% rename from src/pages/Order/Card/OrderForm.vue rename to src/pages/Order/Card/OrderBasicData.vue index 74d0a8b75..dfdb5ae48 100644 --- a/src/pages/Order/Card/OrderForm.vue +++ b/src/pages/Order/Card/OrderBasicData.vue @@ -7,6 +7,7 @@ import { useState } from 'composables/useState'; import FormModel from 'components/FormModel.vue'; import VnRow from 'components/ui/VnRow.vue'; import VnSelect from 'components/common/VnSelect.vue'; +import VnInput from 'components/common/VnInput.vue'; import VnInputDate from 'components/common/VnInputDate.vue'; import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; @@ -117,7 +118,6 @@ const onClientChange = async (clientId) => { :url="`Orders/${route.params.id}`" :url-update="`Orders/${route.params.id}/updateBasicData`" :model="ORDER_MODEL" - :mapper="orderMapper" :filter="orderFilter" @on-fetch="fetchOrderDetails" auto-load @@ -178,8 +178,6 @@ const onClientChange = async (clientId) => { () => fetchAgencyList(data.landed, data.addressFk) " /> - </VnRow> - <VnRow> <VnSelect :label="t('order.form.agencyModeFk')" v-model="data.agencyModeFk" @@ -200,8 +198,16 @@ const onClientChange = async (clientId) => { ? data.agencyModeFk : null " - > - </VnSelect> + /> + </VnRow> + <VnRow> + <VnInput + :label="t('globals.notes')" + type="textarea" + v-model="data.note" + fill-input + autogrow + /> </VnRow> </template> </FormModel> diff --git a/src/router/modules/order.js b/src/router/modules/order.js index 9cac802c2..9ccdb820b 100644 --- a/src/router/modules/order.js +++ b/src/router/modules/order.js @@ -63,7 +63,7 @@ export default { title: 'basicData', icon: 'vn:settings', }, - component: () => import('src/pages/Order/Card/OrderForm.vue'), + component: () => import('src/pages/Order/Card/OrderBasicData.vue'), }, { name: 'OrderCatalog', From e11e51f432d2d80214876f6ad488880ca7d928d8 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Mon, 12 Aug 2024 09:17:51 +0200 Subject: [PATCH 21/60] fix: order description to vnTable --- src/pages/Order/Card/OrderVolume.vue | 166 +++++++++++---------------- 1 file changed, 67 insertions(+), 99 deletions(-) diff --git a/src/pages/Order/Card/OrderVolume.vue b/src/pages/Order/Card/OrderVolume.vue index 8622a1fa5..b16022c2f 100644 --- a/src/pages/Order/Card/OrderVolume.vue +++ b/src/pages/Order/Card/OrderVolume.vue @@ -2,24 +2,20 @@ import axios from 'axios'; import { useRoute } from 'vue-router'; import { useI18n } from 'vue-i18n'; -import { ref, watch } from 'vue'; -import { useRouter } from 'vue-router'; - -import VnPaginate from 'components/ui/VnPaginate.vue'; -import FetchData from 'components/FetchData.vue'; -import VnLv from 'components/ui/VnLv.vue'; -import CardList from 'components/ui/CardList.vue'; -import FetchedTags from 'components/ui/FetchedTags.vue'; -import { useStateStore } from 'stores/useStateStore'; +import { ref } from 'vue'; import { dashIfEmpty } from 'src/filters'; -import { onMounted } from 'vue'; -const router = useRouter(); +import FetchData from 'components/FetchData.vue'; +import FetchedTags from 'components/ui/FetchedTags.vue'; +import ItemDescriptorProxy from 'pages/Item/Card/ItemDescriptorProxy.vue'; +import VnLv from 'components/ui/VnLv.vue'; +import VnTable from 'components/VnTable/VnTable.vue'; + const route = useRoute(); const { t } = useI18n(); const volumeSummary = ref(null); const volumeRef = ref(); -const stateStore = useStateStore(); +const volumes = ref([]); const volumeFilter = ref({ include: [ { @@ -29,26 +25,39 @@ const volumeFilter = ref({ where: { orderFk: route.params.id }, }); +const columns = [ + { + name: 'itemFk', + label: t('item'), + align: 'left', + }, + { + name: 'description', + label: t('globals.description'), + align: 'left', + }, + { + name: 'quantity', + label: t('quantity'), + align: 'left', + }, + { + name: 'volume', + label: t('volume'), + align: 'left', + }, +]; + const loadVolumes = async (rows) => { + if (!rows) return; const { data } = await axios.get(`Orders/${route.params.id}/getVolumes`); - (rows || []).forEach((order) => { + rows.forEach((order) => { (data.volumes || []).forEach((volume) => { - if (order.itemFk === volume.itemFk) { - order.volume = volume.volume; - } + if (order.itemFk === volume.itemFk) order.volume = volume.volume; }); }); + volumes.value = rows; }; - -watch( - () => router.currentRoute.value.params.id, - () => { - volumeFilter.value.where.orderFk = router.currentRoute.value.params.id; - - volumeRef.value.fetch(); - } -); -onMounted(() => (stateStore.rightDrawer = false)); </script> <template> @@ -57,61 +66,38 @@ onMounted(() => (stateStore.rightDrawer = false)); @on-fetch="(data) => (volumeSummary = data)" auto-load /> - <QPage class="column items-center q-pa-md"> - <div class="vn-card-list"> - <div - v-if="!volumeSummary?.totalVolume && !volumeSummary?.totalBoxes" - class="no-result" - > - {{ t('globals.noResults') }} - </div> - <QCard v-else class="order-volume-summary q-pa-lg"> - <VnLv :label="t('total')" :value="`${volumeSummary?.totalVolume} m³`" /> - <VnLv - :label="t('boxes')" - :value="`${dashIfEmpty(volumeSummary?.totalBoxes)} U`" - /> - </QCard> - <VnPaginate - data-key="OrderCatalogVolume" - ref="volumeRef" - url="OrderRows" - :limit="20" - auto-load - :user-filter="volumeFilter" - order="itemFk" - @on-fetch="(data) => loadVolumes(data)" - > - <template #body="{ rows }"> - <div class="catalog-list q-mt-xl"> - <CardList - v-for="row in rows" - :key="row.id" - :id="row.id" - :title="row?.item?.name" - class="cursor-inherit" - > - <template #list-items> - <div class="q-mb-sm"> - <FetchedTags :item="row.item" :max-length="5" /> - </div> - <VnLv :label="t('item')" :value="row.item.id" /> - <VnLv :label="t('subName')"> - <template #value> - <span class="text-uppercase"> - {{ row.item.subName }} - </span> - </template> - </VnLv> - <VnLv :label="t('quantity')" :value="row.quantity" /> - <VnLv :label="t('volume')" :value="row.volume" /> - </template> - </CardList> - </div> - </template> - </VnPaginate> - </div> - </QPage> + <QCard v-if="volumeSummary" class="order-volume-summary q-pa-lg"> + <VnLv :label="t('total')" :value="`${volumeSummary?.totalVolume} m³`" /> + <VnLv + :label="t('boxes')" + :value="`${dashIfEmpty(volumeSummary?.totalBoxes)} U`" + /> + </QCard> + <VnTable + ref="volumeRef" + data-key="OrderCatalogVolume" + url="OrderRows" + :columns="columns" + auto-load + :user-filter="volumeFilter" + order="itemFk" + @on-fetch="(data) => loadVolumes(data)" + :right-search="false" + :column-search="false" + > + <template #column-itemFk="{ row }"> + <span class="link"> + {{ row.itemFk }} + <ItemDescriptorProxy :id="row.itemFk" /> + </span> + </template> + <template #column-description="{ row }"> + <FetchedTags :item="row.item" :max-length="5" /> + </template> + <template #column-volume="{ rowIndex }"> + {{ volumes?.[rowIndex]?.volume }} + </template> + </VnTable> </template> <style lang="scss"> @@ -136,29 +122,12 @@ onMounted(() => (stateStore.rightDrawer = false)); } } </style> -<style lang="scss" scoped> -.header { - color: $primary; - font-weight: bold; - margin-bottom: 25px; - font-size: 20px; - display: inline-block; -} - -.no-result { - font-size: 24px; - font-weight: bold; - color: var(--vn-label-color); - text-align: center; -} -</style> <i18n> en: summary: Summary total: Total boxes: Boxes item: Item - subName: Subname quantity: Quantity volume: m³ per quantity es: @@ -166,7 +135,6 @@ es: total: Total boxes: Cajas item: Artículo - subName: Subnombre quantity: Cantidad volume: m³ por cantidad </i18n> From 190579e7fd8661f4883576753d38b1c91daa5af9 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Mon, 12 Aug 2024 10:22:52 +0200 Subject: [PATCH 22/60] fix: orderCatalogFilter order --- src/pages/Order/Card/OrderCatalogFilter.vue | 122 ++++++++------------ 1 file changed, 45 insertions(+), 77 deletions(-) diff --git a/src/pages/Order/Card/OrderCatalogFilter.vue b/src/pages/Order/Card/OrderCatalogFilter.vue index 887cb6740..e9987d363 100644 --- a/src/pages/Order/Card/OrderCatalogFilter.vue +++ b/src/pages/Order/Card/OrderCatalogFilter.vue @@ -32,14 +32,22 @@ const categoryList = ref(null); const selectedCategoryFk = ref(getParamWhere(route, 'categoryFk')); const typeList = ref([]); const selectedTypeFk = ref(null); -const validationsStore = useValidator(); -const selectedOrder = ref(null); -const selectedOrderField = ref(null); -const moreFields = ref([]); -const moreFieldsOrder = ref([]); const selectedTag = ref(null); const tagValues = ref([{}]); const tagOptions = ref([]); +const vnFilterPanelRef = ref(); +const orderByList = ref([ + { id: 'relevancy DESC, name', name: t('params.relevancy'), priority: 999 }, + { id: 'showOrder, price', name: t('params.colorAndPrice'), priority: 999 }, + { id: 'name', name: t('params.name'), priority: 999 }, + { id: 'price', name: t('params.price'), priority: 999 }, +]); +const orderWayList = ref([ + { id: 'ASC', name: t('params.ASC') }, + { id: 'DESC', name: t('params.DESC') }, +]); +const orderBySelected = ref('relevancy DESC, name'); +const orderWaySelected = ref('ASC'); const createValue = (val, done) => { if (val.length > 2) { @@ -140,35 +148,6 @@ const removeTagChip = (selection, params, search) => { search(); }; -const onOrderChange = (value, params) => { - const tagObj = JSON.parse(params.orderBy); - tagObj.way = value.name; - params.orderBy = JSON.stringify(tagObj); -}; - -const onOrderFieldChange = (value, params) => { - const tagObj = JSON.parse(params.orderBy); - switch (value) { - case 'Relevancy': - tagObj.name = value + ' DESC, name'; - params.orderBy = JSON.stringify(tagObj); - break; - case 'ColorAndPrice': - tagObj.name = 'showOrder, price'; - params.orderBy = JSON.stringify(tagObj); - break; - case 'Name': - tagObj.name = 'name'; - params.orderBy = JSON.stringify(tagObj); - break; - case 'Price': - tagObj.name = 'price'; - params.orderBy = JSON.stringify(tagObj); - break; - } -}; -const _moreFields = ['ASC', 'DESC']; -const _moreFieldsTypes = ['Relevancy', 'ColorAndPrice', 'Name', 'Price']; const setCategoryList = (data) => { categoryList.value = (data || []) .filter((category) => category.display) @@ -176,8 +155,6 @@ const setCategoryList = (data) => { ...category, icon: `vn:${(category.icon || '').split('-')[1]}`, })); - moreFields.value = useLang(_moreFields); - moreFieldsOrder.value = useLang(_moreFieldsTypes); selectedCategoryFk.value && loadTypes(); }; @@ -188,24 +165,19 @@ const getCategoryClass = (category, params) => { } }; -const useLang = (values) => { - const { models } = validationsStore; - const properties = models.Item?.properties || {}; - return values.map((name) => { - let prop = properties[name]; - const label = t(`params.${name}`); - return { - name, - label, - type: prop ? prop.type : null, - }; - }); -}; +function addOrder(value, field, params) { + let { orderBy } = params; + orderBy = JSON.parse(orderBy); + orderBy[field] = value; + params.orderBy = JSON.stringify(orderBy); + vnFilterPanelRef.value.search(); +} </script> <template> <FetchData url="ItemCategories" limit="30" auto-load @on-fetch="setCategoryList" /> <VnFilterPanel + ref="vnFilterPanelRef" :data-key="props.dataKey" :hidden-tags="['orderFk', 'orderBy']" :un-removable-params="['orderFk', 'orderBy']" @@ -298,33 +270,29 @@ const useLang = (values) => { </QItemSection> </QItem> <QSeparator /> - <QItem class="q-my-md"> - <QItemSection> - <VnSelect - :label="t('Order')" - v-model="selectedOrder" - :options="moreFields" - option-label="label" - option-value="way" - dense - outlined - rounded - @update:model-value="(value) => onOrderChange(value, params)" - /> - </QItemSection> - </QItem> <QItem class="q-mb-md"> <QItemSection> <VnSelect :label="t('Order by')" - v-model="selectedOrderField" - :options="moreFieldsOrder" - option-label="label" - option-value="name" + v-model="orderBySelected" + :options="orderByList" dense outlined rounded - @update:model-value="(value) => onOrderFieldChange(value, params)" + @update:model-value="(value) => addOrder(value, 'field', params)" + /> + </QItemSection> + </QItem> + <QItem class="q-my-md"> + <QItemSection> + <VnSelect + :label="t('Order')" + v-model="orderWaySelected" + :options="orderWayList" + dense + outlined + rounded + @update:model-value="(value) => addOrder(value, 'way', params)" /> </QItemSection> </QItem> @@ -490,10 +458,10 @@ en: order: Order ASC: Ascendant DESC: Descendant - Relevancy: Relevancy - ColorAndPrice: Color and price - Name: Name - Price: Price + relevancy: Relevancy + colorAndPrice: Color and price + name: Name + price: Price es: params: type: Tipo @@ -503,10 +471,10 @@ es: order: Orden ASC: Ascendiente DESC: Descendiente - Relevancy: Relevancia - ColorAndPrice: Color y precio - Name: Nombre - Price: Precio + relevancy: Relevancia + colorAndPrice: Color y precio + name: Nombre + price: Precio Order: Orden Order by: Ordenar por Plant: Planta From 3222a7a42cc40effd9291964e0ebf6ddc08f2fdd Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Mon, 12 Aug 2024 11:51:21 +0200 Subject: [PATCH 23/60] fix(orderLines): reload when delete and redirect when confirm --- src/pages/Order/Card/OrderDescriptor.vue | 1 - src/pages/Order/Card/OrderLines.vue | 136 ++++++++++++----------- 2 files changed, 69 insertions(+), 68 deletions(-) diff --git a/src/pages/Order/Card/OrderDescriptor.vue b/src/pages/Order/Card/OrderDescriptor.vue index 8d2b5309e..a035971b0 100644 --- a/src/pages/Order/Card/OrderDescriptor.vue +++ b/src/pages/Order/Card/OrderDescriptor.vue @@ -82,7 +82,6 @@ function ticketFilter(order) { ref="getTotalRef" :url="`Orders/${entityId}/getTotal`" @on-fetch="(response) => (total = response)" - auto-load /> <CardDescriptor ref="descriptor" diff --git a/src/pages/Order/Card/OrderLines.vue b/src/pages/Order/Card/OrderLines.vue index d4a5c4b1d..dab4a959c 100644 --- a/src/pages/Order/Card/OrderLines.vue +++ b/src/pages/Order/Card/OrderLines.vue @@ -1,25 +1,26 @@ <script setup> -import { useRoute } from 'vue-router'; +import { useRoute, useRouter } from 'vue-router'; import { useI18n } from 'vue-i18n'; import { ref, computed, watch } from 'vue'; import { useQuasar } from 'quasar'; -import { useRouter } from 'vue-router'; +import axios from 'axios'; +import { useStateStore } from 'stores/useStateStore'; +import { useArrayData } from 'composables/useArrayData'; +import { toCurrency, toDate } from 'src/filters'; import VnConfirm from 'components/ui/VnConfirm.vue'; -import { toCurrency, toDate } from 'src/filters'; -import axios from 'axios'; import VnTable from 'src/components/VnTable/VnTable.vue'; import FetchData from 'src/components/FetchData.vue'; import VnImg from 'src/components/ui/VnImg.vue'; import VnLv from 'src/components/ui/VnLv.vue'; import FetchedTags from 'src/components/ui/FetchedTags.vue'; -import { useStateStore } from 'stores/useStateStore'; const router = useRouter(); const stateStore = useStateStore(); const route = useRoute(); const { t } = useI18n(); const quasar = useQuasar(); +const descriptorData = useArrayData('orderData'); const componentKey = ref(0); const tableLinesRef = ref(); const order = ref(); @@ -27,6 +28,8 @@ const orderSummary = ref({ total: null, vat: null, }); +const getTotalRef = ref(); +const getVATRef = ref(); const lineFilter = ref({ include: [ @@ -195,6 +198,9 @@ async function remove(item) { type: 'positive', }); tableLinesRef.value.reload(); + descriptorData.fetch({}); + getTotalRef.value.fetch(); + getVATRef.value.fetch(); } async function confirmOrder() { @@ -203,6 +209,12 @@ async function confirmOrder() { message: t('globals.confirm'), type: 'positive', }); + router.push({ + name: 'TicketList', + query: { + table: JSON.stringify({ clientFk: descriptorData.store.data.clientFk }), + }, + }); } watch( @@ -223,90 +235,80 @@ watch( auto-load /> <FetchData + ref="getTotalRef" :key="componentKey" :url="`Orders/${route.params.id}/getTotal`" @on-fetch="(data) => (orderSummary.total = data)" auto-load /> <FetchData + ref="getVATRef" :key="componentKey" :url="`Orders/${route.params.id}/getVAT`" @on-fetch="(data) => (orderSummary.vat = data)" auto-load /> <QDrawer side="right" :width="270" v-model="stateStore.rightDrawer"> - <QCard class="order-lines-summary q-pa-lg"> + <QCard + class="order-lines-summary q-pa-lg" + v-if="orderSummary.vat && orderSummary.total" + > <p class="header text-right block"> {{ t('summary') }} </p> <VnLv - v-if="orderSummary.vat && orderSummary.total" :label="t('subtotal') + ': '" :value="toCurrency(orderSummary.total - orderSummary.vat)" /> - <VnLv - v-if="orderSummary.vat" - :label="t('VAT') + ': '" - :value="toCurrency(orderSummary?.vat)" - /> - <VnLv - v-if="orderSummary.total" - :label="t('total') + ': '" - :value="toCurrency(orderSummary?.total)" - /> + <VnLv :label="t('VAT') + ': '" :value="toCurrency(orderSummary?.vat)" /> + <VnLv :label="t('total') + ': '" :value="toCurrency(orderSummary?.total)" /> </QCard> </QDrawer> - <QPage :key="componentKey" class="column items-center"> - <div class="order-list full-width"> - <div v-if="!orderSummary.total" class="no-result"> - {{ t('globals.noResults') }} - </div> - <VnTable - ref="tableLinesRef" - data-key="OrderLines" - url="OrderRows" - :columns="columns" - :right-search="false" - :use-model="true" - auto-load - :user-filter="lineFilter" - > - <template #column-image="{ row }"> - <div class="image-wrapper"> - <VnImg :id="parseInt(row?.item?.image)" class="rounded" /> - </div> - </template> - <template #column-itemFk="{ row }"> - <div class="row column full-width justify-between items-start"> - {{ row?.item?.name }} - <div v-if="row?.item?.subName" class="subName"> - {{ row?.item?.subName.toUpperCase() }} - </div> - </div> - <FetchedTags :item="row?.item" :max-length="6" /> - </template> - <template #column-amount="{ row }"> - {{ toCurrency(row.quantity * row.price) }} - </template> - <template #column-tableActions="{ row }"> - <QIcon - v-if="row.order?.isConfirmed === 0" - name="delete" - icon="delete" - @click="confirmRemove(row)" - class="cursor-pointer" - /> - </template> - </VnTable> - </div> - <QPageSticky :offset="[20, 20]" v-if="!order?.isConfirmed" style="z-index: 2"> - <QBtn fab icon="check" color="primary" @click="confirmOrder()" /> - <QTooltip> - {{ t('confirm') }} - </QTooltip> - </QPageSticky> - </QPage> + <VnTable + ref="tableLinesRef" + data-key="OrderLines" + url="OrderRows" + :columns="columns" + :right-search="false" + :use-model="true" + auto-load + :user-filter="lineFilter" + > + <template #column-image="{ row }"> + <div class="image-wrapper"> + <VnImg :id="parseInt(row?.item?.image)" class="rounded" /> + </div> + </template> + + <template #column-itemFk="{ row }"> + <div class="row column full-width justify-between items-start"> + {{ row?.item?.name }} + <div v-if="row?.item?.subName" class="subName"> + {{ row?.item?.subName.toUpperCase() }} + </div> + </div> + <FetchedTags :item="row?.item" :max-length="6" /> + </template> + <template #column-amount="{ row }"> + {{ toCurrency(row.quantity * row.price) }} + </template> + <template #column-tableActions="{ row }"> + <QIcon + v-if="row.order?.isConfirmed === 0" + name="delete" + icon="delete" + @click="confirmRemove(row)" + class="cursor-pointer" + /> + </template> + </VnTable> + <QPageSticky :offset="[20, 20]" v-if="!order?.isConfirmed" style="z-index: 2"> + <QBtn fab icon="check" color="primary" @click="confirmOrder()" /> + <QTooltip> + {{ t('confirm') }} + </QTooltip> + </QPageSticky> </template> <style lang="scss" scoped> From 94878b700b0ead02425718e75d0dd8d995daace6 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Mon, 12 Aug 2024 12:39:06 +0200 Subject: [PATCH 24/60] refs #7355 account cards loads --- src/pages/Account/AccountAcls.vue | 1 - src/pages/Account/AccountAliasList.vue | 1 - src/pages/Account/Role/AccountRoles.vue | 1 - 3 files changed, 3 deletions(-) diff --git a/src/pages/Account/AccountAcls.vue b/src/pages/Account/AccountAcls.vue index 080956d46..fdb0ecee1 100644 --- a/src/pages/Account/AccountAcls.vue +++ b/src/pages/Account/AccountAcls.vue @@ -145,7 +145,6 @@ const deleteAcl = async ({ id }) => { order="id DESC" :columns="columns" default-mode="table" - auto-load :right-search="true" :is-editable="true" :use-model="true" diff --git a/src/pages/Account/AccountAliasList.vue b/src/pages/Account/AccountAliasList.vue index b86580017..b8d594d6d 100644 --- a/src/pages/Account/AccountAliasList.vue +++ b/src/pages/Account/AccountAliasList.vue @@ -70,7 +70,6 @@ const columns = computed(() => [ order="id DESC" :columns="columns" default-mode="table" - auto-load redirect="account/alias" :is-editable="true" :use-model="true" diff --git a/src/pages/Account/Role/AccountRoles.vue b/src/pages/Account/Role/AccountRoles.vue index 0604b71af..2f80606b4 100644 --- a/src/pages/Account/Role/AccountRoles.vue +++ b/src/pages/Account/Role/AccountRoles.vue @@ -97,7 +97,6 @@ const exprBuilder = (param, value) => { order="id ASC" :columns="columns" default-mode="table" - auto-load redirect="account/role" /> </template> From a46198e2d0170cd7ae3c41619d54f34b88967c12 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Tue, 13 Aug 2024 12:25:08 +0200 Subject: [PATCH 25/60] feat: add recover password and reset password --- src/components/ui/VnOutForm.vue | 32 ++++++++++ src/i18n/locale/en.yml | 16 +++-- src/i18n/locale/es.yml | 16 +++-- src/pages/Login/LoginMain.vue | 15 ++++- src/pages/Login/RecoverPassword.vue | 59 +++++++++++++++++ src/pages/Login/ResetPassword.vue | 99 +++++++++++++++++++++++++++++ src/pages/Login/TwoFactor.vue | 51 ++++++--------- src/router/index.js | 2 +- src/router/routes.js | 12 ++++ 9 files changed, 256 insertions(+), 46 deletions(-) create mode 100644 src/components/ui/VnOutForm.vue create mode 100644 src/pages/Login/RecoverPassword.vue create mode 100644 src/pages/Login/ResetPassword.vue diff --git a/src/components/ui/VnOutForm.vue b/src/components/ui/VnOutForm.vue new file mode 100644 index 000000000..b44445682 --- /dev/null +++ b/src/components/ui/VnOutForm.vue @@ -0,0 +1,32 @@ +<script setup> +const emit = defineEmits(['submit']); +defineProps({ + icon: { type: String, required: false, default: 'phonelink_lock' }, + title: { type: String, required: true }, +}); +</script> +<template> + <QForm @submit="emit('submit')" class="q-gutter-y-md q-pa-lg formCard"> + <div class="column items-center"> + <QIcon v-if="icon != false" :name="icon" size="xl" color="primary" /> + <h5 class="text-center q-my-md"> + {{ title }} + </h5> + </div> + <slot name="default"></slot> + <div class="q-mt-lg"> + <slot name="buttons"></slot> + </div> + </QForm> +</template> +<style lang="scss" scoped> +.formCard { + max-width: 350px; + min-width: 300px; +} +@media (max-width: $breakpoint-xs-max) { + .formCard { + min-width: 100%; + } +} +</style> diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml index 12680d0cb..ee53da80c 100644 --- a/src/i18n/locale/en.yml +++ b/src/i18n/locale/en.yml @@ -251,6 +251,9 @@ globals: privileges: Privileges ldap: LDAP samba: Samba + twoFactor: Two factor + recoverPassword: Recover password + resetPassword: Reset password created: Created worker: Worker now: Now @@ -288,14 +291,17 @@ twoFactor: explanation: >- Please, enter the verification code that we have sent to your email in the next 5 minutes - pageTitles: - twoFactor: Two-Factor verifyEmail: pageTitles: verifyEmail: Email verification -dashboard: - pageTitles: - +recoverPassword: + userOrEmail: User or recovery email + explanation: >- + We will sent you an email to recover your password +resetPassword: + repeatPassword: Repeat password + passwordNotMatch: Passwords don't match + passwordChanged: Password changed customer: list: phone: Phone diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml index 747a10d51..71e227fbd 100644 --- a/src/i18n/locale/es.yml +++ b/src/i18n/locale/es.yml @@ -253,6 +253,9 @@ globals: packages: Bultos ldap: LDAP samba: Samba + twoFactor: Doble factor + recoverPassword: Recuperar contraseña + resetPassword: Restablecer contraseña created: Fecha creación worker: Trabajador now: Ahora @@ -289,14 +292,17 @@ twoFactor: validate: Validar insert: Introduce el código de verificación explanation: Por favor introduce el código de verificación que te hemos enviado a tu email en los próximos 5 minutos - pageTitles: - twoFactor: Doble factor verifyEmail: pageTitles: verifyEmail: Verificación de correo -dashboard: - pageTitles: - +recoverPassword: + userOrEmail: Usuario o correo de recuperación + explanation: >- + Te enviaremos un correo para restablecer tu contraseña +resetPassword: + repeatPassword: Repetir contraseña + passwordNotMatch: Las contraseñas no coinciden + passwordChanged: Contraseña cambiada customer: list: phone: Teléfono diff --git a/src/pages/Login/LoginMain.vue b/src/pages/Login/LoginMain.vue index 4eb21f573..c17201969 100644 --- a/src/pages/Login/LoginMain.vue +++ b/src/pages/Login/LoginMain.vue @@ -72,7 +72,8 @@ async function onSubmit() { :rules="[(val) => (val && val.length > 0) || t('login.fieldRequired')]" class="red" /> - <div> + <QToggle v-model="keepLogin" :label="t('login.keepLogin')" /> + <div class="column flex-center q-mt-lg"> <QBtn :label="t('login.submit')" type="submit" @@ -81,11 +82,15 @@ async function onSubmit() { rounded unelevated /> + <RouterLink + class="q-mt-md text-primary" + :to="`/recoverPassword?user=${username}`" + > + {{ t('I do not remember my password') }} + </RouterLink> </div> - <QToggle v-model="keepLogin" :label="t('login.keepLogin')" /> </QForm> </template> - <style lang="scss" scoped> .formCard { max-width: 350px; @@ -101,3 +106,7 @@ async function onSubmit() { } } </style> +<i18n> +es: + I do not remember my password: No recuerdo mi contraseña +</i18n> diff --git a/src/pages/Login/RecoverPassword.vue b/src/pages/Login/RecoverPassword.vue new file mode 100644 index 000000000..5e0fd367a --- /dev/null +++ b/src/pages/Login/RecoverPassword.vue @@ -0,0 +1,59 @@ +<script setup> +import { ref } from 'vue'; +import { useQuasar } from 'quasar'; +import { useI18n } from 'vue-i18n'; +import { useRouter, useRoute } from 'vue-router'; +import axios from 'axios'; +import VnInput from 'src/components/common/VnInput.vue'; +import VnOutForm from 'src/components/ui/VnOutForm.vue'; + +const quasar = useQuasar(); +const router = useRouter(); +const route = useRoute(); +const { t } = useI18n(); + +const user = ref(route.query.user); + +async function onSubmit() { + try { + await axios.post('VnUsers/recoverPassword', { user: user.value, app: 'lilium' }); + router.push('Login'); + quasar.notify({ + message: t('globals.notificationSent'), + type: 'positive', + }); + } catch (e) { + quasar.notify({ + message: e.response?.data?.error.message, + type: 'negative', + }); + } +} +</script> +<template> + <VnOutForm @submit="onSubmit" :title="t('globals.pageTitles.recoverPassword')"> + <template #default> + <VnInput + v-model="user" + :label="t('recoverPassword.userOrEmail')" + :hint="t('recoverPassword.explanation')" + autofocus + required + > + <template #prepend> + <QIcon name="contact_mail" /> + </template> + </VnInput> + </template> + <template #buttons> + <QBtn + :label="t('globals.pageTitles.recoverPassword')" + type="submit" + color="primary" + class="full-width q-mt-md" + rounded + unelevated + /> + </template> + </VnOutForm> +</template> diff --git a/src/pages/Login/ResetPassword.vue b/src/pages/Login/ResetPassword.vue new file mode 100644 index 000000000..eff718e97 --- /dev/null +++ b/src/pages/Login/ResetPassword.vue @@ -0,0 +1,99 @@ +<script setup> +import { ref, onMounted } from 'vue'; +import { useQuasar } from 'quasar'; +import { useI18n } from 'vue-i18n'; +import { useRouter, useRoute } from 'vue-router'; +import axios from 'axios'; + +import VnInput from 'components/common/VnInput.vue'; +import VnOutForm from 'components/ui/VnOutForm.vue'; + +const quasar = useQuasar(); +const router = useRouter(); +const route = useRoute(); +const { t } = useI18n(); + +const newPassword = ref(); +const repeatPassword = ref(); +const passRequirements = ref({}); + +onMounted(async () => { + passRequirements.value = (await axios('UserPasswords/findOne')).data; +}); + +async function onSubmit() { + if (newPassword.value != repeatPassword.value) + return quasar.notify({ + message: t('resetPassword.passwordNotMatch'), + type: 'negative', + }); + + const headers = { + Authorization: route.query.access_token, + }; + + try { + console.log('newPassword: ', newPassword); + await axios.post( + 'VnUsers/reset-password', + { newPassword: newPassword.value }, + { headers } + ); + router.push('Login'); + quasar.notify({ + message: t('resetPassword.passwordChanged'), + type: 'positive', + }); + } catch (e) { + quasar.notify({ + message: e.response?.data?.error.message, + type: 'negative', + }); + } +} +</script> +<template> + <VnOutForm @submit="onSubmit" :title="t('globals.pageTitles.resetPassword')"> + <template #default> + <VnInput + type="password" + :label="t('login.password')" + v-model="newPassword" + :info=" + t('passwordRequirements', { + length: passRequirements.length, + nAlpha: passRequirements.nAlpha, + nUpper: passRequirements.nUpper, + nDigits: passRequirements.nDigits, + nPunct: passRequirements.nPunct, + }) + " + required + > + <template #prepend> + <QIcon name="password" /> + </template> + </VnInput> + <VnInput + type="password" + :label="t('resetPassword.repeatPassword')" + v-model="repeatPassword" + required + > + <template #prepend> + <QIcon name="password" /> + </template> + </VnInput> + </template> + <template #buttons> + <QBtn + :label="t('globals.pageTitles.resetPassword')" + type="submit" + color="primary" + class="full-width q-mt-md" + rounded + unelevated + /> + </template> + </VnOutForm> +</template> diff --git a/src/pages/Login/TwoFactor.vue b/src/pages/Login/TwoFactor.vue index 31b4ccc79..dd404094f 100644 --- a/src/pages/Login/TwoFactor.vue +++ b/src/pages/Login/TwoFactor.vue @@ -8,6 +8,7 @@ import axios from 'axios'; import { useSession } from 'src/composables/useSession'; import { useLogin } from 'src/composables/useLogin'; import VnInput from 'src/components/common/VnInput.vue'; +import VnOutForm from 'src/components/ui/VnOutForm.vue'; const quasar = useQuasar(); const session = useSession(); @@ -38,24 +39,22 @@ async function onSubmit() { } </script> <template> - <QForm @submit="onSubmit" class="q-gutter-y-md q-pa-lg formCard"> - <div class="column items-center"> - <QIcon name="phonelink_lock" size="xl" color="primary" /> - <h5 class="text-center q-my-md">{{ t('twoFactor.insert') }}</h5> - </div> - <VnInput - v-model="code" - :hint="t('twoFactor.explanation')" - mask="# # # # # #" - fill-mask - unmasked-value - autofocus - > - <template #prepend> - <QIcon name="lock" /> - </template> - </VnInput> - <div class="q-mt-xl"> + <VnOutForm @submit="onSubmit" :title="t('twoFactor.insert')"> + <template #default> + <VnInput + v-model="code" + :hint="t('twoFactor.explanation')" + mask="# # # # # #" + fill-mask + unmasked-value + autofocus + > + <template #prepend> + <QIcon name="lock" /> + </template> + </VnInput> + </template> + <template #buttons> <QBtn :label="t('twoFactor.validate')" type="submit" @@ -64,18 +63,6 @@ async function onSubmit() { rounded unelevated /> - </div> - </QForm> + </template> + </VnOutForm> </template> -<style lang="scss" scoped> -.formCard { - max-width: 350px; - min-width: 300px; -} - -@media (max-width: $breakpoint-xs-max) { - .formCard { - min-width: 100%; - } -} -</style> diff --git a/src/router/index.js b/src/router/index.js index faa3ab5d4..686da2dde 100644 --- a/src/router/index.js +++ b/src/router/index.js @@ -46,7 +46,7 @@ export { Router }; export default route(function (/* { store, ssrContext } */) { Router.beforeEach(async (to, from, next) => { const { isLoggedIn } = session; - const outLayout = ['Login', 'TwoFactor', 'VerifyEmail']; + const outLayout = Router.options.routes[0].children.map((r) => r.name); if (!isLoggedIn() && !outLayout.includes(to.name)) { return next({ name: 'Login', query: { redirect: to.fullPath } }); } diff --git a/src/router/routes.js b/src/router/routes.js index 805eefb8c..cced308b5 100644 --- a/src/router/routes.js +++ b/src/router/routes.js @@ -46,6 +46,18 @@ const routes = [ meta: { title: 'verifyEmail' }, component: () => import('../pages/Login/VerifyEmail.vue'), }, + { + path: '/recoverPassword', + name: 'RecoverPassword', + meta: { title: 'recoverPassword' }, + component: () => import('../pages/Login/RecoverPassword.vue'), + }, + { + path: '/resetPassword', + name: 'ResetPassword', + meta: { title: 'resetPassword' }, + component: () => import('../pages/Login/ResetPassword.vue'), + }, ], }, { From dc1accff7ab684c9babbd52c94d5085e592b5b3b Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Tue, 13 Aug 2024 12:25:37 +0200 Subject: [PATCH 26/60] test: add recover and reset password e2e --- .../integration/{ => outLogin}/login.spec.js | 0 .../integration/{ => outLogin}/logout.spec.js | 0 .../outLogin/recoverPassword.spec.js | 56 +++++++++++++++++++ 3 files changed, 56 insertions(+) rename test/cypress/integration/{ => outLogin}/login.spec.js (100%) rename test/cypress/integration/{ => outLogin}/logout.spec.js (100%) create mode 100755 test/cypress/integration/outLogin/recoverPassword.spec.js diff --git a/test/cypress/integration/login.spec.js b/test/cypress/integration/outLogin/login.spec.js similarity index 100% rename from test/cypress/integration/login.spec.js rename to test/cypress/integration/outLogin/login.spec.js diff --git a/test/cypress/integration/logout.spec.js b/test/cypress/integration/outLogin/logout.spec.js similarity index 100% rename from test/cypress/integration/logout.spec.js rename to test/cypress/integration/outLogin/logout.spec.js diff --git a/test/cypress/integration/outLogin/recoverPassword.spec.js b/test/cypress/integration/outLogin/recoverPassword.spec.js new file mode 100755 index 000000000..09111f0d7 --- /dev/null +++ b/test/cypress/integration/outLogin/recoverPassword.spec.js @@ -0,0 +1,56 @@ +/// <reference types="cypress" /> +describe('Recover Password', () => { + const username = 'trainee'; + beforeEach(() => { + cy.visit('/#/login'); + cy.get('#switchLanguage').click(); + cy.get('.q-menu > :nth-child(1) > .q-item').click(); + }); + + it('should go to recover password section and send notification', () => { + cy.get('input[aria-label="Username"]').type(username); + cy.get(`a[href="#/recoverPassword?user=${username}"]`).click(); + + cy.waitForElement('input[aria-label="User or recovery email"]'); + cy.get('input[aria-label="User or recovery email"]').should( + 'have.value', + username + ); + + cy.get('button[type="submit"]').click(); + cy.get('.q-notification__message').should('have.text', 'Notification sent'); + }); + + it('should change password to user', () => { + // Get token from mail + cy.request( + `http://localhost:3000/api/Mails?filter=%7B%22where%22%3A%20%7B%22receiver%22%3A%20%22${username}%40mydomain.com%22%7D%2C%20%22order%22%3A%20%5B%22id%20DESC%22%5D%7D&access_token=DEFAULT_TOKEN` + ).then((response) => { + cy.log('response: ', response.body[0].body); + const regex = /access_token=([a-zA-Z0-9]+)/; + const [match] = response.body[0].body.match(regex); + cy.log('response: ', match); + + const resetUrl = '/#/resetPassword?' + match; + cy.visit(resetUrl); + }); + + // Change password + const newPassword = 'test.1234'; + cy.waitForElement('input[aria-label="Password"]'); + cy.waitForElement('input[aria-label="Repeat password"]'); + cy.get('input[aria-label="Password"]').type(newPassword); + cy.get('input[aria-label="Repeat password"]').type(newPassword); + + cy.get('button[type="submit"]').click(); + cy.get('.q-notification__message').should('have.text', 'Password changed'); + + // Try to login successfully + cy.get('input[aria-label="Username"]').type(username); + cy.get('input[aria-label="Password"]').type(newPassword); + cy.get('button[type="submit"]').click(); + cy.url().should('contain', '/dashboard'); + + // ❗The password cannot be returned because "nightmare" does not meet the requirements + }); +}); From b828fd35b7b60b88475972ada0974986f1cb5d16 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Tue, 13 Aug 2024 12:25:46 +0200 Subject: [PATCH 27/60] test: add two factor e2e --- .../integration/outLogin/twoFactor.spec.js | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100755 test/cypress/integration/outLogin/twoFactor.spec.js diff --git a/test/cypress/integration/outLogin/twoFactor.spec.js b/test/cypress/integration/outLogin/twoFactor.spec.js new file mode 100755 index 000000000..f998dda39 --- /dev/null +++ b/test/cypress/integration/outLogin/twoFactor.spec.js @@ -0,0 +1,57 @@ +/// <reference types="cypress" /> +describe('Two Factor', () => { + const username = 'sysadmin'; + const userId = 66; + beforeEach(() => { + cy.visit('/#/login'); + cy.get('#switchLanguage').click(); + cy.get('.q-menu > :nth-child(1) > .q-item').click(); + }); + + it('should enable two factor to sysadmin', () => { + cy.request( + 'PATCH', + `http://localhost:3000/api/VnUsers/${userId}/update-user?access_token=DEFAULT_TOKEN`, + { twoFactor: 'email' } + ); + }); + + it('should fail when login with incorrect two factor', () => { + cy.get('input[aria-label="Username"]').type(username); + cy.get('input[aria-label="Password"]').type('nightmare'); + cy.get('button[type="submit"]').click(); + cy.get('.q-notification__message').should( + 'have.text', + 'Two-factor verification required' + ); + + cy.get('input[type="text"]').type('123456'); + cy.get('button[type="submit"]').click(); + cy.url().should('contain', '/twoFactor'); + }); + + it('should login with correct two factor', () => { + cy.get('input[aria-label="Username"]').type(username); + cy.get('input[aria-label="Password"]').type('nightmare'); + cy.get('button[type="submit"]').click(); + cy.get('.q-notification__message').should( + 'have.text', + 'Two-factor verification required' + ); + + // Get code from mail + cy.request( + `http://localhost:3000/api/Mails?filter=%7B%22where%22%3A%20%7B%22receiver%22%3A%20%22${username}%40mydomain.com%22%7D%2C%20%22order%22%3A%20%5B%22id%20DESC%22%5D%7D&access_token=DEFAULT_TOKEN` + ).then((response) => { + const tempDiv = document.createElement('div'); + tempDiv.innerHTML = response.body[0].body; + const codeElement = tempDiv.querySelector('.code'); + const code = codeElement ? codeElement.textContent.trim() : null; + cy.log('response: ', code); + + cy.get('input[type="text"]').type(code); + cy.get('button[type="submit"]').click(); + cy.url().should('contain', '/dashboard'); + }); + }); +}); From aae881c24a1fb7ac372981f07aae7b29976f2b25 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Tue, 13 Aug 2024 12:59:22 +0200 Subject: [PATCH 28/60] fix(account_card): redirection --- src/pages/Account/Card/AccountCard.vue | 3 +-- src/pages/Account/Role/Card/RoleCard.vue | 2 -- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/pages/Account/Card/AccountCard.vue b/src/pages/Account/Card/AccountCard.vue index a76d549a5..a9857b283 100644 --- a/src/pages/Account/Card/AccountCard.vue +++ b/src/pages/Account/Card/AccountCard.vue @@ -13,10 +13,9 @@ const { t } = useI18n(); search-data-key="AccountUsers" :searchbar-props="{ url: 'VnUsers/preview', - redirect: !!customRouteRedirectName, - customRouteRedirectName, label: t('account.search'), info: t('account.searchInfo'), + searchUrl: 'table', }" /> </template> diff --git a/src/pages/Account/Role/Card/RoleCard.vue b/src/pages/Account/Role/Card/RoleCard.vue index 0c6e3aca0..ba98b9c19 100644 --- a/src/pages/Account/Role/Card/RoleCard.vue +++ b/src/pages/Account/Role/Card/RoleCard.vue @@ -12,8 +12,6 @@ const { t } = useI18n(); search-data-key="AccountRoles" :searchbar-props="{ url: 'VnRoles', - redirect: !!customRouteRedirectName, - customRouteRedirectName, label: t('role.searchRoles'), info: t('role.searchInfo'), }" From 405092f66d2cb145530e34797f0abfca2c1f45f8 Mon Sep 17 00:00:00 2001 From: carlossa <carlossa@verdnatura.es> Date: Tue, 13 Aug 2024 13:11:23 +0200 Subject: [PATCH 29/60] fix: account subsections cards --- src/pages/Account/AccountAliasList.vue | 2 +- src/pages/Account/Alias/Card/AliasCard.vue | 20 +++----------------- src/pages/Account/Role/Card/RoleCard.vue | 1 + 3 files changed, 5 insertions(+), 18 deletions(-) diff --git a/src/pages/Account/AccountAliasList.vue b/src/pages/Account/AccountAliasList.vue index b8d594d6d..b6f7b219c 100644 --- a/src/pages/Account/AccountAliasList.vue +++ b/src/pages/Account/AccountAliasList.vue @@ -60,7 +60,7 @@ const columns = computed(() => [ <VnTable ref="tableRef" data-key="AccountAliasList" - :url="`MailAliases`" + url="MailAliases" :create="{ urlCreate: 'MailAliases', title: 'Create MailAlias', diff --git a/src/pages/Account/Alias/Card/AliasCard.vue b/src/pages/Account/Alias/Card/AliasCard.vue index feb9b6240..65951b3bf 100644 --- a/src/pages/Account/Alias/Card/AliasCard.vue +++ b/src/pages/Account/Alias/Card/AliasCard.vue @@ -1,22 +1,8 @@ <script setup> import { useI18n } from 'vue-i18n'; -import { useRoute } from 'vue-router'; -import { computed } from 'vue'; - import VnCard from 'components/common/VnCard.vue'; import AliasDescriptor from './AliasDescriptor.vue'; - const { t } = useI18n(); -const route = useRoute(); - -const routeName = computed(() => route.name); -const customRouteRedirectName = computed(() => { - return routeName.value; -}); -const searchBarDataKeys = { - AliasBasicData: 'AliasBasicData', - AliasUsers: 'AliasUsers', -}; </script> <template> @@ -24,12 +10,12 @@ const searchBarDataKeys = { data-key="Alias" base-url="MailAliases" :descriptor="AliasDescriptor" - :search-data-key="searchBarDataKeys[routeName]" + search-data-key="AccountAliasList" :searchbar-props="{ - redirect: !!customRouteRedirectName, - customRouteRedirectName, + url: 'MailAliases', info: t('mailAlias.searchInfo'), label: t('mailAlias.search'), + searchUrl: 'table', }" /> </template> diff --git a/src/pages/Account/Role/Card/RoleCard.vue b/src/pages/Account/Role/Card/RoleCard.vue index ba98b9c19..35f9a1f27 100644 --- a/src/pages/Account/Role/Card/RoleCard.vue +++ b/src/pages/Account/Role/Card/RoleCard.vue @@ -14,6 +14,7 @@ const { t } = useI18n(); url: 'VnRoles', label: t('role.searchRoles'), info: t('role.searchInfo'), + searchUrl: 'table', }" /> </template> From fdb508de97570d64d0a9752e82acd7eb02882613 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Tue, 13 Aug 2024 16:31:34 +0200 Subject: [PATCH 30/60] delete cy.logs --- test/cypress/integration/outLogin/recoverPassword.spec.js | 2 -- test/cypress/integration/outLogin/twoFactor.spec.js | 1 - 2 files changed, 3 deletions(-) diff --git a/test/cypress/integration/outLogin/recoverPassword.spec.js b/test/cypress/integration/outLogin/recoverPassword.spec.js index 09111f0d7..eec81b661 100755 --- a/test/cypress/integration/outLogin/recoverPassword.spec.js +++ b/test/cypress/integration/outLogin/recoverPassword.spec.js @@ -26,10 +26,8 @@ describe('Recover Password', () => { cy.request( `http://localhost:3000/api/Mails?filter=%7B%22where%22%3A%20%7B%22receiver%22%3A%20%22${username}%40mydomain.com%22%7D%2C%20%22order%22%3A%20%5B%22id%20DESC%22%5D%7D&access_token=DEFAULT_TOKEN` ).then((response) => { - cy.log('response: ', response.body[0].body); const regex = /access_token=([a-zA-Z0-9]+)/; const [match] = response.body[0].body.match(regex); - cy.log('response: ', match); const resetUrl = '/#/resetPassword?' + match; cy.visit(resetUrl); diff --git a/test/cypress/integration/outLogin/twoFactor.spec.js b/test/cypress/integration/outLogin/twoFactor.spec.js index f998dda39..4d8561f0f 100755 --- a/test/cypress/integration/outLogin/twoFactor.spec.js +++ b/test/cypress/integration/outLogin/twoFactor.spec.js @@ -47,7 +47,6 @@ describe('Two Factor', () => { tempDiv.innerHTML = response.body[0].body; const codeElement = tempDiv.querySelector('.code'); const code = codeElement ? codeElement.textContent.trim() : null; - cy.log('response: ', code); cy.get('input[type="text"]').type(code); cy.get('button[type="submit"]').click(); From 3cbdf25410c0846df4a8223cee988d5a88088f15 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Tue, 13 Aug 2024 16:40:02 +0200 Subject: [PATCH 31/60] delete name default --- src/components/ui/VnOutForm.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/ui/VnOutForm.vue b/src/components/ui/VnOutForm.vue index b44445682..e7ad441d0 100644 --- a/src/components/ui/VnOutForm.vue +++ b/src/components/ui/VnOutForm.vue @@ -13,7 +13,7 @@ defineProps({ {{ title }} </h5> </div> - <slot name="default"></slot> + <slot></slot> <div class="q-mt-lg"> <slot name="buttons"></slot> </div> From 06c0f491281df78a661a71a0a9bb21c4e25411b1 Mon Sep 17 00:00:00 2001 From: jgallego <jgallego@verdnatura.es> Date: Fri, 16 Aug 2024 10:10:40 +0200 Subject: [PATCH 32/60] feat: refs #7346 add seriaType option --- src/pages/InvoiceOut/InvoiceOutGlobalForm.vue | 38 ++++++++++++++++--- src/pages/InvoiceOut/InvoiceOutList.vue | 4 +- src/stores/invoiceOutGlobal.js | 11 +++++- 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue b/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue index 23c63ee6a..5fc519061 100644 --- a/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue +++ b/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue @@ -20,20 +20,18 @@ const { initialDataLoading, formInitialData, invoicing, status } = const { makeInvoice, setStatusValue } = invoiceOutGlobalStore; const clientsToInvoice = ref('all'); - const companiesOptions = ref([]); - const printersOptions = ref([]); - const clientsOptions = ref([]); - +const serialTypesOptions = ref([]); const formData = ref({}); const optionsInitialData = computed(() => { return ( companiesOptions.value.length > 0 && printersOptions.value.length > 0 && - clientsOptions.value.length > 0 + clientsOptions.value.length > 0 && + serialTypesOptions.value.length > 0 ); }); @@ -60,7 +58,22 @@ onMounted(async () => { /> <FetchData url="Printers" @on-fetch="(data) => (printersOptions = data)" auto-load /> <FetchData url="Clients" @on-fetch="(data) => (clientsOptions = data)" auto-load /> - + <FetchData + url="invoiceOutSerials" + @on-fetch=" + (data) => { + const uniqueTypes = new Set(); + serialTypesOptions = data.filter((item) => { + if (item.type === null || uniqueTypes.has(item.type)) { + return false; + } + uniqueTypes.add(item.type); + return true; + }); + } + " + auto-load + /> <QForm v-if="!initialDataLoading && optionsInitialData" @submit="makeInvoice(formData, clientsToInvoice)" @@ -95,6 +108,17 @@ onMounted(async () => { outlined rounded /> + <VnSelect + :label="t('invoiceOutSerialType')" + v-model="formData.invoiceType" + :options="serialTypesOptions" + option-value="type" + option-label="type" + hide-selected + dense + outlined + rounded + /> <VnInputDate v-model="formData.invoiceDate" :label="t('invoiceDate')" @@ -168,6 +192,7 @@ en: printer: Printer invoiceOut: Invoice out client: Client + invoiceOutSerialType: Serial Type stop: Stop es: @@ -179,5 +204,6 @@ es: printer: Impresora invoiceOut: Facturar client: Cliente + invoiceOutSerialType: Tipo de Serie stop: Parar </i18n> diff --git a/src/pages/InvoiceOut/InvoiceOutList.vue b/src/pages/InvoiceOut/InvoiceOutList.vue index 822f6bb33..b08e12b3e 100644 --- a/src/pages/InvoiceOut/InvoiceOutList.vue +++ b/src/pages/InvoiceOut/InvoiceOutList.vue @@ -198,7 +198,7 @@ watchEffect(selectedRows); :url="`${MODEL}/filter`" :create="{ urlCreate: 'InvoiceOuts/createManualInvoice', - title: t('Create Manual Invoice'), + title: t('Create manual invoice'), onDataSaved: ({ id }) => tableRef.redirect(id), formInitialData: { active: true, @@ -275,10 +275,12 @@ en: fileAllowed: Successful download of CSV file youCanSearchByInvoiceReference: You can search by invoice reference createInvoice: Make invoice + Create manual invoice: Create manual invoice es: searchInvoice: Buscar factura emitida fileDenied: El navegador denegó la descarga de archivos... fileAllowed: Descarga exitosa de archivo CSV youCanSearchByInvoiceReference: Puedes buscar por referencia de la factura createInvoice: Crear factura + Create manual invoice: Crear factura manual </i18n> diff --git a/src/stores/invoiceOutGlobal.js b/src/stores/invoiceOutGlobal.js index bb9a3d376..ca123d0b3 100644 --- a/src/stores/invoiceOutGlobal.js +++ b/src/stores/invoiceOutGlobal.js @@ -19,6 +19,7 @@ export const useInvoiceOutGlobalStore = defineStore({ maxShipped: null, clientId: null, printer: null, + invoiceType: null, }, addresses: [], minInvoicingDate: null, @@ -100,6 +101,7 @@ export const useInvoiceOutGlobalStore = defineStore({ maxShipped: new Date(formData.maxShipped), clientId: formData.clientId ? formData.clientId : null, companyFk: formData.companyFk, + invoiceType: formData.invoiceType, }; this.validateMakeInvoceParams(params, clientsToInvoice); @@ -152,7 +154,13 @@ export const useInvoiceOutGlobalStore = defineStore({ ); throw new Error('Invoice date in the future'); } - + if (!params.invoiceType) { + notify( + 'invoiceOut.globalInvoices.errors.chooseValidinvoiceType', + 'negative' + ); + throw new Error('Invalid Serial Type'); + } if (!params.companyFk) { notify('invoiceOut.globalInvoices.errors.chooseValidCompany', 'negative'); throw new Error('Invalid company'); @@ -180,6 +188,7 @@ export const useInvoiceOutGlobalStore = defineStore({ invoiceDate: new Date(formData.invoiceDate), maxShipped: new Date(formData.maxShipped), companyFk: formData.companyFk, + invoiceType: formData.invoiceType, }; this.status = 'invoicing'; From ed29088d766d8362c97cdf55e01f384f4c99a4d7 Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Fri, 16 Aug 2024 10:49:52 +0200 Subject: [PATCH 33/60] typo --- src/pages/Login/LoginMain.vue | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/Login/LoginMain.vue b/src/pages/Login/LoginMain.vue index c17201969..44b868ebd 100644 --- a/src/pages/Login/LoginMain.vue +++ b/src/pages/Login/LoginMain.vue @@ -1,6 +1,6 @@ <script setup> import { ref } from 'vue'; -import { Notify, useQuasar } from 'quasar'; +import { Notify } from 'quasar'; import { useI18n } from 'vue-i18n'; import { useRouter } from 'vue-router'; @@ -11,7 +11,6 @@ import VnLogo from 'components/ui/VnLogo.vue'; import VnInput from 'src/components/common/VnInput.vue'; import axios from 'axios'; -const quasar = useQuasar(); const session = useSession(); const loginCache = useLogin(); const router = useRouter(); From 2bea46b601cb98851c1166f9b4a8ade08516d6bf Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Fri, 16 Aug 2024 11:47:32 +0200 Subject: [PATCH 34/60] fix: test --- test/vitest/__tests__/pages/Login/Login.spec.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/test/vitest/__tests__/pages/Login/Login.spec.js b/test/vitest/__tests__/pages/Login/Login.spec.js index 5ab4cee9e..e90a8ee53 100644 --- a/test/vitest/__tests__/pages/Login/Login.spec.js +++ b/test/vitest/__tests__/pages/Login/Login.spec.js @@ -44,10 +44,6 @@ describe('Login', () => { it('should not set the token into session if any error occurred', async () => { vi.spyOn(axios, 'post').mockReturnValue({ data: null }); - vi.spyOn(vm.quasar, 'notify'); - await vm.onSubmit(); - - expect(vm.quasar.notify).not.toHaveBeenCalled(); }); }); From 4b2f4ffac181fd42c7f5ececc2b56c3daed4035e Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Fri, 16 Aug 2024 12:20:08 +0200 Subject: [PATCH 35/60] fix(VnTable): orderBy v-model --- src/components/VnTable/VnTable.vue | 4 ++-- src/pages/Claim/ClaimList.vue | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/VnTable/VnTable.vue b/src/components/VnTable/VnTable.vue index d6b35d4da..6c77d44df 100644 --- a/src/components/VnTable/VnTable.vue +++ b/src/components/VnTable/VnTable.vue @@ -315,7 +315,7 @@ defineExpose({ col?.columnFilter !== false && col?.name !== 'tableActions' " - v-model="orders[col.name]" + v-model="orders[col.orderBy ?? col.name]" :name="col.orderBy ?? col.name" :data-key="$attrs['data-key']" :search-url="searchUrl" @@ -409,7 +409,7 @@ defineExpose({ style="height: 30px" > <VnTableOrder - v-model="orders[col.name]" + v-model="orders[col.orderBy ?? col.name]" :name="col.orderBy ?? col.name" :label="col?.label" :data-key="$attrs['data-key']" diff --git a/src/pages/Claim/ClaimList.vue b/src/pages/Claim/ClaimList.vue index 6fd607da0..d1a08b9c6 100644 --- a/src/pages/Claim/ClaimList.vue +++ b/src/pages/Claim/ClaimList.vue @@ -50,7 +50,7 @@ const columns = computed(() => [ align: 'left', label: t('claim.attendedBy'), name: 'attendedBy', - cardVisible: true, + orderBy: 'workerFk', columnFilter: { component: 'select', attrs: { @@ -63,6 +63,7 @@ const columns = computed(() => [ optionFilter: 'firstName', }, }, + cardVisible: true, }, { align: 'left', From fa0ca732fc7e7b9ec3ab4b108719b66e64ea795e Mon Sep 17 00:00:00 2001 From: alexm <alexm@verdnatura.es> Date: Fri, 16 Aug 2024 12:29:35 +0200 Subject: [PATCH 36/60] feat(FormModel): trim data by default --- src/components/FormModel.vue | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/components/FormModel.vue b/src/components/FormModel.vue index a0f6bf479..22ef1622c 100644 --- a/src/components/FormModel.vue +++ b/src/components/FormModel.vue @@ -87,6 +87,10 @@ const $props = defineProps({ type: Boolean, default: false, }, + defaultTrim: { + type: Boolean, + default: true, + }, }); const emit = defineEmits(['onFetch', 'onDataSaved']); const modelValue = computed( @@ -195,6 +199,7 @@ async function save() { isLoading.value = true; try { + formData.value = trimData(formData.value); const body = $props.mapper ? $props.mapper(formData.value) : formData.value; const method = $props.urlCreate ? 'post' : 'patch'; const url = @@ -253,6 +258,14 @@ function updateAndEmit(evt, val, res) { emit(evt, state.get(modelValue), res); } +function trimData(data) { + if (!$props.defaultTrim) return data; + for (const key in data) { + if (typeof data[key] == 'string') data[key] = data[key].trim(); + } + return data; +} + defineExpose({ save, isLoading, From 74f335b22e3e0c7a0336b3cac017978aaa260077 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Mon, 19 Aug 2024 06:51:52 +0000 Subject: [PATCH 37/60] fix: #6336 ClaimListStates --- src/pages/Claim/ClaimList.vue | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/pages/Claim/ClaimList.vue b/src/pages/Claim/ClaimList.vue index d1a08b9c6..b03dfb226 100644 --- a/src/pages/Claim/ClaimList.vue +++ b/src/pages/Claim/ClaimList.vue @@ -78,6 +78,9 @@ const columns = computed(() => [ { align: 'left', label: t('claim.state'), + format: ({ stateCode }) => + claimFilterRef.value?.states.find(({code}) => code === stateCode) + ?.description, name: 'stateCode', chip: { condition: () => true, From f3d7590edfc191c4e60b26aecd0d1c8c6c8c9bc6 Mon Sep 17 00:00:00 2001 From: Jon <jon@verdnatura.es> Date: Mon, 19 Aug 2024 10:49:04 +0200 Subject: [PATCH 38/60] refactor: refs #7717 delete useless function and import --- src/pages/Order/Card/OrderBasicData.vue | 10 ---------- src/pages/Order/Card/OrderCatalogFilter.vue | 1 - 2 files changed, 11 deletions(-) diff --git a/src/pages/Order/Card/OrderBasicData.vue b/src/pages/Order/Card/OrderBasicData.vue index dfdb5ae48..4bc9e2e43 100644 --- a/src/pages/Order/Card/OrderBasicData.vue +++ b/src/pages/Order/Card/OrderBasicData.vue @@ -64,16 +64,6 @@ const fetchOrderDetails = (order) => { fetchAgencyList(order?.landed, order?.addressFk); }; -const orderMapper = (order) => { - const mappedOrder = { - addressId: order.addressFk, - landed: new Date(order.landed).toISOString(), - }; - if (order.agencyModeFk !== null && order.agencyModeFk !== undefined) { - mappedOrder.agencyModeId = order.agencyModeFk; - } - return mappedOrder; -}; const orderFilter = { include: [ { relation: 'agencyMode', scope: { fields: ['name'] } }, diff --git a/src/pages/Order/Card/OrderCatalogFilter.vue b/src/pages/Order/Card/OrderCatalogFilter.vue index e9987d363..850abb755 100644 --- a/src/pages/Order/Card/OrderCatalogFilter.vue +++ b/src/pages/Order/Card/OrderCatalogFilter.vue @@ -7,7 +7,6 @@ import FetchData from 'components/FetchData.vue'; import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; import VnSelect from 'components/common/VnSelect.vue'; import VnFilterPanelChip from 'components/ui/VnFilterPanelChip.vue'; -import { useValidator } from 'src/composables/useValidator'; import VnInput from 'src/components/common/VnInput.vue'; import getParamWhere from 'src/filters/getParamWhere'; From c19585fb1b5ef85ffeeafbdbb73587ab1e6f1acd Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Mon, 19 Aug 2024 11:36:12 +0200 Subject: [PATCH 39/60] fix: quasar build warnings --- src/components/ui/VnSms.vue | 2 +- src/pages/Account/AccountAcls.vue | 2 - .../Account/Card/AccountDescriptorMenu.vue | 50 +--------------- src/pages/Account/Role/AccountRoles.vue | 2 - .../Customer/Card/CustomerConsumption.vue | 2 - src/pages/Item/Card/ItemBasicData.vue | 1 - src/pages/Monitor/SalesTicketsTable.vue | 57 +------------------ src/pages/Ticket/Card/TicketSale.vue | 1 - src/pages/Zone/Card/ZoneCard.vue | 5 -- 9 files changed, 6 insertions(+), 116 deletions(-) diff --git a/src/components/ui/VnSms.vue b/src/components/ui/VnSms.vue index 81058a6cb..bf6e0695e 100644 --- a/src/components/ui/VnSms.vue +++ b/src/components/ui/VnSms.vue @@ -1,5 +1,5 @@ <script setup> -import { watch, computed } from 'vue'; +import { computed } from 'vue'; import { date } from 'quasar'; import VnPaginate from 'src/components/ui/VnPaginate.vue'; import VnAvatar from '../ui/VnAvatar.vue'; diff --git a/src/pages/Account/AccountAcls.vue b/src/pages/Account/AccountAcls.vue index fdb0ecee1..110358e7f 100644 --- a/src/pages/Account/AccountAcls.vue +++ b/src/pages/Account/AccountAcls.vue @@ -6,7 +6,6 @@ import axios from 'axios'; import useNotify from 'src/composables/useNotify.js'; import { useQuasar } from 'quasar'; -import FetchData from 'components/FetchData.vue'; import VnTable from 'components/VnTable/VnTable.vue'; import VnSearchbar from 'components/ui/VnSearchbar.vue'; import VnConfirm from 'components/ui/VnConfirm.vue'; @@ -24,7 +23,6 @@ const stateStore = useStateStore(); const quasar = useQuasar(); const tableRef = ref(); -const rolesOptions = ref([]); const exprBuilder = (param, value) => { switch (param) { diff --git a/src/pages/Account/Card/AccountDescriptorMenu.vue b/src/pages/Account/Card/AccountDescriptorMenu.vue index f67cc0c6b..0e35d25f3 100644 --- a/src/pages/Account/Card/AccountDescriptorMenu.vue +++ b/src/pages/Account/Card/AccountDescriptorMenu.vue @@ -1,15 +1,12 @@ <script setup> import axios from 'axios'; import { computed, ref, toRefs } from 'vue'; -import { useQuasar } from 'quasar'; import { useI18n } from 'vue-i18n'; import { useVnConfirm } from 'composables/useVnConfirm'; import { useRoute } from 'vue-router'; import { useArrayData } from 'src/composables/useArrayData'; -import CustomerChangePassword from 'src/pages/Customer/components/CustomerChangePassword.vue'; import VnConfirm from 'src/components/ui/VnConfirm.vue'; import useNotify from 'src/composables/useNotify.js'; -const quasar = useQuasar(); const $props = defineProps({ hasAccount: { type: Boolean, @@ -35,7 +32,7 @@ async function updateStatusAccount(active) { account.value.hasAccount = active; const status = active ? 'enable' : 'disable'; - quasar.notify({ + notify({ message: t(`account.card.${status}Account.success`), type: 'positive', }); @@ -44,19 +41,11 @@ async function updateStatusUser(active) { await axios.patch(`VnUsers/${entityId.value}`, { active }); account.value.active = active; const status = active ? 'activate' : 'deactivate'; - quasar.notify({ + notify({ message: t(`account.card.actions.${status}User.success`), type: 'positive', }); } -function setPassword() { - quasar.dialog({ - component: CustomerChangePassword, - componentProps: { - id: entityId.value, - }, - }); -} const showSyncDialog = ref(false); const syncPassword = ref(null); const shouldSyncPassword = ref(false); @@ -66,20 +55,11 @@ async function sync() { await axios.patch(`Accounts/${account.value.name}/sync`, { params, }); - quasar.notify({ + notify({ message: t('account.card.actions.sync.success'), type: 'positive', }); } - -const removeAccount = async () => { - try { - await axios.delete(`VnUsers/${account.value.id}`); - notify(t('Account removed'), 'positive'); - } catch (error) { - console.error('Error deleting user', error); - } -}; </script> <template> <VnConfirm @@ -112,24 +92,6 @@ const removeAccount = async () => { /> </template> </VnConfirm> - <!-- <QItem v-ripple clickable @click="setPassword"> - <QItemSection>{{ t('account.card.actions.setPassword') }}</QItemSection> - </QItem> - <QItem - v-if="!account.hasAccount" - v-ripple - clickable - @click=" - openConfirmationModal( - t('account.card.actions.enableAccount.title'), - t('account.card.actions.enableAccount.subtitle'), - () => updateStatusAccount(true) - ) - " - > - <QItemSection>{{ t('account.card.actions.enableAccount.name') }}</QItemSection> - </QItem> --> - <QItem v-if="account.hasAccount" v-ripple @@ -178,10 +140,4 @@ const removeAccount = async () => { </QItem> <QSeparator /> - <!-- <QItem @click="removeAccount(id)" v-ripple clickable> - <QItemSection avatar> - <QIcon name="delete" /> - </QItemSection> - <QItemSection>{{ t('account.card.actions.delete.name') }}</QItemSection> - </QItem> --> </template> diff --git a/src/pages/Account/Role/AccountRoles.vue b/src/pages/Account/Role/AccountRoles.vue index 2f80606b4..8f3372a6d 100644 --- a/src/pages/Account/Role/AccountRoles.vue +++ b/src/pages/Account/Role/AccountRoles.vue @@ -4,11 +4,9 @@ import { computed, ref } from 'vue'; import VnTable from 'components/VnTable/VnTable.vue'; import { useRoute } from 'vue-router'; import VnSearchbar from 'components/ui/VnSearchbar.vue'; -import { useStateStore } from 'stores/useStateStore'; import { useSummaryDialog } from 'src/composables/useSummaryDialog'; import RoleSummary from './Card/RoleSummary.vue'; const route = useRoute(); -const stateStore = useStateStore(); const { t } = useI18n(); const $props = defineProps({ id: { diff --git a/src/pages/Customer/Card/CustomerConsumption.vue b/src/pages/Customer/Card/CustomerConsumption.vue index 98a3115da..4d3da1116 100644 --- a/src/pages/Customer/Card/CustomerConsumption.vue +++ b/src/pages/Customer/Card/CustomerConsumption.vue @@ -1,8 +1,6 @@ <script setup> -import { useI18n } from 'vue-i18n'; import CustomerConsumptionFilter from './CustomerConsumptionFilter.vue'; import { useStateStore } from 'src/stores/useStateStore'; -const { t } = useI18n(); </script> <template> diff --git a/src/pages/Item/Card/ItemBasicData.vue b/src/pages/Item/Card/ItemBasicData.vue index 89c883295..ae9e45983 100644 --- a/src/pages/Item/Card/ItemBasicData.vue +++ b/src/pages/Item/Card/ItemBasicData.vue @@ -16,7 +16,6 @@ const route = useRoute(); const { t } = useI18n(); const itemTypesOptions = ref([]); -const itemsWithNameOptions = ref([]); const intrastatsOptions = ref([]); const expensesOptions = ref([]); diff --git a/src/pages/Monitor/SalesTicketsTable.vue b/src/pages/Monitor/SalesTicketsTable.vue index 1ca68240b..b301c1145 100644 --- a/src/pages/Monitor/SalesTicketsTable.vue +++ b/src/pages/Monitor/SalesTicketsTable.vue @@ -1,8 +1,6 @@ <script setup> -import { ref, computed, onMounted, reactive } from 'vue'; +import { ref, computed, reactive } from 'vue'; import { useI18n } from 'vue-i18n'; -import { useRouter } from 'vue-router'; - import FetchData from 'components/FetchData.vue'; import WorkerDescriptorProxy from 'src/pages/Worker/Card/WorkerDescriptorProxy.vue'; import CustomerDescriptorProxy from 'src/pages/Customer/Card/CustomerDescriptorProxy.vue'; @@ -11,18 +9,14 @@ import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.v import InvoiceOutDescriptorProxy from 'src/pages/InvoiceOut/Card/InvoiceOutDescriptorProxy.vue'; import ZoneDescriptorProxy from 'src/pages/Zone/Card/ZoneDescriptorProxy.vue'; import TicketSummary from 'src/pages/Ticket/Card/TicketSummary.vue'; -import VnInput from 'src/components/common/VnInput.vue'; -import VnSelect from 'src/components/common/VnSelect.vue'; -import VnInputDate from 'src/components/common/VnInputDate.vue'; import VnTable from 'components/VnTable/VnTable.vue'; - import { useSummaryDialog } from 'src/composables/useSummaryDialog'; import { toDateFormat, toTimeFormat } from 'src/filters/date.js'; import { toCurrency, dateRange } from 'src/filters'; + const DEFAULT_AUTO_REFRESH = 1000; const { t } = useI18n(); const autoRefresh = ref(false); -const router = useRouter(); const paginateRef = ref(null); const workersActiveOptions = ref([]); const provincesOptions = ref([]); @@ -57,46 +51,6 @@ function exprBuilder(param, value) { const filter = { order: ['totalProblems DESC'] }; let params = reactive({}); -const applyColumnFilter = async (col) => { - try { - const paramKey = col.columnFilter?.filterParamKey || col.field; - params[paramKey] = col.columnFilter.filterValue; - await paginateRef.value.addFilter(null, params); - } catch (err) { - console.error('Error applying column filter', err); - } -}; - -const getInputEvents = (col) => { - return col.columnFilter.type === 'select' || col.columnFilter.type === 'date' - ? { 'update:modelValue': () => applyColumnFilter(col) } - : { - 'keyup.enter': () => applyColumnFilter(col), - }; -}; - -const fetchParams = ($params = {}) => { - const excludedParams = ['search', 'clientFk', 'orderFk', 'refFk', 'scopeDays']; - - const hasExcludedParams = excludedParams.some((param) => { - return $params && $params[param] != undefined; - }); - const hasParams = Object.entries($params).length; - if (!hasParams || !hasExcludedParams) $params.scopeDays = 1; - - if (typeof $params.scopeDays === 'number') { - const from = Date.vnNew(); - from.setHours(0, 0, 0, 0); - - const to = new Date(from.getTime()); - to.setDate(to.getDate() + $params.scopeDays); - to.setHours(23, 59, 59, 999); - - Object.assign($params, { from, to }); - } - return { tableOrder: 'totalProblems DESC', ...$params }; -}; - const columns = computed(() => [ { label: t('salesTicketsTable.problems'), @@ -379,13 +333,6 @@ const redirectToSales = (id) => { const url = `#/ticket/${id}/sale`; window.open(url, '_blank'); }; - -// onMounted(async () => { -// const filteredColumns = columns.value.filter((col) => col.name !== 'rowActions'); -// allColumnNames.value = filteredColumns.map((col) => col.name); -// params = fetchParams(); -// await paginateRef.value.addFilter(null, params); -// }); </script> <template> diff --git a/src/pages/Ticket/Card/TicketSale.vue b/src/pages/Ticket/Card/TicketSale.vue index cbc94b388..5978dbf09 100644 --- a/src/pages/Ticket/Card/TicketSale.vue +++ b/src/pages/Ticket/Card/TicketSale.vue @@ -11,7 +11,6 @@ import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue'; import TicketEditManaProxy from './TicketEditMana.vue'; import VnImg from 'src/components/ui/VnImg.vue'; -import RightMenu from 'src/components/common/RightMenu.vue'; import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; import TicketSaleMoreActions from './TicketSaleMoreActions.vue'; import TicketTransfer from './TicketTransfer.vue'; diff --git a/src/pages/Zone/Card/ZoneCard.vue b/src/pages/Zone/Card/ZoneCard.vue index 02ec12fe7..b81ee9039 100644 --- a/src/pages/Zone/Card/ZoneCard.vue +++ b/src/pages/Zone/Card/ZoneCard.vue @@ -10,11 +10,6 @@ const { t } = useI18n(); const route = useRoute(); const routeName = computed(() => route.name); -const customRouteRedirectName = computed(() => { - if (routeName.value === 'ZoneLocations') return null; - return routeName.value; -}); -const searchbarMakeFetch = computed(() => routeName.value !== 'ZoneEvents'); const searchBarDataKeys = { ZoneWarehouses: 'ZoneWarehouses', ZoneSummary: 'ZoneSummary', From 0eb699bcbedd910007abee213e271962e77118d8 Mon Sep 17 00:00:00 2001 From: jgallego <jgallego@verdnatura.es> Date: Tue, 20 Aug 2024 10:10:11 +0200 Subject: [PATCH 40/60] feat: refs #7346 formdata uses serialType --- src/i18n/locale/en.yml | 1 + src/i18n/locale/es.yml | 1 + src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue | 4 ++-- src/pages/InvoiceOut/InvoiceOutGlobalForm.vue | 2 +- src/pages/Ticket/Card/TicketSaleMoreActions.vue | 4 ++-- src/stores/invoiceOutGlobal.js | 10 +++++----- 6 files changed, 12 insertions(+), 10 deletions(-) diff --git a/src/i18n/locale/en.yml b/src/i18n/locale/en.yml index 7d3682525..7f15ecf6a 100644 --- a/src/i18n/locale/en.yml +++ b/src/i18n/locale/en.yml @@ -689,6 +689,7 @@ invoiceOut: chooseValidClient: Choose a valid client chooseValidCompany: Choose a valid company chooseValidPrinter: Choose a valid printer + chooseValidSerialType: Choose a serial type fillDates: Invoice date and the max date should be filled invoiceDateLessThanMaxDate: Invoice date can not be less than max date invoiceWithFutureDate: Exists an invoice with a future date diff --git a/src/i18n/locale/es.yml b/src/i18n/locale/es.yml index 8157f1803..fb892f015 100644 --- a/src/i18n/locale/es.yml +++ b/src/i18n/locale/es.yml @@ -695,6 +695,7 @@ invoiceOut: chooseValidClient: Selecciona un cliente válido chooseValidCompany: Selecciona una empresa válida chooseValidPrinter: Selecciona una impresora válida + chooseValidSerialType: Selecciona una tipo de serie válida fillDates: La fecha de la factura y la fecha máxima deben estar completas invoiceDateLessThanMaxDate: La fecha de la factura no puede ser menor que la fecha máxima invoiceWithFutureDate: Existe una factura con una fecha futura diff --git a/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue b/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue index e524faa24..4c02ccf84 100644 --- a/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue +++ b/src/pages/InvoiceOut/Card/InvoiceOutDescriptorMenu.vue @@ -222,7 +222,7 @@ const showTransferInvoiceForm = async () => { <QItemSection>{{ t('Generate PDF invoice') }}</QItemSection> </QItem> <QItem v-ripple clickable> - <QItemSection>{{ t('Refund...') }}</QItemSection> + <QItemSection>{{ t('Refund') }}</QItemSection> <QItemSection side> <QIcon name="keyboard_arrow_right" /> </QItemSection> @@ -250,7 +250,7 @@ es: Delete invoice: Eliminar factura Book invoice: Asentar factura Generate PDF invoice: Generar PDF factura - Refund...: Abono + Refund: Abono As PDF: como PDF As CSV: como CSV Send PDF: Enviar PDF diff --git a/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue b/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue index 5fc519061..88d55b093 100644 --- a/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue +++ b/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue @@ -110,7 +110,7 @@ onMounted(async () => { /> <VnSelect :label="t('invoiceOutSerialType')" - v-model="formData.invoiceType" + v-model="formData.serialType" :options="serialTypesOptions" option-value="type" option-label="type" diff --git a/src/pages/Ticket/Card/TicketSaleMoreActions.vue b/src/pages/Ticket/Card/TicketSaleMoreActions.vue index 9ec6b303a..f6803a9f1 100644 --- a/src/pages/Ticket/Card/TicketSaleMoreActions.vue +++ b/src/pages/Ticket/Card/TicketSaleMoreActions.vue @@ -244,7 +244,7 @@ const createRefund = async (withWarehouse) => { </QItem> <QItem clickable v-ripple> <QItemSection> - <QItemLabel>{{ t('Refund...') }}</QItemLabel> + <QItemLabel>{{ t('Refund') }}</QItemLabel> </QItemSection> <QItemSection side> <QIcon name="keyboard_arrow_right" /> @@ -279,7 +279,7 @@ es: Add claim: Crear reclamación Mark as reserved: Marcar como reservado Unmark as reserved: Desmarcar como reservado - Refund...: Abono... + Refund: Abono with warehouse: con almacén without warehouse: sin almacén Claim out of time: Reclamación fuera de plazo diff --git a/src/stores/invoiceOutGlobal.js b/src/stores/invoiceOutGlobal.js index ca123d0b3..669d65728 100644 --- a/src/stores/invoiceOutGlobal.js +++ b/src/stores/invoiceOutGlobal.js @@ -19,7 +19,7 @@ export const useInvoiceOutGlobalStore = defineStore({ maxShipped: null, clientId: null, printer: null, - invoiceType: null, + serialType: null, }, addresses: [], minInvoicingDate: null, @@ -101,7 +101,7 @@ export const useInvoiceOutGlobalStore = defineStore({ maxShipped: new Date(formData.maxShipped), clientId: formData.clientId ? formData.clientId : null, companyFk: formData.companyFk, - invoiceType: formData.invoiceType, + serialType: formData.serialType, }; this.validateMakeInvoceParams(params, clientsToInvoice); @@ -154,9 +154,9 @@ export const useInvoiceOutGlobalStore = defineStore({ ); throw new Error('Invoice date in the future'); } - if (!params.invoiceType) { + if (!params.serialType) { notify( - 'invoiceOut.globalInvoices.errors.chooseValidinvoiceType', + 'invoiceOut.globalInvoices.errors.chooseValidSerialType', 'negative' ); throw new Error('Invalid Serial Type'); @@ -188,7 +188,7 @@ export const useInvoiceOutGlobalStore = defineStore({ invoiceDate: new Date(formData.invoiceDate), maxShipped: new Date(formData.maxShipped), companyFk: formData.companyFk, - invoiceType: formData.invoiceType, + serialType: formData.serialType, }; this.status = 'invoicing'; From 89eaf02800b9839c58565fa389e40588ee546b64 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Wed, 21 Aug 2024 11:45:26 +0200 Subject: [PATCH 41/60] feat: #7323 handle workerPhoto --- src/css/app.scss | 7 +++ src/pages/Item/Card/ItemDescriptorImage.vue | 8 --- src/pages/Worker/Card/WorkerDescriptor.vue | 70 +++++++++++++++------ 3 files changed, 57 insertions(+), 28 deletions(-) diff --git a/src/css/app.scss b/src/css/app.scss index c233b14f0..525fb2a95 100644 --- a/src/css/app.scss +++ b/src/css/app.scss @@ -268,3 +268,10 @@ input::-webkit-inner-spin-button { max-width: 400px; } } +.edit-photo-btn { + position: absolute; + right: 12px; + bottom: 12px; + z-index: 1; + cursor: pointer; +} diff --git a/src/pages/Item/Card/ItemDescriptorImage.vue b/src/pages/Item/Card/ItemDescriptorImage.vue index d923dd28f..d83f534b8 100644 --- a/src/pages/Item/Card/ItemDescriptorImage.vue +++ b/src/pages/Item/Card/ItemDescriptorImage.vue @@ -138,14 +138,6 @@ en: </i18n> <style lang="scss" scoped> -.edit-photo-btn { - position: absolute; - right: 12px; - bottom: 12px; - z-index: 1; - cursor: pointer; -} - .separation-borders { border-left: 1px solid $white; border-right: 1px solid $white; diff --git a/src/pages/Worker/Card/WorkerDescriptor.vue b/src/pages/Worker/Card/WorkerDescriptor.vue index 8fbb23ef8..154db1258 100644 --- a/src/pages/Worker/Card/WorkerDescriptor.vue +++ b/src/pages/Worker/Card/WorkerDescriptor.vue @@ -10,6 +10,7 @@ import useCardDescription from 'src/composables/useCardDescription'; import { useState } from 'src/composables/useState'; import axios from 'axios'; import VnImg from 'src/components/ui/VnImg.vue'; +import EditPictureForm from 'components/EditPictureForm.vue'; const $props = defineProps({ id: { @@ -18,6 +19,7 @@ const $props = defineProps({ default: null, }, }); +const image = ref(null); const route = useRoute(); const { t } = useI18n(); @@ -25,6 +27,10 @@ const state = useState(); const user = state.getUser(); const changePasswordFormDialog = ref(null); const cardDescriptorRef = ref(null); +const showEditPhotoForm = ref(false); +const toggleEditPictureForm = () => { + showEditPhotoForm.value = !showEditPhotoForm.value; +}; const entityId = computed(() => { return $props.id || route.params.id; @@ -99,7 +105,9 @@ const handleExcluded = async () => { workerExcluded.value = !workerExcluded.value; }; - +const handlePhotoUpdated = (evt = false) => { + image.value.reload(evt); +}; const refetch = async () => await cardDescriptorRef.value.getData(); </script> <template> @@ -144,27 +152,49 @@ const refetch = async () => await cardDescriptorRef.value.getData(); </QItem> </template> <template #before> - <VnImg - :id="parseInt(entityId)" - collection="user" - resolution="520x520" - class="photo" - > - <template #error> - <div - class="absolute-full picture text-center q-pa-md flex flex-center" - > - <div> - <div class="text-grey-5" style="opacity: 0.4; font-size: 5vh"> - <QIcon name="vn:claims" /> - </div> - <div class="text-grey-5" style="opacity: 0.4"> - {{ t('worker.imageNotFound') }} + <div class="relative-position"> + <VnImg + ref="image" + :id="parseInt(entityId)" + collection="user" + resolution="520x520" + class="photo" + > + <template #error> + <div + class="absolute-full picture text-center q-pa-md flex flex-center" + > + <div> + <div + class="text-grey-5" + style="opacity: 0.4; font-size: 5vh" + > + <QIcon name="vn:claims" /> + </div> + <div class="text-grey-5" style="opacity: 0.4"> + {{ t('worker.imageNotFound') }} + </div> </div> </div> - </div> - </template> - </VnImg> + </template> </VnImg + ><QBtn + color="primary" + size="lg" + round + class="edit-photo-btn" + @click="toggleEditPictureForm()" + > + <QIcon name="edit" size="sm" /> + <QDialog ref="editPhotoFormDialog" v-model="showEditPhotoForm"> + <EditPictureForm + collection="user" + :id="entityId" + @close-form="toggleEditPictureForm()" + @on-photo-uploaded="handlePhotoUpdated" + /> + </QDialog> + </QBtn> + </div> </template> <template #body="{ entity }"> <VnLv :label="t('worker.card.name')" :value="entity.user?.nickname" /> From 3d5807463b55065b053d6235452927eb1fe3263e Mon Sep 17 00:00:00 2001 From: jgallego <jgallego@verdnatura.es> Date: Wed, 21 Aug 2024 13:46:19 +0200 Subject: [PATCH 42/60] feat: refs #7346 refactor --- src/pages/InvoiceOut/InvoiceOutGlobalForm.vue | 38 ++++++++++--------- src/pages/Item/Card/ItemBasicData.vue | 1 - 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue b/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue index 88d55b093..3dc34dc15 100644 --- a/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue +++ b/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue @@ -24,15 +24,28 @@ const companiesOptions = ref([]); const printersOptions = ref([]); const clientsOptions = ref([]); const serialTypesOptions = ref([]); + +const handleInvoiceOutSerialsFetch = (data) => { + const uniqueTypes = new Set(); + serialTypesOptions.value = data.filter((item) => { + if (item.type === null || uniqueTypes.has(item.type)) { + return false; + } + uniqueTypes.add(item.type); + return true; + }); +}; + const formData = ref({}); const optionsInitialData = computed(() => { - return ( - companiesOptions.value.length > 0 && - printersOptions.value.length > 0 && - clientsOptions.value.length > 0 && - serialTypesOptions.value.length > 0 - ); + const optionsArrays = [ + companiesOptions.value, + printersOptions.value, + clientsOptions.value, + serialTypesOptions.value, + ]; + return optionsArrays.every((arr) => arr.length > 0); }); const getStatus = computed({ @@ -60,18 +73,7 @@ onMounted(async () => { <FetchData url="Clients" @on-fetch="(data) => (clientsOptions = data)" auto-load /> <FetchData url="invoiceOutSerials" - @on-fetch=" - (data) => { - const uniqueTypes = new Set(); - serialTypesOptions = data.filter((item) => { - if (item.type === null || uniqueTypes.has(item.type)) { - return false; - } - uniqueTypes.add(item.type); - return true; - }); - } - " + @on-fetch="handleInvoiceOutSerialsFetch" auto-load /> <QForm diff --git a/src/pages/Item/Card/ItemBasicData.vue b/src/pages/Item/Card/ItemBasicData.vue index 89c883295..ae9e45983 100644 --- a/src/pages/Item/Card/ItemBasicData.vue +++ b/src/pages/Item/Card/ItemBasicData.vue @@ -16,7 +16,6 @@ const route = useRoute(); const { t } = useI18n(); const itemTypesOptions = ref([]); -const itemsWithNameOptions = ref([]); const intrastatsOptions = ref([]); const expensesOptions = ref([]); From b71d24cf3d47edcda32e6a7734112796b5510cde Mon Sep 17 00:00:00 2001 From: jgallego <jgallego@verdnatura.es> Date: Wed, 21 Aug 2024 14:13:36 +0200 Subject: [PATCH 43/60] feat: refs #7346 sonarLint warnings --- src/css/app.scss | 4 ---- src/pages/InvoiceOut/InvoiceOutGlobal.vue | 12 +++++++----- src/stores/invoiceOutGlobal.js | 9 ++------- 3 files changed, 9 insertions(+), 16 deletions(-) diff --git a/src/css/app.scss b/src/css/app.scss index c233b14f0..b2976b86a 100644 --- a/src/css/app.scss +++ b/src/css/app.scss @@ -103,10 +103,6 @@ select:-webkit-autofill { border-radius: 8px; } -.card-width { - width: 770px; -} - .vn-card-list { width: 100%; max-width: 60em; diff --git a/src/pages/InvoiceOut/InvoiceOutGlobal.vue b/src/pages/InvoiceOut/InvoiceOutGlobal.vue index eecc61bc2..5f2eb3c02 100644 --- a/src/pages/InvoiceOut/InvoiceOutGlobal.vue +++ b/src/pages/InvoiceOut/InvoiceOutGlobal.vue @@ -94,11 +94,13 @@ const selectCustomerId = (id) => { }; const statusText = computed(() => { - return status.value === 'invoicing' - ? `${t(`status.${status.value}`)} ${ - addresses.value[getAddressNumber.value]?.clientId - }` - : t(`status.${status.value}`); + const baseStatus = t(`status.${status.value}`); + const clientId = + status.value === 'invoicing' + ? addresses.value[getAddressNumber.value]?.clientId || '' + : ''; + + return clientId ? `${baseStatus} ${clientId}`.trim() : baseStatus; }); onMounted(() => (stateStore.rightDrawer = true)); diff --git a/src/stores/invoiceOutGlobal.js b/src/stores/invoiceOutGlobal.js index 669d65728..42acac013 100644 --- a/src/stores/invoiceOutGlobal.js +++ b/src/stores/invoiceOutGlobal.js @@ -200,12 +200,7 @@ export const useInvoiceOutGlobalStore = defineStore({ this.addressIndex++; this.isInvoicing = false; } catch (err) { - if ( - err && - err.response && - err.response.status >= 400 && - err.response.status < 500 - ) { + if (err?.response?.status >= 400 && err?.response?.status < 500) { this.invoiceClientError(address, err.response?.data?.error?.message); return; } else { @@ -252,7 +247,7 @@ export const useInvoiceOutGlobalStore = defineStore({ params, }); - if (data.data && data.data.error) throw new Error(); + if (data?.data?.error) throw new Error(); const status = exportFile('negativeBases.csv', data, { encoding: 'windows-1252', From 6197d283e4fa2d104644848e3fa98ae7d60cf496 Mon Sep 17 00:00:00 2001 From: wbuezas <wbuezas@verdnatura.es> Date: Wed, 21 Aug 2024 10:51:38 -0300 Subject: [PATCH 44/60] Create ticket future filter --- src/components/ui/VnFilterPanel.vue | 2 +- src/pages/Ticket/TicketFuture.vue | 19 +- src/pages/Ticket/TicketFutureFilter.vue | 244 ++++++++++++++++++++++++ src/pages/Ticket/locale/en.yml | 5 + src/pages/Ticket/locale/es.yml | 5 + 5 files changed, 269 insertions(+), 6 deletions(-) create mode 100644 src/pages/Ticket/TicketFutureFilter.vue diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue index 9059605ca..3598f96a5 100644 --- a/src/components/ui/VnFilterPanel.vue +++ b/src/components/ui/VnFilterPanel.vue @@ -202,7 +202,7 @@ function formatValue(value) { function sanitizer(params) { for (const [key, value] of Object.entries(params)) { - if (typeof value == 'object') { + if (value && typeof value === 'object') { const param = Object.values(value)[0]; if (typeof param == 'string') params[key] = param.replaceAll('%', ''); } diff --git a/src/pages/Ticket/TicketFuture.vue b/src/pages/Ticket/TicketFuture.vue index 2fec6dc18..4ec430e2e 100644 --- a/src/pages/Ticket/TicketFuture.vue +++ b/src/pages/Ticket/TicketFuture.vue @@ -8,6 +8,8 @@ import VnSelect from 'src/components/common/VnSelect.vue'; import TicketDescriptorProxy from 'src/pages/Ticket/Card/TicketDescriptorProxy.vue'; import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; import VnSearchbar from 'src/components/ui/VnSearchbar.vue'; +import RightMenu from 'src/components/common/RightMenu.vue'; +import TicketFutureFilter from './TicketFutureFilter.vue'; import { dashIfEmpty, toCurrency } from 'src/filters'; import { useVnConfirm } from 'composables/useVnConfirm'; @@ -37,9 +39,9 @@ const exprBuilder = (param, value) => { return { liters: value }; case 'lines': return { lines: value }; - case 'ipt': + case 'iptColFilter': return { ipt: { like: `%${value}%` } }; - case 'futureIpt': + case 'futureIptColFilter': return { futureIpt: { like: `%${value}%` } }; case 'totalWithVat': return { totalWithVat: value }; @@ -83,6 +85,8 @@ const getInputEvents = (col) => { }; }; +const tickets = computed(() => store.data); + const ticketColumns = computed(() => [ { label: t('futureTickets.problems'), @@ -121,7 +125,7 @@ const ticketColumns = computed(() => [ sortable: true, columnFilter: { component: VnSelect, - filterParamKey: 'ipt', + filterParamKey: 'iptColFilter', type: 'select', filterValue: null, event: getInputEvents, @@ -214,7 +218,7 @@ const ticketColumns = computed(() => [ sortable: true, columnFilter: { component: VnSelect, - filterParamKey: 'futureIpt', + filterParamKey: 'futureIptColFilter', type: 'select', filterValue: null, event: getInputEvents, @@ -305,9 +309,14 @@ onMounted(async () => { </QBtn> </template> </VnSubToolbar> + <RightMenu> + <template #right-panel> + <TicketFutureFilter data-key="FutureTickets" /> + </template> + </RightMenu> <QPage class="column items-center q-pa-md"> <QTable - :rows="store.data" + :rows="tickets" :columns="ticketColumns" row-key="id" selection="multiple" diff --git a/src/pages/Ticket/TicketFutureFilter.vue b/src/pages/Ticket/TicketFutureFilter.vue new file mode 100644 index 000000000..36a9d154f --- /dev/null +++ b/src/pages/Ticket/TicketFutureFilter.vue @@ -0,0 +1,244 @@ +<script setup> +import { ref } from 'vue'; +import { useI18n } from 'vue-i18n'; + +import FetchData from 'components/FetchData.vue'; +import VnFilterPanel from 'src/components/ui/VnFilterPanel.vue'; +import VnSelect from 'components/common/VnSelect.vue'; +import VnInputDate from 'src/components/common/VnInputDate.vue'; +import VnInput from 'src/components/common/VnInput.vue'; + +import axios from 'axios'; +import { onMounted } from 'vue'; + +const { t } = useI18n(); +const props = defineProps({ + dataKey: { + type: String, + required: true, + }, +}); + +const warehousesOptions = ref([]); +const itemPackingTypes = ref([]); +const stateOptions = ref([]); + +const getItemPackingTypes = async () => { + try { + const filter = { + where: { isActive: true }, + }; + const { data } = await axios.get('ItemPackingTypes', { + params: { filter: JSON.stringify(filter) }, + }); + itemPackingTypes.value = data.map((ipt) => ({ + description: t(ipt.description), + code: ipt.code, + })); + } catch (error) { + console.error(error); + } +}; + +const getGroupedStates = async () => { + try { + const { data } = await axios.get('AlertLevels'); + stateOptions.value = data.map((state) => ({ + id: state.id, + name: t(`futureTickets.${state.code}`), + code: state.code, + })); + } catch (error) { + console.error(error); + } +}; + +onMounted(async () => { + getItemPackingTypes(); + getGroupedStates(); +}); +</script> + +<template> + <FetchData + url="Warehouses" + @on-fetch="(data) => (warehousesOptions = data)" + auto-load + /> + <VnFilterPanel + :data-key="props.dataKey" + :hidden-tags="['search']" + :unremovable-params="['warehouseFk', 'originDated', 'futureDated']" + > + <template #tags="{ tag, formatFn }"> + <div class="q-gutter-x-xs"> + <strong>{{ t(`params.${tag.label}`) }}: </strong> + <span>{{ formatFn(tag.value) }}</span> + </div> + </template> + <template #body="{ params, searchFn }"> + <QItem class="q-my-sm"> + <QItemSection> + <VnInputDate + v-model="params.originDated" + :label="t('params.originDated')" + is-outlined + @update:model-value="searchFn()" + /> + </QItemSection> + </QItem> + <QItem class="q-my-sm"> + <QItemSection> + <VnInputDate + v-model="params.futureDated" + :label="t('params.futureDated')" + is-outlined + @update:model-value="searchFn()" + /> + </QItemSection> + </QItem> + <QItem class="q-my-sm"> + <QItemSection> + <VnInput + :label="t('params.litersMax')" + v-model="params.litersMax" + is-outlined + /> + </QItemSection> + </QItem> + <QItem class="q-my-sm"> + <QItemSection> + <VnInput + :label="t('params.linesMax')" + v-model="params.linesMax" + is-outlined + /> + </QItemSection> + </QItem> + <QItem> + <QItemSection> + <VnSelect + :label="t('params.ipt')" + v-model="params.ipt" + :options="itemPackingTypes" + option-value="code" + option-label="description" + :info="t('iptInfo')" + @update:model-value="searchFn()" + dense + outlined + rounded + > + </VnSelect> + </QItemSection> + </QItem> + <QItem> + <QItemSection> + <VnSelect + :label="t('params.futureIpt')" + v-model="params.futureIpt" + :options="itemPackingTypes" + option-value="code" + option-label="description" + :info="t('iptInfo')" + @update:model-value="searchFn()" + dense + outlined + rounded + > + </VnSelect> + </QItemSection> + </QItem> + <QItem> + <QItemSection> + <VnSelect + :label="t('params.state')" + v-model="params.state" + :options="stateOptions" + option-value="code" + option-label="name" + @update:model-value="searchFn()" + dense + outlined + rounded + > + </VnSelect> + </QItemSection> + </QItem> + <QItem> + <QItemSection> + <VnSelect + :label="t('params.futureState')" + v-model="params.futureState" + :options="stateOptions" + option-value="code" + option-label="name" + @update:model-value="searchFn()" + dense + outlined + rounded + > + </VnSelect> + </QItemSection> + </QItem> + + <QItem> + <QItemSection> + <QCheckbox + :label="t('params.problems')" + v-model="params.problems" + :toggle-indeterminate="false" + @update:model-value="searchFn()" + /> + </QItemSection> + </QItem> + <QItem> + <QItemSection> + <VnSelect + :label="t('params.warehouseFk')" + v-model="params.warehouseFk" + :options="warehousesOptions" + option-value="id" + option-label="name" + @update:model-value="searchFn()" + dense + outlined + rounded + > + </VnSelect> + </QItemSection> + </QItem> + </template> + </VnFilterPanel> +</template> + +<i18n> +en: + iptInfo: IPT + params: + originDated: Origin date + futureDated: Destination date + futureIpt: Destination IPT + ipt: Origin IPT + warehouseFk: Warehouse + litersMax: Max liters + linesMax: Max lines + state: Origin grouped state + futureState: Destination grouped state + problems: With problems +es: + Horizontal: Horizontal + Vertical: Vertical + iptInfo: Encajado + params: + originDated: Fecha origen + futureDated: Fecha destino + futureIpt: IPT destino + ipt: IPT Origen + warehouseFk: Almacén + litersMax: Litros máx. + linesMax: Líneas máx. + state: Estado agrupado origen + futureState: Estado agrupado destino + problems: Con problemas +</i18n> diff --git a/src/pages/Ticket/locale/en.yml b/src/pages/Ticket/locale/en.yml index 10a8e1fa4..305228669 100644 --- a/src/pages/Ticket/locale/en.yml +++ b/src/pages/Ticket/locale/en.yml @@ -93,6 +93,11 @@ futureTickets: moveTicketSuccess: Tickets moved successfully! searchInfo: Search future tickets by date futureTicket: Future tickets + FREE: Free + ON_PREVIOUS: ON_PREVIOUS + ON_PREPARATION: On preparation + PACKED: Packed + DELIVERED: Delivered expedition: id: Expedition item: Item diff --git a/src/pages/Ticket/locale/es.yml b/src/pages/Ticket/locale/es.yml index a80692bfe..20b4edf16 100644 --- a/src/pages/Ticket/locale/es.yml +++ b/src/pages/Ticket/locale/es.yml @@ -140,6 +140,11 @@ futureTickets: moveTicketSuccess: Tickets movidos correctamente searchInfo: Buscar tickets por fecha futureTicket: Tickets a futuro + FREE: Libre + ON_PREVIOUS: ON_PREVIOUS + ON_PREPARATION: En preparación + PACKED: Encajado + DELIVERED: Servido ticketSale: id: Id visible: Visible From 54eb567e39e43fc6c87f8de52c9359a487b59a5c Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Thu, 22 Aug 2024 22:13:50 +0200 Subject: [PATCH 45/60] perf: date fields --- src/pages/Ticket/TicketAdvance.vue | 2 +- src/pages/Ticket/TicketAdvanceFilter.vue | 4 +--- src/pages/Ticket/TicketFuture.vue | 4 ++-- src/pages/Ticket/TicketFutureFilter.vue | 4 +--- 4 files changed, 5 insertions(+), 9 deletions(-) diff --git a/src/pages/Ticket/TicketAdvance.vue b/src/pages/Ticket/TicketAdvance.vue index bf4000fdf..45bd8fd39 100644 --- a/src/pages/Ticket/TicketAdvance.vue +++ b/src/pages/Ticket/TicketAdvance.vue @@ -448,7 +448,7 @@ const handleCloseProgressDialog = () => { const handleCancelProgress = () => (cancelProgress.value = true); onMounted(async () => { - let today = Date.vnNew(); + let today = Date.vnNew().toISOString(); const tomorrow = new Date(today); tomorrow.setDate(tomorrow.getDate() + 1); userParams.dateFuture = tomorrow; diff --git a/src/pages/Ticket/TicketAdvanceFilter.vue b/src/pages/Ticket/TicketAdvanceFilter.vue index c4548763a..ed61d9447 100644 --- a/src/pages/Ticket/TicketAdvanceFilter.vue +++ b/src/pages/Ticket/TicketAdvanceFilter.vue @@ -55,7 +55,7 @@ onMounted(async () => await getItemPackingTypes()); :data-key="props.dataKey" :search-button="true" :hidden-tags="['search']" - :unremovable-params="['warehouseFk', 'dateFuture', 'dateToAdvance']" + :un-removable-params="['warehouseFk', 'dateFuture', 'dateToAdvance']" > <template #tags="{ tag, formatFn }"> <div class="q-gutter-x-xs"> @@ -70,7 +70,6 @@ onMounted(async () => await getItemPackingTypes()); v-model="params.dateFuture" :label="t('params.dateFuture')" is-outlined - @update:model-value="searchFn()" /> </QItemSection> </QItem> @@ -80,7 +79,6 @@ onMounted(async () => await getItemPackingTypes()); v-model="params.dateToAdvance" :label="t('params.dateToAdvance')" is-outlined - @update:model-value="searchFn()" /> </QItemSection> </QItem> diff --git a/src/pages/Ticket/TicketFuture.vue b/src/pages/Ticket/TicketFuture.vue index 4ec430e2e..14a4a5ac8 100644 --- a/src/pages/Ticket/TicketFuture.vue +++ b/src/pages/Ticket/TicketFuture.vue @@ -49,8 +49,8 @@ const exprBuilder = (param, value) => { }; const userParams = reactive({ - futureDated: Date.vnNew(), - originDated: Date.vnNew(), + futureDated: Date.vnNew().toISOString(), + originDated: Date.vnNew().toISOString(), warehouseFk: user.value.warehouseFk, }); diff --git a/src/pages/Ticket/TicketFutureFilter.vue b/src/pages/Ticket/TicketFutureFilter.vue index 36a9d154f..c5e647d64 100644 --- a/src/pages/Ticket/TicketFutureFilter.vue +++ b/src/pages/Ticket/TicketFutureFilter.vue @@ -68,7 +68,7 @@ onMounted(async () => { <VnFilterPanel :data-key="props.dataKey" :hidden-tags="['search']" - :unremovable-params="['warehouseFk', 'originDated', 'futureDated']" + :un-removable-params="['warehouseFk', 'originDated', 'futureDated']" > <template #tags="{ tag, formatFn }"> <div class="q-gutter-x-xs"> @@ -83,7 +83,6 @@ onMounted(async () => { v-model="params.originDated" :label="t('params.originDated')" is-outlined - @update:model-value="searchFn()" /> </QItemSection> </QItem> @@ -93,7 +92,6 @@ onMounted(async () => { v-model="params.futureDated" :label="t('params.futureDated')" is-outlined - @update:model-value="searchFn()" /> </QItemSection> </QItem> From 1984e1d0d81cc21578b86cd279fe0fab7f9fabe3 Mon Sep 17 00:00:00 2001 From: jgallego <jgallego@verdnatura.es> Date: Fri, 23 Aug 2024 10:25:05 +0200 Subject: [PATCH 46/60] feat: refs #7710 uses cloneAll --- src/pages/Ticket/Card/TicketExpedition.vue | 1 - .../Ticket/Card/TicketSaleMoreActions.vue | 22 +++++++++++++------ 2 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/pages/Ticket/Card/TicketExpedition.vue b/src/pages/Ticket/Card/TicketExpedition.vue index a7eb9e27e..f344e4276 100644 --- a/src/pages/Ticket/Card/TicketExpedition.vue +++ b/src/pages/Ticket/Card/TicketExpedition.vue @@ -268,7 +268,6 @@ onMounted(async () => { stateStore.rightDrawer = true; const filteredColumns = columns.value.filter((col) => col.name !== 'history'); allColumnNames.value = filteredColumns.map((col) => col.name); - // await expeditionsArrayData.fetch({ append: false }); }); onUnmounted(() => (stateStore.rightDrawer = false)); diff --git a/src/pages/Ticket/Card/TicketSaleMoreActions.vue b/src/pages/Ticket/Card/TicketSaleMoreActions.vue index 9ec6b303a..ba827d062 100644 --- a/src/pages/Ticket/Card/TicketSaleMoreActions.vue +++ b/src/pages/Ticket/Card/TicketSaleMoreActions.vue @@ -153,14 +153,22 @@ const setReserved = async (reserved) => { }; const createRefund = async (withWarehouse) => { - if (!props.sales) return; + if (!props.ticket) return; - const salesIds = props.sales.map((sale) => sale.id); - const params = { salesIds: salesIds, withWarehouse: withWarehouse, negative: true }; - const { data } = await axios.post('Sales/clone', params); - const [refundTicket] = data; - notify(t('refundTicketCreated', { ticketId: refundTicket.id }), 'positive'); - router.push({ name: 'TicketSale', params: { id: refundTicket.id } }); + const params = { + ticketsIds: [props.ticket.id], + withWarehouse: withWarehouse, + negative: true, + }; + + try { + const { data } = await axios.post('Tickets/cloneAll', params); + const [refundTicket] = data; + notify(t('refundTicketCreated', { ticketId: refundTicket.id }), 'positive'); + router.push({ name: 'TicketSale', params: { id: refundTicket.id } }); + } catch (error) { + console.error(error); + } }; </script> From d0ccce26567cee02c49466d62a78312d45529ee3 Mon Sep 17 00:00:00 2001 From: jgallego <jgallego@verdnatura.es> Date: Fri, 23 Aug 2024 10:32:36 +0200 Subject: [PATCH 47/60] feat: refs #7346 elimino === --- src/pages/InvoiceOut/InvoiceOutGlobalForm.vue | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue b/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue index 3dc34dc15..362ede4be 100644 --- a/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue +++ b/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue @@ -26,14 +26,9 @@ const clientsOptions = ref([]); const serialTypesOptions = ref([]); const handleInvoiceOutSerialsFetch = (data) => { - const uniqueTypes = new Set(); - serialTypesOptions.value = data.filter((item) => { - if (item.type === null || uniqueTypes.has(item.type)) { - return false; - } - uniqueTypes.add(item.type); - return true; - }); + serialTypesOptions.value = Array.from( + new Set(data.map((item) => item.type).filter((type) => type)) + ); }; const formData = ref({}); From efc7df5a60cd4b5ea0c0fa00a8a8b55c20840bca Mon Sep 17 00:00:00 2001 From: jgallego <jgallego@verdnatura.es> Date: Fri, 23 Aug 2024 12:19:54 +0200 Subject: [PATCH 48/60] delivery traducido a Reparto --- src/pages/Claim/locale/es.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Claim/locale/es.yml b/src/pages/Claim/locale/es.yml index 90bef8e66..052416aa7 100644 --- a/src/pages/Claim/locale/es.yml +++ b/src/pages/Claim/locale/es.yml @@ -42,7 +42,7 @@ claim: pickup: Recoger null: No agency: Agencia - delivery: Entrega + delivery: Reparto fileDescription: 'ID de reclamación {claimId} del cliente {clientName} con ID {clientId}' noData: 'No hay imágenes/videos, haz clic aquí o arrastra y suelta el archivo' dragDrop: Arrastra y suelta aquí From fb0ed9c4ce8b5ab956d1a74de4031c941a40f2cc Mon Sep 17 00:00:00 2001 From: jgallego <jgallego@verdnatura.es> Date: Fri, 23 Aug 2024 13:52:46 +0200 Subject: [PATCH 49/60] para que no de el fallo de muchos registros --- src/pages/InvoiceOut/InvoiceOutGlobalForm.vue | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue b/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue index 362ede4be..2070ef09c 100644 --- a/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue +++ b/src/pages/InvoiceOut/InvoiceOutGlobalForm.vue @@ -22,7 +22,6 @@ const { makeInvoice, setStatusValue } = invoiceOutGlobalStore; const clientsToInvoice = ref('all'); const companiesOptions = ref([]); const printersOptions = ref([]); -const clientsOptions = ref([]); const serialTypesOptions = ref([]); const handleInvoiceOutSerialsFetch = (data) => { @@ -37,7 +36,6 @@ const optionsInitialData = computed(() => { const optionsArrays = [ companiesOptions.value, printersOptions.value, - clientsOptions.value, serialTypesOptions.value, ]; return optionsArrays.every((arr) => arr.length > 0); @@ -65,7 +63,6 @@ onMounted(async () => { auto-load /> <FetchData url="Printers" @on-fetch="(data) => (printersOptions = data)" auto-load /> - <FetchData url="Clients" @on-fetch="(data) => (clientsOptions = data)" auto-load /> <FetchData url="invoiceOutSerials" @on-fetch="handleInvoiceOutSerialsFetch" @@ -97,7 +94,7 @@ onMounted(async () => { v-if="clientsToInvoice === 'one'" :label="t('client')" v-model="formData.clientId" - :options="clientsOptions" + url="Clients" option-value="id" option-label="name" hide-selected From 566a649c208c4a1d85ba7b0438b3abe730f49d61 Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Mon, 26 Aug 2024 16:34:46 +0200 Subject: [PATCH 50/60] fix: refs #7524 use limit item Section --- src/components/ui/VnFilterPanel.vue | 2 +- src/pages/Item/Card/ItemBotanical.vue | 28 +++++++++------------------ src/pages/Item/Card/ItemShelving.vue | 24 ++++++----------------- src/pages/Item/Card/ItemTags.vue | 4 +++- src/pages/Item/ItemListFilter.vue | 12 ++++-------- src/pages/Item/ItemRequest.vue | 12 +++--------- src/pages/Item/ItemRequestFilter.vue | 18 ++++------------- 7 files changed, 30 insertions(+), 70 deletions(-) diff --git a/src/components/ui/VnFilterPanel.vue b/src/components/ui/VnFilterPanel.vue index ab4e2e0da..b12a9f7b8 100644 --- a/src/components/ui/VnFilterPanel.vue +++ b/src/components/ui/VnFilterPanel.vue @@ -202,7 +202,7 @@ function formatValue(value) { function sanitizer(params) { for (const [key, value] of Object.entries(params)) { if (typeof value == 'object') - params[key] = Object.values(value)[0].replaceAll('%', ''); + params[key] = Object.values(value)?.[0]?.replaceAll('%', ''); } return params; } diff --git a/src/pages/Item/Card/ItemBotanical.vue b/src/pages/Item/Card/ItemBotanical.vue index 416c7f78b..599cd2b32 100644 --- a/src/pages/Item/Card/ItemBotanical.vue +++ b/src/pages/Item/Card/ItemBotanical.vue @@ -19,15 +19,6 @@ const itemSpeciesOptions = ref([]); const itemBotanicals = ref([]); let itemBotanicalsForm = reactive({ itemFk: null }); -const onGenusCreated = (response, formData) => { - itemGenusOptions.value = [...itemGenusOptions.value, response]; - formData.genusFk = response.id; -}; - -const onSpecieCreated = (response, formData) => { - itemSpeciesOptions.value = [...itemSpeciesOptions.value, response]; - formData.specieFk = response.id; -}; const entityId = computed(() => { return route.params.id; }); @@ -69,36 +60,35 @@ onMounted(async () => { <template #form="{ data }"> <VnRow> <VnSelectDialog + ref="genusRef" :label="t('Genus')" v-model="data.genusFk" - :options="itemGenusOptions" + url="Genera" option-label="name" option-value="id" + :fields="['id', 'name']" + sort-by="name ASC" hide-selected > <template #form> <CreateGenusForm - @on-data-saved=" - (_, requestResponse) => - onGenusCreated(requestResponse, data) - " + @on-data-saved="(_, res) => (data.genusFk = res.id)" /> </template> </VnSelectDialog> <VnSelectDialog :label="t('Species')" v-model="data.specieFk" - :options="itemSpeciesOptions" + url="Species" option-label="name" option-value="id" + :fields="['id', 'name']" + sort-by="name ASC" hide-selected > <template #form> <CreateSpecieForm - @on-data-saved=" - (_, requestResponse) => - onSpecieCreated(requestResponse, data) - " + @on-data-saved="(_, res) => (data.specieFk = res.id)" /> </template> </VnSelectDialog> diff --git a/src/pages/Item/Card/ItemShelving.vue b/src/pages/Item/Card/ItemShelving.vue index 7e7faab36..41cb34c03 100644 --- a/src/pages/Item/Card/ItemShelving.vue +++ b/src/pages/Item/Card/ItemShelving.vue @@ -3,7 +3,6 @@ import { onMounted, ref, computed, reactive } from 'vue'; import { useI18n } from 'vue-i18n'; import { useRoute } from 'vue-router'; -import FetchData from 'components/FetchData.vue'; import VnInput from 'src/components/common/VnInput.vue'; import VnSelect from 'src/components/common/VnSelect.vue'; import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue'; @@ -24,8 +23,6 @@ const { notify } = useNotify(); const { openConfirmationModal } = useVnConfirm(); const rowsSelected = ref([]); -const parkingsOptions = ref([]); -const shelvingsOptions = ref([]); const exprBuilder = (param, value) => { switch (param) { @@ -104,7 +101,9 @@ const columns = computed(() => [ filterValue: null, event: getInputEvents, attrs: { - options: parkingsOptions.value, + url: 'parkings', + fields: ['code'], + 'sort-by': 'code ASC', 'option-value': 'code', 'option-label': 'code', dense: true, @@ -124,7 +123,9 @@ const columns = computed(() => [ filterValue: null, event: getInputEvents, attrs: { - options: shelvingsOptions.value, + url: 'shelvings', + fields: ['code'], + 'sort-by': 'code ASC', 'option-value': 'code', 'option-label': 'code', dense: true, @@ -188,18 +189,6 @@ onMounted(async () => { </script> <template> - <FetchData - url="parkings" - :filter="{ fields: ['code'], order: 'code ASC' }" - auto-load - @on-fetch="(data) => (parkingsOptions = data)" - /> - <FetchData - url="shelvings" - :filter="{ fields: ['code'], order: 'code ASC' }" - auto-load - @on-fetch="(data) => (shelvingsOptions = data)" - /> <template v-if="stateStore.isHeaderMounted()"> <Teleport to="#st-data"> <div class="q-pa-md q-mr-lg q-ma-xs" style="border: 2px solid #222"> @@ -237,7 +226,6 @@ onMounted(async () => { </QBtn> </Teleport> </template> - <QPage class="column items-center q-pa-md"> <QTable :rows="rows" diff --git a/src/pages/Item/Card/ItemTags.vue b/src/pages/Item/Card/ItemTags.vue index 40c9941e9..39723ae65 100644 --- a/src/pages/Item/Card/ItemTags.vue +++ b/src/pages/Item/Card/ItemTags.vue @@ -24,6 +24,7 @@ const getSelectedTagValues = async (tag) => { const filter = { fields: ['value'], order: 'value ASC', + limit: 30, }; const params = { filter: JSON.stringify(filter) }; @@ -126,7 +127,7 @@ const insertTag = (rows) => { :key="row.tagFk" :label="t('Value')" v-model="row.value" - :options="valueOptionsMap.get(row.tagFk)" + :url="`Tags/${row.tagFk}/filterValue`" option-label="value" option-value="value" emit-value @@ -135,6 +136,7 @@ const insertTag = (rows) => { :is-clearable="false" :required="false" :rules="validate('itemTag.tagFk')" + :use-like="false" /> <VnInput v-else-if=" diff --git a/src/pages/Item/ItemListFilter.vue b/src/pages/Item/ItemListFilter.vue index 62c0c56dc..22dce9c64 100644 --- a/src/pages/Item/ItemListFilter.vue +++ b/src/pages/Item/ItemListFilter.vue @@ -30,7 +30,7 @@ const itemTypesRef = ref(null); const categoriesOptions = ref([]); const itemTypesOptions = ref([]); const buyersOptions = ref([]); -const suppliersOptions = ref([]); +const tagOptions = ref([]); const tagValues = ref([]); const fieldFiltersValues = ref([]); const moreFields = ref([]); @@ -161,12 +161,6 @@ onMounted(async () => { @on-fetch="(data) => (buyersOptions = data)" auto-load /> - <FetchData - url="Suppliers" - :filter="{ fields: ['id', 'name', 'nickname'], order: 'name ASC' }" - @on-fetch="(data) => (suppliersOptions = data)" - auto-load - /> <FetchData url="Tags" :filter="{ fields: ['id', 'name', 'isFree'] }" @@ -261,9 +255,11 @@ onMounted(async () => { :label="t('params.supplierFk')" v-model="params.supplierFk" @update:model-value="searchFn()" - :options="suppliersOptions" + url="Suppliers" option-value="id" option-label="name" + :fields="['id', 'name', 'nickname']" + sort-by="name ASC" hide-selected dense outlined diff --git a/src/pages/Item/ItemRequest.vue b/src/pages/Item/ItemRequest.vue index ae6638953..c1ee15a7e 100644 --- a/src/pages/Item/ItemRequest.vue +++ b/src/pages/Item/ItemRequest.vue @@ -22,7 +22,6 @@ import RightMenu from 'src/components/common/RightMenu.vue'; const { t } = useI18n(); const { notify } = useNotify(); const stateStore = useStateStore(); -const workersOptions = ref([]); let filterParams = ref({}); const denyFormRef = ref(null); const denyRequestId = ref(null); @@ -208,13 +207,6 @@ onBeforeMount(() => { </script> <template> - <FetchData - url="Workers" - :filter="{ where: { role: 'buyer' } }" - order="id" - @on-fetch="(data) => (workersOptions = data)" - auto-load - /> <VnSearchbar data-key="ItemRequests" url="TicketRequests/filter" @@ -268,7 +260,9 @@ onBeforeMount(() => { <QTd> <VnSelect v-model="row.attenderFk" - :options="workersOptions" + :where="{ role: 'buyer' }" + sort-by="id" + url="Workers" hide-selected option-label="firstName" option-value="id" diff --git a/src/pages/Item/ItemRequestFilter.vue b/src/pages/Item/ItemRequestFilter.vue index c9340dc1f..aa07b8d50 100644 --- a/src/pages/Item/ItemRequestFilter.vue +++ b/src/pages/Item/ItemRequestFilter.vue @@ -24,7 +24,6 @@ const stateOptions = [ const itemTypesOptions = ref([]); const warehousesOptions = ref([]); -const workersOptions = ref([]); const exprBuilder = (param, value) => { switch (param) { @@ -72,18 +71,6 @@ const decrement = (paramsObj, key) => { @on-fetch="(data) => (warehousesOptions = data)" auto-load /> - <FetchData - url="Workers/search" - :filter="{ - fields: ['id', 'name'], - order: 'name ASC', - }" - :params="{ - departmentCodes: ['VT'], - }" - @on-fetch="(data) => (workersOptions = data)" - auto-load - /> <VnFilterPanel :data-key="props.dataKey" :search-button="true" @@ -162,7 +149,10 @@ const decrement = (paramsObj, key) => { :label="t('params.requesterFk')" v-model="params.requesterFk" @update:model-value="searchFn()" - :options="workersOptions" + url="Workers/search" + :fields="['id', 'name']" + order="name ASC" + :params="{ departmentCodes: ['VT'] }" option-value="id" option-label="name" hide-selected From 7c2a13db0b8b71388713b341d0c2710df2f0e318 Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Mon, 26 Aug 2024 16:39:17 +0200 Subject: [PATCH 51/60] fix: refs #7524 use limit shelving Section --- src/pages/Shelving/Card/ShelvingFilter.vue | 55 +++++++--------------- src/pages/Shelving/Card/ShelvingForm.vue | 48 +++---------------- 2 files changed, 24 insertions(+), 79 deletions(-) diff --git a/src/pages/Shelving/Card/ShelvingFilter.vue b/src/pages/Shelving/Card/ShelvingFilter.vue index abc91373b..0056ffaec 100644 --- a/src/pages/Shelving/Card/ShelvingFilter.vue +++ b/src/pages/Shelving/Card/ShelvingFilter.vue @@ -3,6 +3,7 @@ import { ref } from 'vue'; import { useI18n } from 'vue-i18n'; import FetchData from 'components/FetchData.vue'; import VnFilterPanel from 'components/ui/VnFilterPanel.vue'; +import VnSelect from 'src/components/common/VnSelect.vue'; const { t } = useI18n(); const props = defineProps({ @@ -15,25 +16,13 @@ const props = defineProps({ const emit = defineEmits(['search']); const workers = ref(); -const parkings = ref(); function setWorkers(data) { workers.value = data; } - -function setParkings(data) { - parkings.value = data; -} </script> <template> - <FetchData - url="Parkings" - :filter="{ fields: ['id', 'code'] }" - sort-by="code ASC" - @on-fetch="setParkings" - auto-load - /> <FetchData url="Workers/activeWithInheritedRole" :filter="{ where: { role: 'salesPerson' } }" @@ -54,44 +43,36 @@ function setParkings(data) { </template> <template #body="{ params }"> <QItem class="q-my-sm"> - <QItemSection v-if="!parkings"> - <QSkeleton type="QInput" class="full-width" /> - </QItemSection> - <QItemSection v-if="parkings"> - <QSelect + <QItemSection> + <VnSelect + v-model="params.parkingFk" + url="Parkings" + :fields="['id', 'code']" + :label="t('params.parkingFk')" + option-value="id" + option-label="code" + :filter-options="['id', 'code']" dense outlined rounded - :label="t('params.parkingFk')" - v-model="params.parkingFk" - :options="parkings" - option-value="id" - option-label="code" - emit-value - map-options - use-input - :input-debounce="0" + sort-by="code ASC" /> </QItemSection> </QItem> <QItem class="q-mb-sm"> - <QItemSection v-if="!workers"> - <QSkeleton type="QInput" class="full-width" /> - </QItemSection> - <QItemSection v-if="workers"> - <QSelect + <QItemSection> + <VnSelect dense outlined rounded :label="t('params.userFk')" v-model="params.userFk" - :options="workers" + url="Workers/activeWithInheritedRole" option-value="id" - option-label="name" - emit-value - map-options - use-input - :input-debounce="0" + option-label="firstName" + :where="{ role: 'salesPerson' }" + sort-by="firstName ASC" + :use-like="false" /> </QItemSection> </QItem> diff --git a/src/pages/Shelving/Card/ShelvingForm.vue b/src/pages/Shelving/Card/ShelvingForm.vue index dd1c4e4a2..aee6f7f3a 100644 --- a/src/pages/Shelving/Card/ShelvingForm.vue +++ b/src/pages/Shelving/Card/ShelvingForm.vue @@ -1,12 +1,11 @@ <script setup> import { useI18n } from 'vue-i18n'; -import { ref } from 'vue'; import { useRoute, useRouter } from 'vue-router'; import VnRow from 'components/ui/VnRow.vue'; -import FetchData from 'components/FetchData.vue'; import FormModel from 'components/FormModel.vue'; import VnInput from 'src/components/common/VnInput.vue'; import VnSubToolbar from 'src/components/ui/VnSubToolbar.vue'; +import VnSelect from 'src/components/common/VnSelect.vue'; const { t } = useI18n(); const route = useRoute(); @@ -20,30 +19,6 @@ const defaultInitialData = { isRecyclable: false, }; -const parkingFilter = { fields: ['id', 'code'] }; -const parkingList = ref([]); -const parkingListCopy = ref([]); - -const setParkingList = (data) => { - parkingList.value = data; - parkingListCopy.value = data; -}; - -const parkingSelectFilter = { - options: parkingList, - filterFn: (options, value) => { - const search = value.trim().toLowerCase(); - - if (!search || search === '') { - return parkingListCopy.value; - } - - return options.value.filter((option) => - option.code.toLowerCase().startsWith(search) - ); - }, -}; - const shelvingFilter = { include: [ { @@ -68,12 +43,6 @@ const onSave = (shelving, newShelving) => { </script> <template> <VnSubToolbar /> - <FetchData - url="Parkings" - :filter="parkingFilter" - @on-fetch="setParkingList" - auto-load - /> <FormModel :url="isNew ? null : `Shelvings/${shelvingId}`" :url-create="isNew ? 'Shelvings' : null" @@ -84,27 +53,22 @@ const onSave = (shelving, newShelving) => { :form-initial-data="defaultInitialData" @on-data-saved="onSave" > - <template #form="{ data, validate, filter }"> + <template #form="{ data, validate }"> <VnRow> <VnInput v-model="data.code" :label="t('shelving.basicData.code')" :rules="validate('Shelving.code')" /> - <QSelect + <VnSelect v-model="data.parkingFk" - :options="parkingList" + url="Parkings" option-value="id" option-label="code" - emit-value + :filter-options="['id', 'code']" + :fields="['id', 'code']" :label="t('shelving.basicData.parking')" - map-options - use-input - @filter=" - (value, update) => filter(value, update, parkingSelectFilter) - " :rules="validate('Shelving.parkingFk')" - :input-debounce="0" /> </VnRow> <VnRow> From 717c52fc02a39edeb554ac9b644daf088c06051c Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Mon, 26 Aug 2024 16:55:16 +0200 Subject: [PATCH 52/60] fix: refs #7524 use id --- src/pages/Customer/Card/CustomerBasicData.vue | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/pages/Customer/Card/CustomerBasicData.vue b/src/pages/Customer/Card/CustomerBasicData.vue index 87a3b08f7..a6150bb3f 100644 --- a/src/pages/Customer/Card/CustomerBasicData.vue +++ b/src/pages/Customer/Card/CustomerBasicData.vue @@ -97,7 +97,12 @@ const title = ref(); :rules="validate('client.salesPersonFk')" :use-like="false" :emit-value="false" - @update:model-value="(val) => (title = val?.nickname)" + @update:model-value=" + (val) => { + title = val?.nickname; + data.salesPersonFk = val?.id; + } + " > <template #prepend> <VnAvatar From 4d7a4be2c044ab18ab4eda266b6666aefe1804d5 Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Tue, 27 Aug 2024 09:54:21 +0200 Subject: [PATCH 53/60] fix: refs #7524 use limit entry Section wip --- src/pages/Entry/Card/EntryBasicData.vue | 22 ++++------------------ src/pages/Entry/EntryFilter.vue | 11 +---------- 2 files changed, 5 insertions(+), 28 deletions(-) diff --git a/src/pages/Entry/Card/EntryBasicData.vue b/src/pages/Entry/Card/EntryBasicData.vue index e5d6f50d9..b81b1db22 100644 --- a/src/pages/Entry/Card/EntryBasicData.vue +++ b/src/pages/Entry/Card/EntryBasicData.vue @@ -19,8 +19,6 @@ const { t } = useI18n(); const { hasAny } = useRole(); const isAdministrative = () => hasAny(['administrative']); -const suppliersOptions = ref([]); -const travelsOptions = ref([]); const companiesOptions = ref([]); const currenciesOptions = ref([]); @@ -29,20 +27,6 @@ const onFilterTravelSelected = (formData, id) => { }; </script> <template> - <FetchData - url="Suppliers" - :filter="{ fields: ['id', 'nickname'] }" - order="nickname" - @on-fetch="(data) => (suppliersOptions = data)" - auto-load - /> - <FetchData - url="Travels/filter" - :filter="{ fields: ['id', 'warehouseInName'] }" - order="id" - @on-fetch="(data) => (travelsOptions = data)" - auto-load - /> <FetchData ref="companiesRef" url="Companies" @@ -71,9 +55,10 @@ const onFilterTravelSelected = (formData, id) => { <VnSelect :label="t('entry.basicData.supplier')" v-model="data.supplierFk" - :options="suppliersOptions" + url="Suppliers" option-value="id" option-label="nickname" + :fields="['id', 'nickname']" hide-selected :required="true" map-options @@ -92,7 +77,8 @@ const onFilterTravelSelected = (formData, id) => { <VnSelectDialog :label="t('entry.basicData.travel')" v-model="data.travelFk" - :options="travelsOptions" + url="Travels/filter" + :fields="['id', 'warehouseInName']" option-value="id" option-label="warehouseInName" map-options diff --git a/src/pages/Entry/EntryFilter.vue b/src/pages/Entry/EntryFilter.vue index 194ce0a28..c4d821842 100644 --- a/src/pages/Entry/EntryFilter.vue +++ b/src/pages/Entry/EntryFilter.vue @@ -20,7 +20,6 @@ const props = defineProps({ const currenciesOptions = ref([]); const companiesOptions = ref([]); -const suppliersOptions = ref([]); const stateStore = useStateStore(); onMounted(async () => { @@ -45,14 +44,6 @@ onMounted(async () => { @on-fetch="(data) => (currenciesOptions = data)" auto-load /> - <FetchData - url="Suppliers" - :filter="{ fields: ['id', 'nickname', 'name'] }" - order="nickname" - @on-fetch="(data) => (suppliersOptions = data)" - auto-load - /> - <VnFilterPanel :data-key="props.dataKey" :search-button="true"> <template #tags="{ tag, formatFn }"> <div class="q-gutter-x-xs"> @@ -135,7 +126,7 @@ onMounted(async () => { :label="t('params.supplierFk')" v-model="params.supplierFk" @update:model-value="searchFn()" - :options="suppliersOptions" + url="Suppliers" option-value="id" option-label="name" hide-selected From 4a815988837815dd835650c654af639a23ffedb4 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Tue, 27 Aug 2024 10:27:43 +0200 Subject: [PATCH 54/60] fix: ticketDescriptorMenu --- src/pages/Ticket/Card/TicketDescriptorMenu.vue | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/pages/Ticket/Card/TicketDescriptorMenu.vue b/src/pages/Ticket/Card/TicketDescriptorMenu.vue index c7784bc2a..d5a578ca7 100644 --- a/src/pages/Ticket/Card/TicketDescriptorMenu.vue +++ b/src/pages/Ticket/Card/TicketDescriptorMenu.vue @@ -30,10 +30,13 @@ const actions = { let clonedTicketId; try { - const { data } = await axios.post(`Tickets/${ticketId}/clone`, { + const { data } = await axios.post(`Tickets/cloneAll`, { shipped: ticket.value.shipped, + ticketsIds: [ticket.value.id], + withWarehouse: true, + negative: false, }); - clonedTicketId = data; + clonedTicketId = data[0].id; } catch (e) { opts.message = t('It was not able to clone the ticket'); opts.type = 'negative'; From ceec78da17b6ceec640e6fb6d37d37c49d8d43b3 Mon Sep 17 00:00:00 2001 From: Javier Segarra <jsegarra@verdnatura.es> Date: Tue, 27 Aug 2024 10:27:51 +0200 Subject: [PATCH 55/60] test: ticketDescriptorMenu --- test/cypress/integration/ticket/ticketDescriptor.spec.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/cypress/integration/ticket/ticketDescriptor.spec.js b/test/cypress/integration/ticket/ticketDescriptor.spec.js index fc920f346..882aab157 100644 --- a/test/cypress/integration/ticket/ticketDescriptor.spec.js +++ b/test/cypress/integration/ticket/ticketDescriptor.spec.js @@ -16,7 +16,7 @@ describe('Ticket descriptor', () => { cy.openActionsDescriptor(); cy.get(toCloneOpt).click(); cy.clickConfirm(); - cy.get(warehouseValue).contains('-'); + cy.get(warehouseValue).contains('Warehouse One'); cy.get(summaryHeader) .invoke('text') .then((text) => { From 3051b134781a819dfecb321a9f223017d66456a8 Mon Sep 17 00:00:00 2001 From: jgallego <jgallego@verdnatura.es> Date: Wed, 28 Aug 2024 17:31:39 +0200 Subject: [PATCH 56/60] warmFix fields with wrong name --- src/pages/InvoiceOut/InvoiceOutList.vue | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/InvoiceOut/InvoiceOutList.vue b/src/pages/InvoiceOut/InvoiceOutList.vue index b08e12b3e..9423c28f6 100644 --- a/src/pages/InvoiceOut/InvoiceOutList.vue +++ b/src/pages/InvoiceOut/InvoiceOutList.vue @@ -242,7 +242,7 @@ watchEffect(selectedRows); /> <VnSelect url="InvoiceOutSerials" - v-model="data.invoiceOutSerial" + v-model="data.serial" :label="t('invoiceOutList.tableVisibleColumns.invoiceOutSerial')" :options="invoiceOutSerialsOptions" option-label="description" @@ -254,7 +254,7 @@ watchEffect(selectedRows); /> <VnSelect url="TaxAreas" - v-model="data.area" + v-model="data.taxArea" :label="t('invoiceOutList.tableVisibleColumns.taxArea')" :options="taxAreasOptions" option-label="code" From 77c93117609a31e0a1f653daf9f42b5766f5e63b Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Wed, 28 Aug 2024 17:39:39 +0200 Subject: [PATCH 57/60] fix: refs #7524 use limit travels section --- src/pages/Travel/ExtraCommunityFilter.vue | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/src/pages/Travel/ExtraCommunityFilter.vue b/src/pages/Travel/ExtraCommunityFilter.vue index c51151451..a8bbde75b 100644 --- a/src/pages/Travel/ExtraCommunityFilter.vue +++ b/src/pages/Travel/ExtraCommunityFilter.vue @@ -20,7 +20,6 @@ const props = defineProps({ const warehousesOptions = ref([]); const continentsOptions = ref([]); const agenciesOptions = ref([]); -const suppliersOptions = ref([]); const warehousesByContinent = ref({}); const add = (paramsObj, key) => { @@ -76,12 +75,6 @@ warehouses(); @on-fetch="(data) => (agenciesOptions = data)" auto-load /> - <FetchData - url="Suppliers" - @on-fetch="(data) => (suppliersOptions = data)" - auto-load - /> - <VnFilterPanel :data-key="props.dataKey" :search-button="true"> <template #tags="{ tag, formatFn }"> <div class="q-gutter-x-xs"> @@ -220,7 +213,7 @@ warehouses(); <VnSelect :label="t('globals.pageTitles.supplier')" v-model="params.cargoSupplierFk" - :options="suppliersOptions" + url="Suppliers" option-value="id" option-label="name" hide-selected From 42e7629c4089b04f7867adb231628c8e666855da Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Thu, 29 Aug 2024 13:53:10 +0200 Subject: [PATCH 58/60] fix: refs #7524 use limit ticket section --- .../Ticket/Card/BasicData/TicketBasicDataForm.vue | 14 +++----------- src/pages/Ticket/Card/TicketExpedition.vue | 12 +++--------- src/pages/Ticket/Card/TicketSale.vue | 11 +++-------- src/pages/Ticket/Card/TicketSaleTracking.vue | 11 ++--------- 4 files changed, 11 insertions(+), 37 deletions(-) diff --git a/src/pages/Ticket/Card/BasicData/TicketBasicDataForm.vue b/src/pages/Ticket/Card/BasicData/TicketBasicDataForm.vue index 2d937346a..28c6fcf15 100644 --- a/src/pages/Ticket/Card/BasicData/TicketBasicDataForm.vue +++ b/src/pages/Ticket/Card/BasicData/TicketBasicDataForm.vue @@ -30,7 +30,6 @@ const { t } = useI18n(); const agencyFetchRef = ref(null); const zonesFetchRef = ref(null); -const clientsOptions = ref([]); const warehousesOptions = ref([]); const companiesOptions = ref([]); const agenciesOptions = ref([]); @@ -273,15 +272,6 @@ const redirectToCustomerAddress = () => { onMounted(() => onFormModelInit()); </script> <template> - <FetchData - url="Clients" - :filter="{ - fields: ['id', 'name'], - order: 'id', - }" - @on-fetch="(data) => (clientsOptions = data)" - auto-load - /> <FetchData url="Warehouses" @on-fetch="(data) => (warehousesOptions = data)" @@ -317,7 +307,9 @@ onMounted(() => onFormModelInit()); v-model="clientId" option-value="id" option-label="name" - :options="clientsOptions" + url="Clients" + :fields="['id', 'name']" + sort-by="id" hide-selected map-options :required="true" diff --git a/src/pages/Ticket/Card/TicketExpedition.vue b/src/pages/Ticket/Card/TicketExpedition.vue index f344e4276..c4ab63b39 100644 --- a/src/pages/Ticket/Card/TicketExpedition.vue +++ b/src/pages/Ticket/Card/TicketExpedition.vue @@ -3,7 +3,6 @@ import { onMounted, ref, computed, onUnmounted, reactive, watch } from 'vue'; import { useI18n } from 'vue-i18n'; import { useRoute } from 'vue-router'; -import FetchData from 'components/FetchData.vue'; import VnInput from 'src/components/common/VnInput.vue'; import ItemDescriptorProxy from 'src/pages/Item/Card/ItemDescriptorProxy.vue'; import TicketEditManaProxy from './TicketEditMana.vue'; @@ -35,7 +34,6 @@ const selectedExpeditions = ref([]); const allColumnNames = ref([]); const visibleColumns = ref([]); const newTicketWithRoute = ref(false); -const itemsOptions = ref([]); const exprBuilder = (param, value) => { switch (param) { @@ -139,7 +137,9 @@ const columns = computed(() => [ filterValue: null, event: getInputEvents, attrs: { - options: itemsOptions.value, + url: 'Items', + fields: ['id', 'name'], + 'sort-by': 'name ASC', 'option-value': 'id', 'option-label': 'name', dense: true, @@ -274,12 +274,6 @@ onUnmounted(() => (stateStore.rightDrawer = false)); </script> <template> - <FetchData - url="Items" - auto-load - :filter="{ fields: ['id', 'name'], order: 'name ASC' }" - @on-fetch="(data) => (itemsOptions = data)" - /> <VnSubToolbar> <template #st-data> <TableVisibleColumns diff --git a/src/pages/Ticket/Card/TicketSale.vue b/src/pages/Ticket/Card/TicketSale.vue index 5978dbf09..bd7297b56 100644 --- a/src/pages/Ticket/Card/TicketSale.vue +++ b/src/pages/Ticket/Card/TicketSale.vue @@ -38,7 +38,6 @@ const ticketConfig = ref(null); const isLocked = ref(false); const isTicketEditable = ref(false); const sales = ref([]); -const itemsWithNameOptions = ref([]); const editableStatesOptions = ref([]); const selectedSales = ref([]); const mana = ref(null); @@ -435,12 +434,6 @@ onUnmounted(() => (stateStore.rightDrawer = false)); auto-load @on-fetch="(data) => (isLocked = data)" /> - <FetchData - url="Items/withName" - :filter="{ fields: ['id', 'name'], order: 'id DESC' }" - auto-load - @on-fetch="(data) => (itemsWithNameOptions = data)" - /> <FetchData url="States/editableStates" :filter="{ fields: ['code', 'name', 'id', 'alertLevel'], order: 'name ASC' }" @@ -626,10 +619,12 @@ onUnmounted(() => (stateStore.rightDrawer = false)); </div> <VnSelect v-else - :options="itemsWithNameOptions" hide-selected option-label="name" option-value="id" + url="Items/withName" + :fields="['id', 'name']" + sort-by="id DESC" @update:model-value="changeQuantity(row)" v-model="row.itemFk" > diff --git a/src/pages/Ticket/Card/TicketSaleTracking.vue b/src/pages/Ticket/Card/TicketSaleTracking.vue index d519bc2c6..b8e166e6a 100644 --- a/src/pages/Ticket/Card/TicketSaleTracking.vue +++ b/src/pages/Ticket/Card/TicketSaleTracking.vue @@ -26,7 +26,6 @@ const saleTrackingFetchDataRef = ref(null); const sales = ref([]); const saleTrackings = ref([]); const itemShelvignsSales = ref([]); -const shelvingsOptions = ref([]); const parkingsOptions = ref([]); const saleTrackingUrl = computed(() => `SaleTrackings/${route.params.id}/filter`); const oldQuantity = ref(null); @@ -330,12 +329,6 @@ const qCheckBoxController = (sale, action) => { auto-load @on-fetch="(data) => (sales = data)" /> - <FetchData - url="Shelvings" - auto-load - @on-fetch="(data) => (shelvingsOptions = data)" - /> - <FetchData url="Parkings" auto-load @on-fetch="(data) => (parkingsOptions = data)" /> <QTable :rows="sales" :columns="columns" @@ -500,7 +493,7 @@ const qCheckBoxController = (sale, action) => { <template #body-cell-shelving="{ row }"> <QTd auto-width> <VnSelect - :options="shelvingsOptions" + url="Shelvings" hide-selected option-label="code" option-value="code" @@ -513,7 +506,7 @@ const qCheckBoxController = (sale, action) => { <template #body-cell-parking="{ row }"> <QTd auto-width> <VnSelect - :options="parkingsOptions" + url="Parkings" hide-selected option-label="code" option-value="id" From 7bac607746df61570dea888a54bc97b896264293 Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Thu, 29 Aug 2024 14:20:52 +0200 Subject: [PATCH 59/60] fix: refs #7524 add sort-by --- src/pages/Entry/EntryFilter.vue | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/pages/Entry/EntryFilter.vue b/src/pages/Entry/EntryFilter.vue index c4d821842..3b88072fa 100644 --- a/src/pages/Entry/EntryFilter.vue +++ b/src/pages/Entry/EntryFilter.vue @@ -129,6 +129,8 @@ onMounted(async () => { url="Suppliers" option-value="id" option-label="name" + :fields="['id', 'name', 'nickname']" + sort-by="nickname" hide-selected dense outlined From 56d226b36b7fbd5807dcf52a432cc56fa8aed0b2 Mon Sep 17 00:00:00 2001 From: jorgep <jorgep@verdnatura.es> Date: Mon, 2 Sep 2024 10:03:01 +0200 Subject: [PATCH 60/60] fix: refs #7524 dynamic fetching --- src/pages/Item/Card/ItemBotanical.vue | 12 ------------ src/pages/Ticket/Card/TicketCreateTracking.vue | 12 +++--------- src/pages/Ticket/Card/TicketSaleTracking.vue | 1 - 3 files changed, 3 insertions(+), 22 deletions(-) diff --git a/src/pages/Item/Card/ItemBotanical.vue b/src/pages/Item/Card/ItemBotanical.vue index 599cd2b32..e9f1f11ee 100644 --- a/src/pages/Item/Card/ItemBotanical.vue +++ b/src/pages/Item/Card/ItemBotanical.vue @@ -38,18 +38,6 @@ onMounted(async () => { }" @on-fetch="(data) => (itemBotanicals = data)" /> - <FetchData - url="Genera" - :filter="{ fields: ['id', 'name'], order: 'name ASC' }" - @on-fetch="(data) => (itemGenusOptions = data)" - auto-load - /> - <FetchData - url="Species" - :filter="{ fields: ['id', 'name'], order: 'name ASC' }" - @on-fetch="(data) => (itemSpeciesOptions = data)" - auto-load - /> <FormModel url-update="ItemBotanicals" model="entry" diff --git a/src/pages/Ticket/Card/TicketCreateTracking.vue b/src/pages/Ticket/Card/TicketCreateTracking.vue index 87ca8dd3f..d692f550d 100644 --- a/src/pages/Ticket/Card/TicketCreateTracking.vue +++ b/src/pages/Ticket/Card/TicketCreateTracking.vue @@ -17,9 +17,7 @@ const { t } = useI18n(); const state = useState(); const user = state.getUser(); const stateFetchDataRef = ref(null); - const statesOptions = ref([]); -const workersOptions = ref([]); const onStateFkChange = (formData) => (formData.userFk = user.value.id); </script> @@ -30,12 +28,6 @@ const onStateFkChange = (formData) => (formData.userFk = user.value.id); auto-load @on-fetch="(data) => (statesOptions = data)" /> - <FetchData - url="Workers/search" - :filter="{ fields: ['id', 'name'], order: 'name ASC' }" - auto-load - @on-fetch="(data) => (workersOptions = data)" - /> <FormModelPopup :title="t('Create tracking')" url-create="Tickets/state" @@ -57,7 +49,9 @@ const onStateFkChange = (formData) => (formData.userFk = user.value.id); <VnSelect :label="t('tracking.worker')" v-model="data.userFk" - :options="workersOptions" + url="Workers/search" + fields=" ['id', 'name']" + sort-by="name ASC" hide-selected option-label="name" option-value="id" diff --git a/src/pages/Ticket/Card/TicketSaleTracking.vue b/src/pages/Ticket/Card/TicketSaleTracking.vue index b8e166e6a..e699d2bfd 100644 --- a/src/pages/Ticket/Card/TicketSaleTracking.vue +++ b/src/pages/Ticket/Card/TicketSaleTracking.vue @@ -26,7 +26,6 @@ const saleTrackingFetchDataRef = ref(null); const sales = ref([]); const saleTrackings = ref([]); const itemShelvignsSales = ref([]); -const parkingsOptions = ref([]); const saleTrackingUrl = computed(() => `SaleTrackings/${route.params.id}/filter`); const oldQuantity = ref(null);